6.显示锁Lock 和 线程通信Condition
显示锁 Lock
一、用于解决多线程 安全问题的方式:
synchronized: 1.同步代码块 2.同步方法
jdk1.5 后:第三种:同步锁Lock (注意:同步(synchronized)是隐式的锁操作,而Lock对象是一个显示锁,需要通过lock() 方法上锁,必须通过unlock()方法进行释放锁(所以最好放到 finally 中))
在 Java5.0 之前,协调共享对象的访问时可以使用的机制只有 synchronized 和 volatile
在 Java5.0 后增加了一些 新的机制,但并不是一种替代内置锁的方法,而是当内置锁不适用时,作为一种可选择的高级功能
/*ReentrantLock*/ 实现了 Lock 接口,并提供了 与 synchronized 相同的互斥性 和 内存可见性。,但相较于synchronized 提供了更高的处理锁的灵活性(使用Lock 加锁和解锁的时刻都是由我们控制的)
1 public class TestLock { 2 public static void main(String[] args) { 3 TicketThread tt = new TicketThread(); 4 new Thread(tt, "1号窗口").start(); 5 new Thread(tt, "2号窗口").start(); 6 new Thread(tt, "3号窗口").start(); 7 } 8 } 9 10 class TicketThread implements Runnable { 11 private int ticks = 100; 12 // 创建锁 13 private ReentrantLock lock = new ReentrantLock(); 14 15 @Override 16 public void run() { 17 18 while (true) { 19 // synchronized (this) { 20 lock.lock(); // 上锁 21 try { 22 if (ticks > 0) { 23 try { 24 Thread.sleep(200); 25 } catch (InterruptedException e) { 26 // TODO Auto-generated catch block 27 e.printStackTrace(); 28 } 29 System.out.println(Thread.currentThread().getName() 30 + "完成售票,余票为:" + --ticks); 31 } 32 33 } finally { 34 lock.unlock(); //解锁,解锁操作必须有,所以最好就写在 fianlly 中 35 } 36 } 37 } 38 39 }
线程通信Conidtion
Condition 控制线程通信,Condition接口描述了 可能会与锁有关联的条件变量。这些变量在用法上与使用Object.wait 访问的隐式监听器类似,但提供了更强大的功能。
需要特别指出的是,单个Lock 可能 与多个Condition 对象 关联。为了避免兼容性问题,Condition方法的名称与对应的Object版本中的不同
之前使用的 是 wait notify notifyAll 完成线程之间的通信
在 Condition 对象中,与wait、notify和 notifyAll 方法对应的分别是 await、singal 和 signalAll
Condition的强大之处在于它可以为多个线程间建立不同的Condition,一个线程对饮一个Condition
Condition 实例实质上被绑定到一个锁上。(即Condition使用的前提是 使用Lock同步锁)要为特定 Lock 实例 获得 Condition 实例,需使用其newCondition() /* Condition condition = lock.newCondition();*/
举例:线程按序交替
编写一个程序,开启3个线程,这三个线程的ID分别为A、B、C,每个线程将自己的ID在屏幕上打印10遍,要求输出的结果必须按顺序显示,如 : ABCABCABC...... 依次递归
创建一个线程的标志num,用于判断按顺序排到了哪个线程执行,1
创建三个Condition,绑定在一个Lock上,printA被加锁,线程A执行printA方法,先判断 这个线程的标志是否 等于 1(因为A是第一个线程),如果不等于1,说明现在还没排到这个线程执行,则阻塞线程1 Condition1.await,等待唤醒;如果等于1,就打印出A,将num置为2,说明轮到第二个线程执行了,则唤醒线程2Condition2.singal
以此类推,得到 printB 和 printC方法
1 public class TestABCAlternate { 2 public static void main(String[] args) { 3 final AlternateThread at = new AlternateThread(); 4 5 //一般实际中,都是用这种匿名内部类的实现方式 6 new Thread(new Runnable() { 7 @Override 8 public void run() { 9 for (int i = 0; i < 10; i++) { 10 at.printA(); 11 } 12 13 } 14 }, "A").start(); 15 new Thread(new Runnable() { 16 @Override 17 public void run() { 18 for (int i = 0; i < 10; i++) { 19 at.printB(); 20 } 21 } 22 }, "B").start(); 23 new Thread(new Runnable() { 24 @Override 25 public void run() { 26 for (int i = 0; i < 10; i++) { 27 at.printC(); 28 System.out.println("---------------------"); 29 } 30 } 31 }, "C").start(); 32 } 33 } 34 35 class AlternateThread { 36 private int num = 1; // 创建线程的一个标志 37 38 private ReentrantLock lock = new ReentrantLock(); 39 private Condition condition1 = lock.newCondition(); 40 private Condition condition2 = lock.newCondition(); 41 private Condition condition3 = lock.newCondition(); 42 public void printA() { 43 lock.lock(); 44 try { 45 // 1.判断 46 if (num != 1) { 47 condition1.await(); 48 } 49 // 2.打印 50 System.out.println(Thread.currentThread().getName()); 51 52 // 3.唤醒 53 num = 2; 54 condition2.signal(); 55 56 } catch (Exception e) { 57 58 } finally { 59 lock.unlock(); 60 } 61 62 } 63 64 public void printB() { 65 lock.lock(); 66 try { 67 // 1.判断 68 if (num != 2) { 69 condition2.await(); 70 } 71 // 2.打印 72 System.out.println(Thread.currentThread().getName()); 73 74 // 3.唤醒 75 num = 3; 76 condition3.signal(); 77 78 } catch (Exception e) { 79 80 } finally { 81 lock.unlock(); 82 } 83 84 } 85 86 public void printC() { 87 lock.lock(); 88 try { 89 // 1.判断 90 if (num != 3) { 91 condition3.await(); 92 } 93 // 2.打印 94 System.out.println(Thread.currentThread().getName()); 95 96 // 3.唤醒 97 num = 1; 98 condition1.signal(); 99 100 } catch (Exception e) { 101 102 } finally { 103 lock.unlock(); 104 } 105 106 } 107 }