原创

java面试题-锁如何使用?有什么注意事项?

在Java中,锁是处理多线程并发访问共享资源的关键工具。本教程将详细介绍Java中常见的锁,包括synchronized关键字、可重入锁ReentrantLock和可重复读写锁ReentrantReadWriteLock的用法。我们还将讨论锁的注意事项,以确保在多线程编程中避免常见的问题。

1. synchronized关键字的用法

synchronized关键字是Java中最基本的锁机制,用于保护共享资源,避免多个线程同时访问导致数据不一致的问题。以下是synchronized的三种用法:

1.1 修饰普通方法

public class TestsynchronizedNormalMethod {
    private int count = 0;

    private synchronized void add1000() {
        for (int i = 0; i < 1000; i++) {
            count++;
        }
    }

    private void test() throws InterruptedException {
        // 创建30个线程,每个线程对Testsynchronized对象的count属性加1000
        List<Thread> threads = new ArrayList<>();

        for (int i = 0; i < 30; i++) {
            Thread t = new Thread(this::add1000);
            t.start();
            threads.add(t);
        }

        // 等待所有线程执行完毕
        for (Thread t : threads) {
            t.join();
        }

        // 打印count的值
        System.out.println(count);
    }

    public static void main(String[] args) throws InterruptedException {
        new TestsynchronizedNormalMethod().test();
    }
}

1.2 修饰静态方法

public class TestsynchronizedStaticMethod {
    private static int count = 0;

    private static synchronized void add1000() {
        for (int i = 0; i < 1000; i++) {
            count++;
        }
    }

    public static void main(String[] args) throws InterruptedException {
        // 创建30个线程,每个线程对Testsynchronized对象的count属性加1000
        List<Thread> threads = new ArrayList<>();

        for (int i = 0; i < 30; i++) {
            Thread t = new Thread(TestsynchronizedStaticMethod::add1000);
            t.start();
            threads.add(t);
        }

        // 等待所有线程执行完毕
        for (Thread t : threads) {
            t.join();
        }

        // 打印count的值
        System.out.println(count);
    }
}

1.3 锁定Java对象

public class TestsynchronizedCodeBlock {
    private int count = 0;
    private final Object obj = new Object();

    private void add1000() {
        synchronized (obj) {
            for (int i = 0; i < 1000; i++) {
                count++;
            }
        }
    }

    private void test() throws InterruptedException {
        // 创建30个线程,每个线程对Testsynchronized对象的count属性加1000
        List<Thread> threads = new ArrayList<>();

        for (int i = 0; i < 30; i++) {
            Thread t = new Thread(this::add1000);
            t.start();
            threads.add(t);
        }

        // 等待所有线程执行完毕
        for (Thread t : threads) {
            t.join();
        }

        // 打印count的值
        System.out.println(count);
    }

    public static void main(String[] args) throws InterruptedException {
        new TestsynchronizedCodeBlock().test();
    }
}

2. ReentrantLock的使用示例

ReentrantLock是Java并发包中提供的可重入锁,它相比synchronized更加灵活,可以实现更复杂的线程同步控制。

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class ReentrantLockDemo {
    private int count = 0;
    private final Lock lock = new ReentrantLock();

    private void add1000() {
        lock.lock();
        try {
            for (int i = 0; i < 1000; i++) {
                count++;
            }
        } finally {
            lock.unlock();
        }
    }

    private void test() throws

 InterruptedException {
        // 创建30个线程,每个线程对Testsynchronized对象的count属性加1000
        List<Thread> threads = new ArrayList<>();

        for (int i = 0; i < 30; i++) {
            Thread t = new Thread(this::add1000);
            t.start();
            threads.add(t);
        }

        // 等待所有线程执行完毕
        for (Thread t : threads) {
            t.join();
        }

        // 打印count的值
        System.out.println(count);
    }

    public static void main(String[] args) throws InterruptedException {
        new ReentrantLockDemo().test();
    }
}

3. ReentrantReadWriteLock的使用示例

ReentrantReadWriteLock是可重复读写锁,适用于对共享资源进行频繁读操作而写操作较少的场景。

import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

public class ReentrantReadWriteLockDemo {
    private Map<String, Object> map = new HashMap<>();
    private final ReadWriteLock lock = new ReentrantReadWriteLock();

    public Object get(String key) {
        Object value = null;
        lock.readLock().lock();
        try {
            Thread.sleep(50L);
            value = map.get(key);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.readLock().unlock();
        }
        return value;
    }

    public void set(String key, Object value) {
        lock.writeLock().lock();
        try {
            Thread.sleep(50L);
            map.put(key, value);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.writeLock().unlock();
        }
    }

    public static void main(String[] args) {
        ReentrantReadWriteLockDemo test = new ReentrantReadWriteLockDemo();
        String key = "lock";
        Random r = new Random();
        for (int i = 0; i < 5; i++) {
            new Thread(() -> {
                for (int j = 0; j < 10; j++) {
                    System.out.println(Thread.currentThread().getName() + " read value=" + test.get(key));
                }
            }).start();
            new Thread(() -> {
                for (int j = 0; j < 10; j++) {
                    int value = r.nextInt(1000);
                    test.set(key, value);
                    System.out.println(Thread.currentThread().getName() + " write value=" + value);
                }
            }).start();
        }
    }
}

4. 锁的使用注意事项

在使用锁时,有一些注意事项需要牢记:

  • 不要锁定基本类型的包装类,避免缓存问题。
  • 确保锁定的是同一个对象,避免线程互斥问题。
  • synchronized不支持尝试获取锁、锁超时和公平锁。
  • 在使用ReentrantLock时,一定要在finally语句块中调用unlock()方法,以避免死锁。
  • 在高并发情况下,ReentrantLock的自旋可能会消耗大量CPU资源。
  • ReentrantReadWriteLock适合读操作频繁、写操作较少的场景。

通过深入学习锁的使用和注意事项,你将能够编写更加高效和可维护的多线程程序。

正文到此结束
本文目录