Skip to content

Latest commit

 

History

History
1135 lines (888 loc) · 33.9 KB

1.Lock对象的使用.md

File metadata and controls

1135 lines (888 loc) · 33.9 KB

目录

Lock 对象的使用

Java 多线程中不仅可以使用 synchronized 来保证线程的安全性,在 JDK1.5 中新增加的 ReentrantLock 类也能达到同样的效果,并且在扩展功能上更加强大。

ReentrantLock 类

下面来介绍一下 ReentrantLock 的使用方式。

创建方法类:

public class MyReentRantLockService {
    private Lock lock = new ReentrantLock();
    
    public void reentrantLockService(){
        // 加锁
        lock.lock();
        for (int i=0; i<5; i++){
            System.out.println("ThreadName = " + Thread.currentThread().getName() + (" " + (i + 1)));
        }
        // 释放锁
        lock.unlock();
    }
}

方法执行前加锁,执行完成释放锁。

接着创建线程类:

public class MyReentRantLockThread implements Runnable{
    
    private MyReentRantLockService myReentRantLockService;

    public MyReentRantLockThread(MyReentRantLockService myReentRantLockService) {
        this.myReentRantLockService = myReentRantLockService;
    }

    public void run() {
        myReentRantLockService.reentrantLockService();
    }
}

创建测试类 demo

public class MyReentRantLockThreadTest {
    public static void main(String[] args) {
        MyReentRantLockService myReentRantLockService = new MyReentRantLockService();
        
        for (int i=0; i<2; i++){
            MyReentRantLockThread myReentRantLockThread = new MyReentRantLockThread(myReentRantLockService);
            Thread t1 = new Thread(myReentRantLockThread);
            t1.setName("Thread=" + i);
            t1.start();
        }
    }
}

运行结果:

ThreadName = Thread=0 1
ThreadName = Thread=0 2
ThreadName = Thread=0 3
ThreadName = Thread=0 4
ThreadName = Thread=0 5
ThreadName = Thread=1 1
ThreadName = Thread=1 2
ThreadName = Thread=1 3
ThreadName = Thread=1 4
ThreadName = Thread=1 5

根据结果可以看到,只有当前线程执行完成释放锁之后,其他线程才可以执行。

await() 方法的错误用法与更正

关键字 synchronized 与 wait()、notify()/notifyAll() 方法相结合可以实现 wait/notify 模式。 ReentrantLock 类也可以实现同样的功能。但是 需要借助 Condition 对象。Condition 类是 JDK 5 的技术,具有更多的灵活性

Condition 对象的作用是控制并处理线程的状态,可以使线程呈 wait() 状态,也可以让线程继续运行。

创建方法类:

public class MyReentrantLockConditionService {
    
    private Condition condition = new ReentrantLock().newCondition();
    
    
    public void waitCondition(){
        try {
            condition.await();
        } catch (InterruptedException e) {
            // doNothing
        }
    }
    
}

await() 方法的作用是使当前线程连接到通知或被中断之前一直处于等待 wait 状态,和 wait() 方法的作用一样

创建线程类:

public class MyReentrantLockConditionServiceThread implements Runnable{
    
    private MyReentrantLockConditionService conditionService;

    public MyReentrantLockConditionServiceThread(MyReentrantLockConditionService conditionService) {
        this.conditionService = conditionService;
    }

    @Override
    public void run() {
        conditionService.waitCondition();
    }
}

测试类:

public class MyReentrantLockConditionServiceThreadTest {
    public static void main(String[] args) {
        MyReentrantLockConditionService conditionService = new MyReentrantLockConditionService();
        
        Thread t1 = new Thread(new MyReentrantLockConditionServiceThread(conditionService));
        t1.start();
    }
}

执行方法, 会发现抛异常了。

Exception in thread "Thread-0" java.lang.IllegalMonitorStateException
	at java.util.concurrent.locks.ReentrantLock$Sync.tryRelease(ReentrantLock.java:151)
	at java.util.concurrent.locks.AbstractQueuedSynchronizer.release(AbstractQueuedSynchronizer.java:1261)
	at java.util.concurrent.locks.AbstractQueuedSynchronizer.fullyRelease(AbstractQueuedSynchronizer.java:1723)
	at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2036)
	at com.anzhi.reentrantlockuse.MyReentrantLockConditionService.waitCondition(MyReentrantLockConditionService.java:13)
	at com.anzhi.reentrantlockuse.MyReentrantLockConditionServiceThread.run(MyReentrantLockConditionServiceThread.java:13)
	at java.lang.Thread.run(Thread.java:748)

报错的异常信息是监视器出错,这是因为在执行 condition.await() 方法调用之前必须先获得对象的锁,即lock.lock()。修改 MyReentrantLockConditionService 类:

public class MyReentrantLockConditionService {
    
    private Lock lock = new ReentrantLock();
    
    private Condition condition = lock.newCondition();
    
    
    public void waitCondition(){
        try {
            lock.lock();
            System.out.println("上锁等待解锁");
            System.out.println(" 执行 await 方法, 使线程进入 wait 状态");
            condition.await();
            System.out.println();
        } catch (InterruptedException e) {
            // doNothing
        }finally {
            lock.unlock();
            System.out.println("锁释放了");
        }
    }
    
}

运行程序结果:

上锁等待解锁
 执行 await 方法, 使线程t0进入 wait 状态
上锁等待解锁
 执行 await 方法, 使线程t1进入 wait 状态
上锁等待解锁
 执行 await 方法, 使线程t2进入 wait 状态

根据结果可以发现调用了 Condition 对象的 await() 方法将当前执行任务的线程转换成 wait 状态并释放了锁。

使用 await 和 signal 实现 wait/notify 机制

创建方法:

public class MyReentrantLockAwaitSignalService {
    private Lock lock = new ReentrantLock();
    
    public Condition condition = lock.newCondition();
    
    public void awaitMethod(){
        try{
            lock.lock();
            System.out.println("  await 时间为" + System.currentTimeMillis());
            condition.await();
        }catch (Exception e){
            // doNothing
        }
    }
    
    
    public void signalMethod(){
        try{
            lock.lock();
            System.out.println(" signal 时间为" + System.currentTimeMillis());
            condition.signal();
        }catch (Exception e){
            
        }
    }
}

实现 await 方法与 signal 方法

创建相应的线程

public class MyReentrantLockAwaitSignalServiceThread implements Runnable{
    
    private MyReentrantLockAwaitSignalService awaitSignalService = new MyReentrantLockAwaitSignalService();

    public MyReentrantLockAwaitSignalServiceThread(MyReentrantLockAwaitSignalService awaitSignalService) {
        this.awaitSignalService = awaitSignalService;
    }

    @Override
    public void run() {
        awaitSignalService.awaitMethod();
    }
}

创建测试类

public class MyReentrantLockAwaitSignalServiceThreadTest {
    public static void main(String[] args) {

        MyReentrantLockAwaitSignalService awaitSignalService = new MyReentrantLockAwaitSignalService();

        Thread thread = new Thread(new MyReentrantLockAwaitSignalServiceThread(awaitSignalService));
        thread.setName("t1");
        thread.start();

        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            // doNothing
        }
        
        awaitSignalService.signalMethod();
    }
}

运行程序结果:

  await 时间为1658055080901
 signal 时间为1658055083902

可以看到线程 thread 先调用了 Condition.await 使线程处于暂停状态,接着调用 signal 通知线程继续执行。

通知部分线程

创建方法:

public class MyReentrantLockAwaitSignalMoreConditionService {
    private Lock lock = new ReentrantLock();
    
    public Condition conditionA = lock.newCondition();
    public Condition conditionB = lock.newCondition();
    
    public void awaitMethodA(){
        lock.lock();
        System.out.println("begin awaitMethodA 时间为" + System.currentTimeMillis() + " ThreadName = " + Thread.currentThread().getName());
        try {
            conditionA.await();
            System.out.println("  endi awaitA时间为" + System.currentTimeMillis() + " ThreadName = " + Thread.currentThread().getName());
        } catch (InterruptedException e) {
            // doNothing
        }finally {
            lock.unlock();
        }
    }
    
    public void awaitMethodB(){
        lock.lock();
        System.out.println("begin awaitMethodB 时间为" + System.currentTimeMillis() + " ThreadName = " + Thread.currentThread().getName());
        try {
            conditionB.await();
            System.out.println("  endi awaitB时间为" + System.currentTimeMillis() + " ThreadName = " + Thread.currentThread().getName());
        } catch (InterruptedException e) {
            // doNothing
        }finally {
            lock.unlock();
        }
    }
    
    public void signalAll_A(){
        lock.lock();
        System.out.println("     signalAll_A 时间为" + System.currentTimeMillis() + " ThreadName = " + Thread.currentThread().getName());
        try {
            conditionA.signalAll();
        } catch (Exception e) {
            // doNothing
        }finally {
            lock.unlock();
        }
    }

    public void signalAll_B(){
        lock.lock();
        System.out.println("     signalAll_B 时间为" + System.currentTimeMillis() + " ThreadName = " + Thread.currentThread().getName());
        try {
            conditionB.signalAll();
        } catch (Exception e) {
            // doNothing
        }finally {
            lock.unlock();
        }
    }
    
}

创建 A、B 线程

public class MyReentrantLockAwaitSignalMoreConditionServiceThreadA implements Runnable{
    
    private MyReentrantLockAwaitSignalMoreConditionService awaitSignalMoreConditionService = new MyReentrantLockAwaitSignalMoreConditionService();

    public MyReentrantLockAwaitSignalMoreConditionServiceThreadA(MyReentrantLockAwaitSignalMoreConditionService awaitSignalMoreConditionService) {
        this.awaitSignalMoreConditionService = awaitSignalMoreConditionService;
    }

    @Override
    public void run() {
        awaitSignalMoreConditionService.awaitMethodA();
    }
}

public class MyReentrantLockAwaitSignalMoreConditionServiceThreadB implements Runnable{
    private MyReentrantLockAwaitSignalMoreConditionService awaitSignalMoreConditionService = new MyReentrantLockAwaitSignalMoreConditionService();

    public MyReentrantLockAwaitSignalMoreConditionServiceThreadB(MyReentrantLockAwaitSignalMoreConditionService awaitSignalMoreConditionService) {
        this.awaitSignalMoreConditionService = awaitSignalMoreConditionService;
    }

    @Override
    public void run() {
        awaitSignalMoreConditionService.awaitMethodB();
    }
}

创建测试类

public class MyReentrantLockAwaitSignalMoreConditionServiceThreadTest {
    public static void main(String[] args) {
        MyReentrantLockAwaitSignalMoreConditionService awaitSignalMoreConditionService = new MyReentrantLockAwaitSignalMoreConditionService();
        
        MyReentrantLockAwaitSignalMoreConditionServiceThreadA serviceThreadA = new MyReentrantLockAwaitSignalMoreConditionServiceThreadA(awaitSignalMoreConditionService);
        MyReentrantLockAwaitSignalMoreConditionServiceThreadB serviceThreadB = new MyReentrantLockAwaitSignalMoreConditionServiceThreadB(awaitSignalMoreConditionService);
        
        Thread threadA = new Thread(serviceThreadA);
        threadA.setName("threadA");
        
        Thread threadB = new Thread(serviceThreadB);
        threadB.setName("threadB");
        
        threadA.start();
        threadB.start();

        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            // doNothing
        }
        
        awaitSignalMoreConditionService.signalAll_A();
    }
}

运行程序结果:

begin awaitMethodA 时间为1658062070092 ThreadName = threadA
begin awaitMethodB 时间为1658062070093 ThreadName = threadB
     signalAll_A 时间为1658062073093 ThreadName = main
  endi awaitA时间为1658062073093 ThreadName = threadA

使用 Condition 对象可以唤醒指定种类的线程,这是控制部分线程行为的方便方式。

生产者/消费者模式多对多交替输出

创建方法类:

public class MyReentrantLockConditionMethod {

    // 创建锁
    Lock lock = new ReentrantLock();
    private Condition condition = lock.newCondition();

    private int hasValue = 0;

    public void set(){
        try {
            lock.lock();
            while (hasValue == 1) {
                System.out.println("  有可能★★★连续");
                condition.await();
            }
            System.out.println("打印★");
            hasValue = 1;
            condition.signalAll();
        }catch (Exception e){
            // doNothing
        }finally {
            lock.unlock();
        }
    }


    public void get(){
        try{
            lock.lock();
            while(hasValue == 0){
                System.out.println(" 有可能连续☆☆☆");
                condition.await();
            }
            System.out.println("打印☆");
            hasValue = 0;
            condition.signalAll();
        }catch (Exception e){
            // doNothing
        }finally {
            lock.unlock();
        }
    }
}

创建生产消费者类:

public class MyReentrantLockConditionConsumerThread implements Runnable{

    private MyReentrantLockConditionMethod conditionMethod;

    public MyReentrantLockConditionConsumerThread(MyReentrantLockConditionMethod conditionMethod) {
        this.conditionMethod = conditionMethod;
    }

    @Override
    public void run() {
        for(int i=0; i<Integer.MAX_VALUE; i++){
            conditionMethod.get();
        }
    }
}

public class MyReentrantLockConditionProducerThread implements Runnable{

    private MyReentrantLockConditionMethod conditionMethod;

    public MyReentrantLockConditionProducerThread(MyReentrantLockConditionMethod conditionMethod) {
        this.conditionMethod = conditionMethod;
    }

    @Override
    public void run() {
        for(int i=0; i<Integer.MAX_VALUE; i++){
            conditionMethod.set();
        }
    }
}

创建测试类:

public class MyReentrantLockConditionMethodDemoTest {
    public static void main(String[] args) {
        MyReentrantLockConditionMethod conditionMethod = new MyReentrantLockConditionMethod();

        MyReentrantLockConditionConsumerThread[] consumerThreadArray = new MyReentrantLockConditionConsumerThread[10];
        MyReentrantLockConditionProducerThread[] producerThreadArray = new MyReentrantLockConditionProducerThread[10];

        for (int i=0; i<10; i++){
            producerThreadArray[i] = new MyReentrantLockConditionProducerThread(conditionMethod);
            consumerThreadArray[i] = new MyReentrantLockConditionConsumerThread(conditionMethod);

            Thread t1 = new Thread(producerThreadArray[i]);
            Thread t2 = new Thread(consumerThreadArray[i]);

            t1.start();
            t2.start();
        }

    }
}

输出结果:

打印★
  有可能★★★连续
  有可能★★★连续
  有可能★★★连续
  有可能★★★连续
  有可能★★★连续
打印☆
 有可能连续☆☆☆
 有可能连续☆☆☆
 有可能连续☆☆☆
 有可能连续☆☆☆
打印★
  有可能★★★连续
打印☆
 有可能连续☆☆☆
打印★
  有可能★★★连续
  有可能★★★连续
  有可能★★★连续
  有可能★★★连续
  有可能★★★连续
  有可能★★★连续
  有可能★★★连续
打印☆
 有可能连续☆☆☆
打印★
  有可能★★★连续
打印☆
 有可能连续☆☆☆
 有可能连续☆☆☆
 有可能连续☆☆☆
 有可能连续☆☆☆
 有可能连续☆☆☆
 有可能连续☆☆☆
打印★
  有可能★★★连续
打印☆
 有可能连续☆☆☆
打印★
  有可能★★★连续
打印☆
 有可能连续☆☆☆
 有可能连续☆☆☆
打印★
.....

公平锁与非公平锁

Lock 锁分为 “公平锁” 和 “非公平锁”。 公平锁表示线程获取锁的顺序是按照加锁的顺序来分配的,即先来先得的 FIFO 先进先出的顺序。而非公平锁就是一种 获取锁的抢占机制,是随机获取锁的,这种机制可能造成某些线程一直拿不到锁。

公平锁测试

创建 方法类

public class LockFairMethod {
    private ReentrantLock lock;

    public LockFairMethod(boolean isFair) {
        lock = new ReentrantLock(isFair);
    }

    public void lockMethodService(){
        try{
            lock.lock();
            System.out.println("ThreadName= " + Thread.currentThread().getName() + " 获得锁定");
        }finally {
            lock.unlock();
        }
    }
}

创建测试类

public class LockRunFairDemoTest {
    public static void main(String[] args) {
        final LockFairMethod lockFairMethod = new LockFairMethod(true);
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                System.out.println("★线程 " + Thread.currentThread().getName() + "运行了 ");
                lockFairMethod.lockMethodService();
            }
        };

        Thread[] threadArray = new Thread[10];
        for (int i=0; i<10; i++){
            threadArray[i] = new Thread(runnable);
        }

        for(int i=0; i<10; i++){
            threadArray[i].start();
        }
    }
}

运行结果:

★线程 Thread-1运行了 
★线程 Thread-4运行了 
★线程 Thread-3运行了 
★线程 Thread-2运行了 
★线程 Thread-0运行了 
★线程 Thread-6运行了 
ThreadName= Thread-1 获得锁定
★线程 Thread-8运行了 
★线程 Thread-5运行了 
★线程 Thread-9运行了 
★线程 Thread-7运行了 
ThreadName= Thread-4 获得锁定
ThreadName= Thread-3 获得锁定
ThreadName= Thread-2 获得锁定
ThreadName= Thread-0 获得锁定
ThreadName= Thread-6 获得锁定
ThreadName= Thread-8 获得锁定
ThreadName= Thread-5 获得锁定
ThreadName= Thread-9 获得锁定
ThreadName= Thread-7 获得锁定

根据结果可以看到是按照线程运行的先后活得 lock 锁。

非公平锁测试

输出结果:

★线程 Thread-0运行了 
★线程 Thread-7运行了 
★线程 Thread-5运行了 
★线程 Thread-6运行了 
★线程 Thread-4运行了 
★线程 Thread-3运行了 
★线程 Thread-2运行了 
★线程 Thread-1运行了 
★线程 Thread-9运行了 
ThreadName= Thread-0 获得锁定
★线程 Thread-8运行了 
ThreadName= Thread-8 获得锁定
ThreadName= Thread-7 获得锁定
ThreadName= Thread-5 获得锁定
ThreadName= Thread-6 获得锁定
ThreadName= Thread-4 获得锁定
ThreadName= Thread-3 获得锁定
ThreadName= Thread-2 获得锁定
ThreadName= Thread-1 获得锁定
ThreadName= Thread-9 获得锁定

根据结果可以看出获得锁的顺序并没有按照线程的启动顺序来获得锁定。

public int getHoldCount() 方法的使用

该方法的作用是查询 "当前线程" 保持锁定的个数,即调用 lock() 方法的次数。

创建方法类:

public class GetholdCountMethodDemo {
    private ReentrantLock lock = new ReentrantLock(true);

    public void lockMethod1(){
        System.out.println("A " + lock.getHoldCount());
        lock.lock();;
        System.out.println("B " + lock.getHoldCount());
        lockMethod2();
        System.out.println("F " + lock.getHoldCount());
        lock.unlock();
        System.out.println("G " + lock.getHoldCount());
    }

    public void lockMethod2(){
        System.out.println("C " + lock.getHoldCount());
        lock.lock();;
        System.out.println("D " + lock.getHoldCount());
        lock.unlock();
        System.out.println("E " + lock.getHoldCount());

    }
}

创建测试类

public class GetholdCountMethodDemoTest {
    public static void main(String[] args) {
        GetholdCountMethodDemo holdCountMethodDemo = new GetholdCountMethodDemo();
        holdCountMethodDemo.lockMethod1();
    }
}

运行结果如下:

A 0
B 1
C 1
D 2
E 1
F 1
G 0

执行 lock() 方法进行锁重入导致 count 计数加 1 的效果,执行 unlock() 方法会使 count 呈减 1 的效果。

public final int getQueueLength() 方法的使用

该方法的作用是返回正在等待获取此锁的线程估计数。

创建方法类:

public class GetQueueLengthMethodDemo {
    private ReentrantLock lock = new ReentrantLock(true);

    public void queueLengthMethodDemo(){
        try{
            lock.lock();
            System.out.println("ThreadName= " + Thread.currentThread().getName() + "进入方法");
            Thread.sleep(Integer.MAX_VALUE);
        }catch (Exception e){
            // doNothing
        }finally {
            lock.unlock();
        }
    }

    public ReentrantLock getLock() {
        return lock;
    }
}

创建测试类:

public class GetQueueLengthMethodDemoTest {
    public static void main(String[] args) {
        GetQueueLengthMethodDemo queueLengthMethodDemo = new GetQueueLengthMethodDemo();

        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                queueLengthMethodDemo.queueLengthMethodDemo();
            }
        };

        Thread[] threadArray = new Thread[10];
        for (int i=0; i<10; i++){
            threadArray[i] = new Thread(runnable);
        }

        for(int i=0; i<10; i++){
            threadArray[i].start();
        }

        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            // doNothing
        }

        System.out.println("有线程数: " + queueLengthMethodDemo.getLock().getQueueLength() + "在等待获取锁");
    }
}

输出结果如下:

ThreadName= Thread-0进入方法
有线程数: 9在等待获取锁

public int getWaitQueueLength(Condition condition) 方法的使用

该方法的作用是返回等待与此锁相关给定条件 Condition 的线程估计数。即有多少个线程执行了 condition 对象中的 await() 方法。

public final boolean hasQueueThread(Thread thread) 方法的使用

该方法的作用是查询指定的线程是否正在等待获取此锁,也就是判断参数中的线程是否在等待队列中。

public final boolean hasQueuedThreads() 方法的使用

该方法的作用是查询是否有线程正在等待获取此锁,也就是等待队列中是否有等待的线程。

public boolean hasWaiters(Condition condition) 方法的使用

该方法的作用是查询是否有线程正在等待与此锁有关的 condtion 条件,即线程是否执行了 condition 的 await() 方法而处于等待状态。

public final boolean isFair() 方法的使用

该方法的作用是判断 lock 锁是否是公平锁。

public boolean isHeldByCurrentThread() 方法的使用

该方法的作用查询当前线程是否保持此锁。

public boolean isLocked() 方法的使用

该方法的作用是查询此锁是否由任意线程保持并没有被释放。

public void lockInterruptibly() 方法的使用

该方法的作用是当某个线程尝试获得锁并且阻塞在 lockInterruptibly() 方法时,该线程可以被中断。即优先响应中断,而不是等待线程获取锁。

public boolean tryLock() 方法的使用

该方法的作用是嗅探拿锁,如果当前线程发现锁被其他线程持有,则立即返回 false。程序继续执行后面的代码,而不是呈阻塞等待锁的状态。

public boolean tryLock(long timeout, TimeUnit unit) 方法的使用

该方法的作用是嗅探拿锁,如果当前线程发现锁被其他线程持有了,则返回 false; 程序继续执行后面的代码,而不会阻塞。如果当前线程在指定的 timeout 内持有了锁; 则返回 true; 超时则返回 false。

public boolean await(long time, Timunit unit) 方法的使用

该方法的作用与 public final native void wait(long timeout) 方法一样,都具有自动唤醒线程的功能。

public long awaitNanos(long naonsTimeout) 方法的使用

该方法的作用和 public final native void wait(long timeout) 方法一样,区别是时间单位为纳秒(ns)级时间唤醒。

public boolean awaitUntil(Date deadline) 方法的使用

该方法的作用是在指定的 Date 结束等待。

public void awaitUninterruptibly() 方法的使用

该方法的作用是实现线程在等待的过程中, 不允许被中断。

ReentrantReadWriteLock 类

ReentrantLock 类具有完全互斥排他的效果,同一时间只有一个线程在执行 ReentrantLock.lock() 方法后面的任务,这样做虽然保证了线程的安全性,但 效率非常低下。所以 JDK 提供了一种读写锁——ReentrantReadWriteLock 类, 使用它可以在进行读操作时不需要同步执行,提升效率。

读写锁分为两个锁:一个是读锁,也称之为共享锁,即读锁之间不互斥;另一个是写锁,也称之为排他锁,即写锁互斥同步;

读读共享

比较一下 ReentrantLock 与 ReentrantReadWriteLock 比较:

创建 ReentrantLock 方法:

public class ReentrantLockDemo {
    private ReentrantLock lock = new ReentrantLock();
    private String username = "abc";

    public void testMethod(){
        try{
            lock.lock();
            System.out.println("begin " + Thread.currentThread().getName() + " " + System.currentTimeMillis());
            System.out.println("print service " + username);
            Thread.sleep(4000);
            System.out.println("    end " + Thread.currentThread().getName() + " " + System.currentTimeMillis());
            lock.unlock();
        }catch (Exception e){
            // doNothing
        }
    }
}

创建线程类:

public class ReentRantLockThread implements Runnable{

    private ReentrantLockDemo reentrantLockDemo;

    public ReentRantLockThread(ReentrantLockDemo reentrantLockDemo) {
        this.reentrantLockDemo = reentrantLockDemo;
    }

    @Override
    public void run() {
        reentrantLockDemo.testMethod();
    }
}

创建测试类:

public class ReentRantLockThreadTest {
    public static void main(String[] args) {
        ReentrantLockDemo reentrantLockDemo = new ReentrantLockDemo();
        ReentRantLockThread reentRantLockThread = new ReentRantLockThread(reentrantLockDemo);
        Thread t1 = new Thread(reentRantLockThread);
        t1.start();

        Thread t2 = new Thread(reentRantLockThread);
        t2.start();
    }
}

运行结果:

begin Thread-0 1659025344555
print service abc
    end Thread-0 1659025348560
begin Thread-1 1659025348560
print service abc
    end Thread-1 1659025352560

根据结果可以看到两个线程读取实例变量共耗时 5 s。只是简单地读取两个线程就要耗时 2.5 s, 非常浪费 CPU 资源。其实读取实例变量是可以同时进行的。

创建 ReentrantReadWriteLock 类

public class ReentrantReadWriteLockDemo {
    private ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
    private String userName = "abc";

    public void tetMethod(){
        try{
            readWriteLock.readLock().lock();
            System.out.println("begin " + Thread.currentThread().getName() + " " + System.currentTimeMillis());
            System.out.println("print service " + userName);
            Thread.sleep(4000);
            System.out.println("    end " + Thread.currentThread().getName() + " " + System.currentTimeMillis());
        }catch (Exception e){
            // doNothing
        }finally {
            readWriteLock.readLock().unlock();
        }
    }
}

创建线程类:

public class ReentrantReadWriteLockThread implements Runnable {

    private ReentrantReadWriteLockDemo reentrantReadWriteLockDemo;

    public ReentrantReadWriteLockThread(ReentrantReadWriteLockDemo reentrantReadWriteLockDemo) {
        this.reentrantReadWriteLockDemo = reentrantReadWriteLockDemo;
    }

    @Override
    public void run() {
        reentrantReadWriteLockDemo.tetMethod();
    }
}

创建测试类

public class ReentrantReadWriteLockDemoTest {
    public static void main(String[] args) {
        ReentrantReadWriteLockDemo reentrantReadWriteLockDemo = new ReentrantReadWriteLockDemo();
        ReentrantReadWriteLockThread readWriteLockThread = new ReentrantReadWriteLockThread(reentrantReadWriteLockDemo);
        Thread t1 = new Thread(readWriteLockThread);
        t1.start();

        Thread t2 = new Thread(readWriteLockThread);
        t2.start();
    }
}

运行结果:

begin Thread-0 1659027104619
begin Thread-1 1659027104619
print service abc
print service abc
    end Thread-1 1659027108621
    end Thread-0 1659027108621

根据结果可以看出两个线程是同时进入 lock() 方法后面的代码,共耗时 3 s。可以看到即使加锁,读操作也是异步的。

读写互斥

创建读写方法:

public class ReentrantReadWriteLockMultDemo {
    private ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();

    public void read(){
        try {
            readWriteLock.readLock().lock();
            System.out.println("获得读锁" + Thread.currentThread().getName() + " " + System.currentTimeMillis());
            Thread.sleep(10000);
        } catch (Exception e){
            // doNothing
        } finally {
            readWriteLock.readLock().unlock();
        }
    }


    public void write(){
        try{
            readWriteLock.writeLock().lock();
            System.out.println("获得写锁" + Thread.currentThread().getName() + " " + System.currentTimeMillis());
            Thread.sleep(10000);
        }catch (Exception e){
            // doNothing
        }finally {
            readWriteLock.writeLock().unlock();
        }
    }
}

创建读写线程类:

public class ReentrantReadWriteLockReadMultThread implements Runnable {

    private ReentrantReadWriteLockMultDemo readWriteLockMultDemo;

    public ReentrantReadWriteLockReadMultThread(ReentrantReadWriteLockMultDemo readWriteLockMultDemo) {
        this.readWriteLockMultDemo = readWriteLockMultDemo;
    }

    @Override
    public void run() {
        readWriteLockMultDemo.read();
    }
}


public class ReentrantReadWriteLockWriteMultThread implements Runnable {

    private ReentrantReadWriteLockMultDemo readWriteLockMultDemo;

    public ReentrantReadWriteLockWriteMultThread(ReentrantReadWriteLockMultDemo readWriteLockMultDemo) {
        this.readWriteLockMultDemo = readWriteLockMultDemo;
    }

    @Override
    public void run() {
        readWriteLockMultDemo.write();
    }
}

创建测试类:

public class ReentrantReadWriteLockMultDemoTest {
    public static void main(String[] args) {
        ReentrantReadWriteLockMultDemo readWriteLockMultDemo = new ReentrantReadWriteLockMultDemo();
        ReentrantReadWriteLockReadMultThread readMultThread = new ReentrantReadWriteLockReadMultThread(readWriteLockMultDemo);
        Thread t1 = new Thread(readMultThread);
        t1.start();

        ReentrantReadWriteLockWriteMultThread writeLockWriteMultThread = new ReentrantReadWriteLockWriteMultThread(readWriteLockMultDemo);
        Thread t2 = new Thread(writeLockWriteMultThread);
        t2.start();
    }
}

输出结果:

获得读锁Thread-0 1659030187633
获得写锁Thread-1 1659030197637

从输出结果可以说明 "读写" 操作是互斥的。