《Java 多线程编程核心技术》笔记——第4章 Lock 的使用(二)

声明:

本博客是本人在学习《Java 多线程编程核心技术》后整理的笔记,旨在方便复习和回顾,并非用作商业用途。

本博客已标明出处,如有侵权请告知,马上删除。

4.1 使用 ReentrantLock 类

4.1.10 方法 getHoldCount()、getQueueLength() 和 getWaitQueueLength() 的测试

getHoldCount()

方法 int getHoldCount() 的作用是查询当前线程保持此锁定的个数,也就是调用 lock() 方法的次数

  1. 创建公共类

    public class Service {
        private ReentrantLock lock = new ReentrantLock();
        public void serviceMethod1() {
            try {
                lock.lock();
                System.out.println("serviceMethod1 getHoldCount = " + lock.getHoldCount());
                serviceMethod2();
            }finally {
                lock.unlock();
            }
        }
    
        public void serviceMethod2() {
            try {
                lock.lock();
                System.out.println("serviceMethod2 getHoldCount = " + lock.getHoldCount());
            }finally {
                lock.unlock();
            }
        }
    }
    
  2. 测试类

    public class Run {
        public static void main(String[] args) {
            // getiHoldCount的作用是查询当前线程保持此锁定的个数,也就是调用lock()方法次数。
            Service service = new Service();
            service.serviceMethod1();
        }
    }
    

    运行结果:

    serviceMethod1 getHoldCount = 1
    serviceMethod2 getHoldCount = 2
    

getQueueLength()

方法 int getQueueLength() 的作用是返回正等待获取此锁定的线程估计数。比如有 5 个线程,1 个线程首先执行 await() 方法,那么在调用 getQueueLength() 方法后返回值是 4,说明有 4 个线程同时再等待 lock 的释放。

  1. 创建公共类

    public class Service {
        public ReentrantLock lock = new ReentrantLock();
    
        public void serviceMethod1() {
            try {
                lock.lock();
                System.out.println("Thread name = " + Thread.currentThread().getName() + " in");
                Thread.sleep(Integer.MAX_VALUE);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }finally {
                lock.unlock();
            }
        }
    }
    
  2. 测试类

    public class Run {
        public static void main(String[] args) throws InterruptedException {
            final Service service = new Service();
            
            Runnable runnable = new Runnable() {
                @Override
                public void run() {
                    service.serviceMethod1();
             }
            };
    
            Thread[] thread = new Thread[10];
            for (int i = 0; i < 10; i++) {
                thread[i] = new Thread(runnable);
            }
    
            for (int i = 0; i < 10; i++) {
                thread[i].start();
            }
            Thread.sleep(2000);
            System.out.println(service.lock.getQueueLength() + " processes is waiting!");
        }
    }
    

    运行结果:

    Thread name = Thread-0 in
    9 processes is waiting!
    

getWaitQueueLength

方法 int getWaitQueueLength(Condition condition) 的作用是返回等待与此锁定相关的给定条件 Condition 的线程估计数,比如 5 个线程,每个线程都执行了同一个 condition 对象的 await() 方法,则调用 getWaitQueueLength(Condition condition) 方法时返回的 int 值是 5 。

  1. 创建公共类

    public class Service {
        private ReentrantLock lock = new ReentrantLock();
        private Condition newCondition = lock.newCondition();
    
        public void waitMethod() {
            try {
                lock.lock();
                newCondition.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
    
        public void notifyMethod() {
            try {
                lock.lock();
                System.out.println("有 " + lock.getWaitQueueLength(newCondition) + " 个线程正在等待newCondition");
                newCondition.signal();
            } finally {
                lock.unlock();
            }
        }
    }
    
  2. 测试类

    public class Run {
        public static void main(String[] args) throws InterruptedException {
            final Service service = new Service();
            Runnable runnable = new Runnable() {
                @Override
                public void run() {
                    service.waitMethod();
                }
            };
    
         Thread[] threads = new Thread[10];
            for (int i = 0; i < threads.length; i++) {
                threads[i] = new Thread(runnable);
            }
            for (int i = 0; i < threads.length; i++) {
                threads[i].start();
            }
            Thread.sleep(2000);
            service.notifyMethod();
        }
    }
    
    

    运行结果:

    有 10 个线程正在等待newCondition
    

4.1.11 方法 hasQueuedThread()、hasQueuedThreads() 和 hasWaiters() 的测试

hasQueuedThread

  • 方法 boolean hasQueuedThread(Thread thread) 的作用是查询指定的线程是否正在等待获取此锁定

  • 方法 boolean hasQueuedThreads() 的作用是查询是否有线程正在等待获取此锁定

  1. 创建公共类

    public class Service {
        public ReentrantLock lock = new ReentrantLock();
        public Condition newCondition = lock.newCondition();
    
        public void waitMethod() {
            try {
                lock.lock();
                Thread.sleep(Integer.MAX_VALUE);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
    }
    
  2. 测试类

    public class Run {
        public static void main(String[] args) throws InterruptedException {
            final Service service = new Service();
            Runnable runnable = new Runnable() {
                @Override
                public void run() {
                    service.waitMethod();
                }
            };
            Thread threadA = new Thread(runnable);
            threadA.start();
            Thread.sleep(500);
            Thread threadB = new Thread(runnable);
            threadB.start();
            Thread.sleep(500);
            /**
             * hasQueuedThread(thread)查询知道这个的线程是否正在等待获取此lock
             * hasQueuedThreads()则是查询是否有线程正在等待获取此lock
             */
            System.out.println(service.lock.hasQueuedThread(threadA));
            System.out.println(service.lock.hasQueuedThread(threadB));
            System.out.println(service.lock.hasQueuedThreads());
    
        }
    }
    

    运行结果:

    false
    true
    true
    

hasWaiters

方法 boolean hasWaiters(Condition condition) 的作用是查询是否有线程正在等待与此锁定有关的 condition 条件

  1. 创建公共类

    public class Service {
        public ReentrantLock lock = new ReentrantLock();
        public Condition newCondition = lock.newCondition();
    
        public void waitMethod() {
            try {
                lock.lock();
                newCondition.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
    
        public void notifyMethod() {
            try {
                lock.lock();
                System.out.println("有没有线程正在等待newCondition? " + lock.hasWaiters(newCondition) + " 线程数是多少? " + lock.getWaitQueueLength(newCondition));
                newCondition.signal();
            } finally {
                lock.unlock();
            }
        }
    }
    
  2. 测试类

    public class Run {
        public static void main(String[] args) throws InterruptedException {
            final Service service = new Service();
            Runnable runnable = new Runnable() {
                @Override
                public void run() {
                    service.waitMethod();
                }
            };
            Thread[] threads = new Thread[10];
            for (int i = 0; i < threads.length; i++) {
                threads[i] = new Thread(runnable);
            }
            for (int i = 0; i < threads.length; i++) {
                threads[i].start();
            }
            Thread.sleep(2000);
            service.notifyMethod();
        }
    }
    

    运行结果:

    有没有线程正在等待newCondition? true 线程数是多少? 10
    

4.1.12 方法 isFair()、isHeldByCurrentThread() 和 isLocked() 的测试

isFair()

方法 boolean isFair() 的作用是判断是不是公平锁。在默认的情况下,ReentrantLock 类使用的是非公平锁。

  1. 创建公共类

    public class Service {
        private ReentrantLock lock = new ReentrantLock();
    
        public Service(boolean isFair) {
            lock = new ReentrantLock(isFair);
        }
    
        public void serviceMethod() {
            try {
                lock.lock();
                System.out.println("公平锁的情况: " + lock.isFair());
            } finally {
                lock.unlock();
            }
        }
    }
    
  2. 测试类

    public class Run {
        public static void main(String[] args) {
            final Service service1 = new Service(true);
            final Service service2 = new Service(false);
            Runnable runnable1 = new Runnable() {
                @Override
                public void run() {
                    service1.serviceMethod();
                }
            };
            Runnable runnable2 = new Runnable() {
                @Override
                public void run() {
                    service2.serviceMethod();
                }
            };
            //ReentrantLock 默认是非公平锁
            Thread t1 = new Thread(runnable1);
            Thread t2 = new Thread(runnable2);
            t1.start();
            t2.start();
        }
    }
    

    运行结果:

    公平锁的情况: true
    公平锁的情况: false
    

isHeldByCurrentThread()

方法 boolean isHeldByCurrentThread() 的作用是查询当前线程是否保持此锁定

  1. 创建公共类

    public class Service {
        private ReentrantLock lock;
    
        public Service(boolean isFair) {
            lock = new ReentrantLock(isFair);
        }
    
        public void serviceMethod() {
            try {
                System.out.println(lock.isHeldByCurrentThread());
                lock.lock();
                System.out.println(lock.isHeldByCurrentThread());
            } finally {
                lock.unlock();
            }
        }
    }
    
    
  2. 测试类

    public class Run {
        public static void main(String[] args) {
            final Service service1 = new Service(true);
            Runnable runnable = new Runnable() {
                @Override
                public void run() {
                    service1.serviceMethod();
                }
            };
            Thread thread = new Thread(runnable);
            //isHeldByCurrentThread 是查看当前线程是否保持此锁定。
            thread.start();
        }
    }
    

    运行结果:

    false
    true
    

isLocked()

方法 boolean isLocked() 的作用是查询此锁定是否由任意线程保持

  1. 创建公共类

    public class Service {
        private ReentrantLock lock;
    
        public Service(boolean isFair) {
            super();
            lock = new ReentrantLock(isFair);
        }
    
        public void serviceMethod() {
            try {
                System.out.println(lock.isLocked());
                lock.lock();
                System.out.println(lock.isLocked());
            } finally {
                lock.unlock();
            }
        }
    }
    
  2. 测试类

    public class Run {
        public static void main(String[] args) {
            final Service service1 = new Service(true);
            Runnable runnable = new Runnable() {
                @Override
                public void run() {
                    service1.serviceMethod();
                }
            };
            Thread thread = new Thread(runnable);
            //isLocked作用是查询此锁定是否由任意线程保持
            thread.start();
        }
    }
    

    运行结果:

    false
    true
    

4.1.13 方法 lockInterruptibly()、tryLock() 和 tryLock(long timeout,TimeUnit unit) 的测试

lockInterruptibly()

方法 void lockInterruptibly() 的作用是:如果当前线程未被中断,则获取锁定,如果已经被中断,则出现异常

  1. 创建公共类

    public class Service {
        public ReentrantLock lock = new ReentrantLock();
        private Condition condition = lock.newCondition();
    
        public void waitMethod(){
            try {
                lock.lock();
                System.out.println("lock begin " + Thread.currentThread().getName());
                for (int i = 0; i < Integer.MAX_VALUE / 10; i++) {
                    String newString = "";
                    Math.random();
                }
                System.out.println("lock   end " + Thread.currentThread().getName());
            } finally {
                if (lock.isHeldByCurrentThread()) {
                    lock.unlock();
                }
            }
        }
    }
    
  2. 测试类

    public class Run {
        public static void main(String[] args) throws InterruptedException {
            final Service service = new Service();
            Runnable runnableRef = new Runnable() {
                @Override
                public void run() {
                    service.waitMethod();
                }
            };
            Thread threadA = new Thread(runnableRef);
            threadA.setName("A");
            threadA.start();
            Thread.sleep(500);
            Thread threadB = new Thread(runnableRef);
            threadB.setName("B");
            threadB.start();
            threadB.interrupt(); // 打标记
            System.out.println("main end!");
        }
    }
    

    运行结果:

    lock begin A
    main end!
    lock   end A
    lock begin B
    lock   end B
    

    没有出现异常,A、B 线程正常结束,按钮变灰。

    前面实验使用的是 Lock() 方法,说明线程 B 被 interrupt 中断了,那么执行 lock() 则不出现异常,正常执行。

  3. 将类 Service.java 中原有代码 “lock.lock()” 变成 “lock.lockInterruptibly()”,运行结果如下

    lock begin A
    java.lang.InterruptedException
    	at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireInterruptibly(AbstractQueuedSynchronizer.java:1220)
    	at java.util.concurrent.locks.ReentrantLock.lockInterruptibly(ReentrantLock.java:335)
    	at rt9.Service.waitMethod(Service.java:12)
    	at rt9.Run$1.run(Run.java:9)
    	at java.lang.Thread.run(Thread.java:745)
    main end!
    lock   end A
    

tryLock()

方法 boolean tryLock() 的作用是,仅在调用时锁定未被另一个线程保持的情况下,才获得该锁定

  1. 创建公共类

    public class Service {
        public ReentrantLock lock = new ReentrantLock();
    
        public void waitMethod() {
            if (lock.tryLock()) {
                System.out.println(Thread.currentThread().getName() + "获得锁");
            } else {
                System.out.println(Thread.currentThread().getName() + "没有获得锁");
            }
        }
    }
    
  2. 测试类

    public class Run {
        public static void main(String[] args) throws InterruptedException {
            final Service service = new Service();
    
            Runnable runnableRef = new Runnable() {
                @Override
                public void run() {
                    service.waitMethod();
                }
            };
            //tryLock没有参数时 仅在调用时锁定未被另一个线程保持的情况下,才或得该锁定。
            Thread threadA = new Thread(runnableRef);
            threadA.setName("A");
            threadA.start();
            Thread threadB = new Thread(runnableRef);
            threadB.setName("B");
            threadB.start();
        }
    }
    

    运行结果:

    A获得锁
    B没有获得锁
    

tryLock(long timeout, TimeUnit unit)

方法 boolean tryLock(long timeout,TimeUnit unit) 的作用是,如果锁定在给定等待时间内没有被另一个线程保持,且当前线程未被中断,则获取该锁定

  1. 创建公共类

    public class Service {
        public ReentrantLock lock = new ReentrantLock();
    
        public void waitMethod() {
            try {
                //如果3秒没有锁没有被其他线程保持,并且当前线程没有中断就或得锁定
                if (lock.tryLock(3, TimeUnit.SECONDS)) {
                    System.out.println("      " + Thread.currentThread().getName()
                            + "获得锁的时间:" + System.currentTimeMillis());
                    Thread.sleep(10000);
                } else {
                    System.out.println("      " + Thread.currentThread().getName()
                            + "没有获得锁");
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                if (lock.isHeldByCurrentThread()) {
                    lock.unlock();
                }
            }
        }
    }
    
  2. 测试类

    public class Run {
        public static void main(String[] args) throws InterruptedException {
            final Service service = new Service();
    
            Runnable runnableRef = () -> {
                System.out.println(Thread.currentThread().getName() + "调用waitMethod时间:" + System.currentTimeMillis());
                service.waitMethod();
            };
            Thread threadA = new Thread(runnableRef);
            threadA.setName("A");
            threadA.start();
            Thread.sleep(500);
            Thread threadB = new Thread(runnableRef);
            threadB.setName("B");
            threadB.start();
        }
    }
    

    运行结果:

    A调用waitMethod时间:1608024376272
          A获得锁的时间:1608024376274
    B调用waitMethod时间:1608024376772
          B没有获得锁
    

4.1.14 方法 awitUniterruptibly() 的使用

当调用了 thread.interrupt() 方法之后,condition.await() 方法会报异常,但是调用 awaitUniterruptibly() 方法不会报异常

  1. 创建公共类

    public class Service {
        private ReentrantLock lock = new ReentrantLock();
        private Condition condition = lock.newCondition();
    
        public void testMethod() {
            try {
                lock.lock();
                System.out.println("wait begin");
                condition.await();
                System.out.println("wait   end");
            } catch (InterruptedException e) {
                e.printStackTrace();
                System.out.println("catch");
            } finally {
                lock.unlock();
            }
    
        }
    }
    
  2. 创建自定义线程类

    public class MyThread extends Thread {
        private Service service;
    
        public MyThread(Service service) {
            this.service = service;
        }
    
        @Override
        public void run() {
            service.testMethod();
        }
    }
    
  3. 测试类

    public class Run {
        public static void main(String[] args) {
            try {
                Service service = new Service();
                MyThread myThread = new MyThread(service);
                myThread.start();
                Thread.sleep(3000);
                myThread.interrupt();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    

    运行结果:

    wait begin
    catch
    java.lang.InterruptedException
    	at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.reportInterruptAfterWait(AbstractQueuedSynchronizer.java:2014)
    	at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2048)
    	at rt12.Service.testMethod(Service.java:14)
    	at rt12.MyThread.run(MyThread.java:12)
    

    程序运行后出现异常,这是正常现象。

  4. 更改 Service.java 类,修改 condition.await() 为 condition.awaitUninterruptibly()

    public class Service {
        private ReentrantLock lock = new ReentrantLock();
        private Condition condition = lock.newCondition();
    
        public void testMethod() {
            try {
                lock.lock();
                System.out.println("wait begin");
                condition.awaitUninterruptibly();
                System.out.println("wait   end");
            } finally {
                lock.unlock();
            }
    
        }
    }
    

    运行结果:

    wait begin
    

4.1.15 方法 awaitUntil() 的使用

调用 awaitUntil(long time) 是等待 time 时间后自动唤醒

  1. 创建公共类

    public class Service {
        private ReentrantLock lock = new ReentrantLock();
        private Condition condition = lock.newCondition();
    
        public void waitMethod() {
            try {
                Calendar calendarRef = Calendar.getInstance();
                calendarRef.add(Calendar.SECOND, 10);
                lock.lock();
                System.out.println("wait begin timer=" + System.currentTimeMillis());
                condition.awaitUntil(calendarRef.getTime());
                System.out.println("wait   end timer=" + System.currentTimeMillis());
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
    
        }
    
        public void notifyMethod() {
            try {
                Calendar calendarRef = Calendar.getInstance();
                calendarRef.add(Calendar.SECOND, 10);
                lock.lock();
                System.out.println("notify begin timer=" + System.currentTimeMillis());
                condition.signalAll();
                System.out.println("notify   end timer=" + System.currentTimeMillis());
            } finally {
                lock.unlock();
            }
    
        }
    }
    
  2. 创建 2 个自定义线程类

    public class MyThreadA extends Thread {
        private Service service;
    
        public MyThreadA(Service service) {
            super();
            this.service = service;
        }
    
        @Override
        public void run() {
            service.waitMethod();
        }
    }
    
    public class MyThreadB extends Thread {
        private Service service;
    
        public MyThreadB(Service service) {
            super();
            this.service = service;
        }
    
        @Override
        public void run() {
            service.notifyMethod();
        }
    }
    
  3. 测试类

    public class Run1 {
        public static void main(String[] args){
         Service service = new Service();
            MyThreadA myThreadA = new MyThreadA(service);
         myThreadA.start();
        }
    }
    

    运行结果:

    wait begin timer=1608027202380
    wait   end timer=1608027212365
    
  4. 测试类2

    public class Run2 {
        public static void main(String[] args) throws InterruptedException {
            Service service = new Service();
            MyThreadA myThreadA = new MyThreadA(service);
            MyThreadB myThreadB = new MyThreadB(service);
            myThreadA.start();
            Thread.sleep(2000);
            myThreadB.start();
        }
    }
    

    运行结果:

    wait begin timer=1608027571582
    notify begin timer=1608027573553
    notify   end timer=1608027573553
    wait   end timer=1608027573553
    

说明线程在等待时间到达前,可以被其他线程提前唤醒。

4.1.16 使用 Condition 实现顺序执行

使用 Condition 对象可以对线程执行的业务进行排序规划

测试类:

public class Run {
    volatile private static int nextPrintWho = 1;
    private static ReentrantLock lock = new ReentrantLock();
    final private static Condition conditionA = lock.newCondition();
    final private static Condition conditionB = lock.newCondition();
    final private static Condition conditionC = lock.newCondition();

    public static void main(String[] args) {

        Thread threadA = new Thread(() -> {
            try {
                lock.lock();
                while (nextPrintWho != 1) {
                    conditionA.await();
                }
                for (int i = 0; i < 3; i++) {
                    System.out.println("ThreadA " + (i + 1));
                }
                nextPrintWho = 2;
                conditionB.signalAll();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        });

        Thread threadB = new Thread(() -> {
            try {
                lock.lock();
                while (nextPrintWho != 2) {
                    conditionB.await();
                }
                for (int i = 0; i < 3; i++) {
                    System.out.println("ThreadB " + (i + 1));
                }
                nextPrintWho = 3;
                conditionC.signalAll();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        });

        Thread threadC = new Thread(() -> {
            try {
                lock.lock();
                while (nextPrintWho != 3) {
                    conditionC.await();
                }
                for (int i = 0; i < 3; i++) {
                    System.out.println("ThreadC " + (i + 1));
                }
                nextPrintWho = 1;
                conditionA.signalAll();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        });
        Thread[] aArray = new Thread[5];
        Thread[] bArray = new Thread[5];
        Thread[] cArray = new Thread[5];

        for (int i = 0; i < 5; i++) {
            aArray[i] = new Thread(threadA);
            bArray[i] = new Thread(threadB);
            cArray[i] = new Thread(threadC);

            aArray[i].start();
            bArray[i].start();
            cArray[i].start();
        }

    }
}

运行结果:

ThreadA 1
ThreadA 2
ThreadA 3
ThreadB 1
ThreadB 2
ThreadB 3
ThreadC 1
ThreadC 2
ThreadC 3
ThreadA 1
ThreadA 2
ThreadA 3
ThreadB 1
ThreadB 2
ThreadB 3
ThreadC 1
ThreadC 2
ThreadC 3
ThreadA 1
ThreadA 2
ThreadA 3
ThreadB 1
ThreadB 2
ThreadB 3
ThreadC 1
ThreadC 2
ThreadC 3
ThreadA 1
ThreadA 2
ThreadA 3
ThreadB 1
ThreadB 2
ThreadB 3
ThreadC 1
ThreadC 2
ThreadC 3
ThreadA 1
ThreadA 2
ThreadA 3
ThreadB 1
ThreadB 2
ThreadB 3
ThreadC 1
ThreadC 2
ThreadC 3

4.2 使用 ReentrantReadWriteLock 类

类 ReentrantLock 具有完全互斥排他的效果,即同一时间只有一个线程在执行 ReentrantLock.lock() 方法后面的任务。这样做虽然保证了实例变量的线程安全性,但效率却是非常低下的。所以在 JDK 中提供了一种读写锁 ReentrantReadWriteLock 类,使用它可以加快运行效率,在某些不需要操作实例变量的方法中,完全可以使用读写锁 ReentrantReadWriteLock 来提升该方法的代码运行速度

读写锁表示也有两个锁,一个是读操作相关的锁,也称为共享锁;另一个是写操作相关的锁,也叫排他锁。也就是多个读锁之间不互斥,读锁与写锁互斥,写锁与写锁互斥。在没有线程 Thread 进行写入操作时,进行读取操作的多个 Thread 都可以获取读锁,而进入写入操作的 Thread 只有在获取写锁后才能进行写入操作。即多个 Thread 可以同时进行读取操作,但是同一时刻只允许一个 Thread 进行写入操作

4.2.1 类 ReentrantReadWriteLock 的使用:读读共享

  1. 创建公共类

    public class Service {
    
        private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
    
        public void read() {
            try {
                try {
                    lock.readLock().lock();
                    System.out.println("获得读锁" + Thread.currentThread().getName()
                            + " " + System.currentTimeMillis());
                    Thread.sleep(10000);
                } finally {
                    lock.readLock().unlock();
                }
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    
    }
    
  2. 创建 2 个自定义线程类

    public class ThreadA extends Thread {
    
        private Service service;
    
        public ThreadA(Service service) {
            super();
            this.service = service;
        }
    
        @Override
        public void run() {
            service.read();
        }
    }
    
    public class ThreadB extends Thread {
    
        private Service service;
    
        public ThreadB(Service service) {
            super();
            this.service = service;
        }
    
        @Override
        public void run() {
            service.read();
        }
    }
    
  3. 测试类

    public class Run {
        public static void main(String[] args) {
         Service service = new Service();
            ThreadA a = new ThreadA(service);
         ThreadB b = new ThreadB(service);
            b.setName("B");
            a.setName("A");
            b.start();
            a.start();
        }
    }
    

    运行结果:

    获得读锁B 1608031685984
    获得读锁A 1608031685984
    

从控制台中打印的时间来看,两个线程几乎同时进入 lock() 方法后面的代码。说明在此使用了 lock.readLock() 读锁可以提高程序运行效率,允许多个线程同时执行 lock() 方法后面的代码

4.2.2 类 ReentrantReadWriteLock 的使用:写写互斥

  1. 修改公共类

    public class Service {
    
        private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
    
        public void read() {
            try {
                try {
                    lock.writeLock().lock();
                    System.out.println("获得写锁" + Thread.currentThread().getName()
                            + " " + System.currentTimeMillis());
                    Thread.sleep(10000);
                } finally {
                    lock.writeLock().unlock();
                }
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    
    }
    
  2. 运行结果

    获得写锁B 1608030877595
    获得写锁A 1608030887596
    

使用写锁代码 lockwrieLock() 的效果就是同一时间只允许一个线程执行 lock() 方法后面的代码

4.2.3 类 ReentrantReadWriteLock 的使用:读写互斥

  1. 创建公共类

    public class Service {
    
        private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
    
        public void read() {
            try {
                try {
                    lock.readLock().lock();
                    System.out.println("获得读锁" + Thread.currentThread().getName()
                            + " " + System.currentTimeMillis());
                    Thread.sleep(10000);
                } finally {
                    lock.readLock().unlock();
                }
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    
        public void write() {
            try {
                try {
                    lock.writeLock().lock();
                    System.out.println("获得写锁" + Thread.currentThread().getName()
                            + " " + System.currentTimeMillis());
                    Thread.sleep(10000);
                } finally {
                    lock.writeLock().unlock();
                }
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }
    
  2. 创建 2 个自定义线程类

    public class ThreadA extends Thread {
    
        private Service service;
    
        public ThreadA(Service service) {
            super();
            this.service = service;
        }
    
        @Override
        public void run() {
            service.read();
        }
    }
    
    public class ThreadB extends Thread {
    
        private Service service;
    
        public ThreadB(Service service) {
            super();
            this.service = service;
        }
    
        @Override
        public void run() {
            service.write();
        }
    }
    
  3. 测试类

    public class Run {
        public static void main(String[] args) {
            try {
                Service service = new Service();
                ThreadA a = new ThreadA(service);
                ThreadB b = new ThreadB(service);
                a.setName("A");
                b.setName("B");
                a.start();
                Thread.sleep(1000);
                b.start();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    

    运行结果:

    获得读锁A 1608032477712
    获得写锁B 1608032487712
    

此实验说明 “读写” 操作是互斥的,而且下一个示例说明 “写读” 操作也是互斥的。

即只要出现“写操作”的过程,就是互斥的

4.2.4 类 ReentrantReadWriteLock 的使用:写读互斥

  1. 修改测试类

    public class Run {
        public static void main(String[] args) {
            try {
                Service service = new Service();
                ThreadA a = new ThreadA(service);
                ThreadB b = new ThreadB(service);
                a.setName("A");
                b.setName("B");
                b.start();
                Thread.sleep(1000);
                a.start();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    
  2. 运行结果

    获得写锁B 1608032791225
    获得读锁A 1608032801225
    

从控制台中打印的结果来看,“写读” 操作也是互斥的。

“读写”、“写读” 和 “写写” 都是互斥的;而 “读读” 是异步的,非互斥的

4.3 本章总结

本章的学习已经完毕,在本章中完全可以使用 Lock 对象将 synchronized 关键字替换掉,而且其具有的独特功能也是 synchronized 所不具有的。在学习并发时,Lock 是 synchronized 关键字的进阶,掌握 Lock 有助于学习并发包中源代码的实现原理,在并发包中大量的类使用了 Lock 接口作为同步的处理方式。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

bm1998

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值