在JDK5里面,提供了一个Lock接口。该接口通过底层框架的形式为设计更面向对象、可更加细粒度控制线程代码、更灵活控制线程通信提供了基础。实现Lock接口且使用得比较多的是可重入锁(ReentrantLock)以及读写锁(ReentrantReadWriteLock)。

1. ReentrantLock

  在Java多线程(二) 多线程的锁机制 里面,已经总结过通过使用Synchronized关键字实现线程内的方法锁定。但使用Synchronized关键字有一些局限性,上锁和释放锁是由JVM决定的,用户没法上锁和释放进行控制。那么问题就来了:假如有一个线程业务类管理某一全局变量的读和写。对于每条线程,在读的时候数据是共享的可以让多个线程同时去读。但有某个线程在对该全局变量进行写的时候,其他的线程都不能够对变量进行读或者写(对应数据库内的读共享写互斥)。可能会有如下伪代码:

 1 package com.scl.thread.lock;
 2 
 3 public class MyCounter
 4 {
 5     public int count;
 6 
 7     public int readCount()
 8     {
 9         return this.count;
10     }
11 
12     public void writeCount()
13     {
14         synchronized(this)
15         {
16             count++;
17         }
18     }
19 }

  尽管对写操作进行了空值,但是在写的时候,线程还是能够进行读操作!由此,JDK5并发库内提供了Lock接口。程序员可以通过实现Lock接口对代码块进行更灵活的锁控制.

 JDK5通过使用AbstractQueuedSynchronizer(简写为AQS)抽象类把Lock接口的功能实现了一大部分功能,如果程序员需要编写一套跟有自身逻辑的"锁"时,可以简单地通过实现public boolean tryAcquire(int acquire) 及 public boolean tryRelease(int releases) 进行加锁及释放锁功能。AQS为整个并发内容的核心框架,类似synchronized的锁(ReentrantLock :可重入锁)就是使用了AQS框架进行构建。ReentrantLock提供了一个可中断、拥有并发竞争机制[指线程对锁的竞争方式:公平竞争或不公平竞争]的方式,该部分的内容的源码分析可以查看: ReentrantLock 实现原理深入探究

        正如ReentrantLock跟Synchronized关键字所使用的功能基本一样,而且Synchronized还能自己释放锁,那什么时候使用ReentrantLock?

  ① 在中断线程的时候,可以使用ReentrantLock进行控制

    如线程1有一个耗时很大的任务在执行,执行时线程2必须进行等待。当线程1执行的任务时间实在太长了,线程2放弃等待进行线程后续的操作。该情况下如果使用Synchronized,只能通过抛出异常的形式进行异常操作。

  ② 多条件变量通讯

    如有3条线程,线程1完成任务后通知线程2执行,线程2执行完业务逻辑以后通知线程3执行,线程3执行完通知线程1继续执行。用Synchronized关键字很难处理这种问题。用Lock却可以很好的处理这些内容。当然,线程1 、2、3 同样地可以换由一个线程组去执行这些任务。  

   1.1  可中断的线程控制

       1.1.1  Java的线程中断机制

  Java中断线程可以通过实例方法: stop 或 interrupt 进行线程中断,两者有什么区别?先查看以下两段代码及运行结果。

  1 package com.scl.thread.interrupt;
  2 
  3 public class TestInterrupt
  4 {
  5     // 各线程可见的线程状态标志位
  6     public static volatile boolean isStop = false;
  7 
  8     public static void main(String[] args) throws InterruptedException
  9     {
 10         // 创建三条线程,线程1使用stop方法中断,线程2使用interrupt方法中断,线程3与线程2比较使用了interrupt后是否因中断退出
 11         Thread th1 = new Thread(new SubThread1(), "SubThread1");
 12         Thread th2 = new Thread(new SubThread2(), "SubThread2");
 13         Thread th3 = new Thread(new SubThread3(), "SubThread3");
 14 
 15         System.out.println("==============subThread1 code block result==============");
 16         System.out.println("Main Thread call subThread1 to start");
 17         th1.start();
 18         Thread.sleep(3000);
 19         System.out.println("Main Thread start to stop subThread1");
 20         th1.stop();
 21         System.out.println("subThread1 was stopped by Main Thread");
 22         // 等待子线程进行stop,让子线程有充分时间处理相关业务
 23         Thread.sleep(20);
 24         System.out.println("===================================================");
 25         Thread.sleep(20);
 26 
 27         System.out.println("==============subThread2 code block result==============");
 28         System.out.println("Main Thread call subThread2 to start");
 29         th2.start();
 30         Thread.sleep(3000);
 31         System.out.println("Main Thread start to interrupt subThread2");
 32         // 设置标志位,令子线程2可以按顺序退出
 33         isStop = true;
 34         th2.interrupt();
 35         // 等待子线程进行interrupt,让子线程有充分时间处理相关业务
 36         Thread.sleep(20);
 37         System.out.println(" subThread2 was interruptted by Main Thread");
 38         System.out.println("===================================================");
 39         Thread.sleep(20);
 40 
 41         System.out.println("==============subThread3 code block result==============");
 42         System.out.println("Main Thread call subThread3 to start");
 43         th3.start();
 44         Thread.sleep(3000);
 45         System.out.println("Main Thread start to interrupt subThread3");
 46         th2.interrupt();
 47         // 等待子线程进行interrupt,让子线程有充分时间处理相关业务
 48         Thread.sleep(20);
 49         System.out.println("subThread3 was interrupted by Main Thread");
 50         System.out.println("===================================================");
 51         Thread.sleep(20);
 52 
 53         System.out.println("Main Thread end");
 54     }
 55 }
 56 
 57 class SubThread1 implements Runnable
 58 {
 59     @Override
 60     public void run()
 61     {
 62         while (!TestInterrupt.isStop)
 63         {
 64             try
 65             {
 66                 // 子线程1进行睡眠
 67                 Thread.sleep(2000);
 68             }
 69             catch (InterruptedException e)
 70             {
 71                 e.printStackTrace();
 72             }
 73 
 74             System.out.println(Thread.currentThread().getName() + " is running...");
 75         }
 76         // 调用stop方法,该语句不会被执行,因为线程整个退出了
 77         System.out.println(Thread.currentThread().getName() + " is ready to cancle");
 78     }
 79 }
 80 
 81 class SubThread2 implements Runnable
 82 {
 83     @Override
 84     public void run()
 85     {
 86         while (!TestInterrupt.isStop)
 87         {
 88             try
 89             {
 90                 // 子线程1进行睡眠
 91                 Thread.sleep(200);
 92             }
 93             catch (InterruptedException e)
 94             {
 95                 e.printStackTrace();
 96             }
 97 
 98             System.out.println(Thread.currentThread().getName() + " is running...");
 99         }
100         // 使用interrupt方法,在发现线程2被阻塞或休眠(sleep)的情况下,会收到一个interrupt的异常。但不会终止线程,仅设置线程是否可以中断的标志位
101         System.out.println(Thread.currentThread().getName() + " is ready to cancle");
102     }
103 }
104 
105 // 调用interrupt方法,对比子线程2,发现使用interrupt方法根本没有中断整个线程,设置后线程也没有进行退出。一直运行
106 class SubThread3 implements Runnable
107 {
108     @Override
109     public void run()
110     {
111         // 使用true代替标志位,判断调用interrupt方法后是否正常中断线程
112         while (true)
113         {
114             try
115             {
116                 Thread.sleep(2000);
117             }
118             catch (InterruptedException e)
119             {
120                 e.printStackTrace();
121             }
122             System.out.println(Thread.currentThread().getName() + " is running...");
123         }
124     }
125 }
stop方法及interrupt方法对比

相关文章: