Java深入学习12:线程按目标顺序执行以及Lock锁和Condiiton接口
一、一个多线程问题,有三类线程,分别是A、B、C,如如实现ABCABCABCABCABC,顺次执行。
方案1。代码如下
import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class ThreadSequenceOutputTest { public static void main(String[] args) { ThreadDemo threadDemo = new ThreadDemo(); for(int i=1; i<5; i++){ //A类线程 new Thread(new Runnable() { @Override public void run() { threadDemo.outPutA(); } },"A").start(); //C类线程 new Thread(new Runnable() { @Override public void run() { threadDemo.outPutB(); } },"B").start(); //C类线程 new Thread(new Runnable() { @Override public void run() { threadDemo.outPutC(); } },"C").start(); } } } class ThreadDemo{ private int num = 1;//当前线程标记,123分别对应ABC private Lock lock = new ReentrantLock(); private Condition condition1 = lock.newCondition();//condition1控制A类线程 private Condition condition2 = lock.newCondition();//condition2控制B类线程 private Condition condition3 = lock.newCondition();//condition3控制C类线程 //输出A public void outPutA(){ lock.lock(); try { //如果没有轮到A类线程,则继续等待 if(num != 1){ condition1.await(); } //打印线程名称 System.out.println(Thread.currentThread().getName()); //num标志切换成B线程 num = 2; //唤醒B类线程 condition2.signal(); } catch (InterruptedException e){ e.printStackTrace(); } finally { lock.unlock(); } } //输出B public void outPutB(){ lock.lock(); try { if(num != 2){ condition2.await(); } System.out.println(Thread.currentThread().getName()); num = 3; condition3.signal(); } catch (InterruptedException e){ e.printStackTrace(); } finally { lock.unlock(); } } //输出C public void outPutC(){ lock.lock(); try { if(num != 3){ condition3.await(); } System.out.println(Thread.currentThread().getName()); num = 1; condition1.signal(); } catch (InterruptedException e){ e.printStackTrace(); } finally { lock.unlock(); } } }
二、Condiiton接口
1-Condition
Condition是在java 1.5中才出现的,它用来替代传统的Object的wait()、notify()实现线程间的协作,相比使用Object的wait()、notify(),使用Condition的await()、signal()这种方式实现线程间协作更加安全和高效。因此通常来说比较推荐使用Condition,阻塞队列实际上是使用了Condition来模拟线程间协作。
Condition是个接口,基本的方法就是await()和signal()方法;
Condition依赖于Lock接口,生成一个Condition的基本代码是lock.newCondition()
调用Condition的await()和signal()方法,都必须在lock保护之内,就是说必须在lock.lock()和lock.unlock之间才可以使用
Conditon中的await()对应Object的wait();
Condition中的signal()对应Object的notify();
Condition中的signalAll()对应Object的notifyAll()。
2-在Java Condition接口的注释中有一个案例如下
import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /*这是一个简单的生产者消费者模型,生产者往buffer里put,消费者从buffer里take。 1-同一状态的顺序访问: 有三个状态需要顺序访问:buffer的大小count,生产者用于put的游标putptr,消费者用于take的游标takeptr。 2-基于该状态的条件等待: 当count = 0时,消费者的take需要等待;当count = buffer.size(buffer满了),生产者需要等待。 */ public class BoundedBuffer { public static void main(String[] args) { BoundedBuffer boundedBuffer = new BoundedBuffer(); for(int i=1; i<100; i++){ //生产者 new Thread(new Runnable() { @Override public void run() { try { boundedBuffer.put((int)(Math.random()*100)); } catch (InterruptedException e) { e.printStackTrace(); } } }).start(); //消费者 new Thread(new Runnable() { @Override public void run() { try { boundedBuffer.take(); } catch (InterruptedException e) { e.printStackTrace(); } } }).start(); } } final Lock lock = new ReentrantLock(); final Condition notFull = lock.newCondition(); final Condition notEmpty = lock.newCondition(); final Object[] items = new Object[100]; int putptr, takeptr, count; //生产 public void put(Object x) throws InterruptedException { System.out.println("count " + count); //上锁 lock.lock(); try { //如果count已满,设置“未满”进程等待 while (count == items.length){ notFull.await(); } //赋值 items[putptr] = x; System.out.println("put " + x); //判断put的游标到了最后一位,则移到首位 if (++putptr == items.length){ putptr = 0; } //计数count+1 ++count; //唤起“非空”继承 notEmpty.signal(); } finally { //释放锁 lock.unlock(); } } //消费 public Object take() throws InterruptedException { //上锁 lock.lock(); try { //当缓存为空是,设置“非空”线程等待 while (count == 0){ notEmpty.await(); } //获取当前take游标的value Object x = items[takeptr]; System.out.println("take " + x); //判断take的游标如果到了数组末端,则更新为首位 if (++takeptr == items.length) { takeptr = 0; } //计数-1 --count; //唤醒“未满”线程 notFull.signal(); return x; } finally { //释放锁 lock.unlock(); } } }