概述
Lock锁的条件变量
设想这样的一种情况,现在有一个盘子,一个线程负责往盘子里放一个苹果,一个线程从盘子取一个苹果,如何保证线程A放一个苹果,线程B就把这个苹果取了,不会出现已经放了好几个了,线程B才一个一个的取,现在限定一个条件,盘子里每次只能放一个苹果,由于两个线程随机执行,不能保证线程A刚放了苹果,线程B就刚好取了。如果用通用的思想的话怎么做呢
应该是加条件判断,线程A每次放的时候,判断盘子里是否有苹果,如果有,则不做处理,线程B执行的时候判断是否有一个苹果,有的话,把这个苹果取了。苹果可以用数字表示,0表示盘子没有苹果,1表示有一个苹果,那么线程A做的事情就是加1,线程B做的就是减1,由于是对同一数据同时操作,必须要用锁保证数据安全
下面是基本的实现
public class ThreadDemo4 {
public static void main(String[] args) {
int[] apple = new int[]{0};
Object obj = new Object();
new Thread(new Runnable() {
@Override
public void run() {
while(true){
synchronized (obj){
if(apple[0] == 0) {
apple[0]++;
System.out.println("线程 "+Thread.currentThread().getId()+ " 放了一个苹果 "+apple[0]);
}else{
System.out.println("线程 "+Thread.currentThread().getId()+ " 放苹果,已经有苹果");
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
while(true){
synchronized (obj){
if(apple[0] == 1) {
apple[0]--;
System.out.println("线程 "+Thread.currentThread().getId()+ " 取了一个苹果 "+apple[0]);
}else{
System.out.println("线程 "+Thread.currentThread().getId()+ " 取苹果,没有苹果...");
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}).start();
}
}
打印结果
从打印结果来看,虽然线程12可以取到苹果,但是并不是都是在线程11刚放入就能取到,从第三行开始,线程12取了好几次都没有取到,这是因为CPU时间片此时被线程12占有,线程11没有执行的时间片,也就不能放苹果了,但是这样的话,会导致资源的消耗,明知没有苹果,那么线程12应该不用再取了,而是等待线程11放了一个苹果后再取,如果线程12发现没苹果了,就不再继续取了,而是等待线程11放苹果,放完之后,你给我发个通知,我再取,由于两个线程都是随机执行的,没法保证按顺序一放一取。怎么才能做到呢?这就是著名的多线程生产者和消费者问题
Java的Object提供了几个方法,用来做线程件的通信
public final native void notify(); 唤醒一个正在等待的线程,如果有多个等待的线程,那么会随机唤醒了一个,这些线程唤醒之后继续尝试获得锁的占有权,进入同步块
public final native void notifyAll(); 唤醒所有等待的线程
public final void wait() throws InterruptedException 释放锁,进入线程等待池,等待被别的线程notify
JDK1.5提供了一个对象锁的条件变量,类似Object的wait,nofity,notifyAll
类Condition的方法
下面是一个常见的面试题
如何用两个线程依次打印出100以内的奇数和偶数,一个线程打印奇数,另一个打印偶数,前面说了如果不用wait和notify的话,没法控制线程的顺序执行和条件执行,很可能一个线程打印了几次,另外一个才打印一次
现在使用Condition条件变量来实现
class Obj {
public int state = 1;
}
class ThreadA implements Runnable {
private int numA = 0;
private Obj obj;
private Lock lock;
private Condition condition;
public ThreadA(Obj obj, Lock lock, Condition condition) {
this.obj = obj;
this.lock = lock;
this.condition = condition;
}
@Override
public void run() {
while (numA < 100) {
lock.lock();
try {
if (obj.state != 1) {
condition.await();
} else {
System.out.println(Thread.currentThread().getName() + " >>> " + numA);
Thread.sleep(100);
numA += 2;
obj.state = 2;
condition.signal();
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
}
class ThreadB implements Runnable {
private int numB = 1;
private Obj obj;
private Lock lock;
private Condition condition;
public ThreadB(Obj obj, Lock lock, Condition condition) {
this.obj = obj;
this.lock = lock;
this.condition = condition;
}
@Override
public void run() {
while (numB < 100) {
lock.lock();
try {
if (obj.state != 2) {
condition.await();
} else {
System.out.println(Thread.currentThread().getName() + " >>> " + numB);
Thread.sleep(100);
numB += 2;
obj.state = 1;
condition.signal();
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
}
public class ThreadDemo2 {
public static void main(String[] args) {
Obj obj = new Obj();
Lock lock = new ReentrantLock();
Condition d1 = lock.newCondition();
Runnable a = new ThreadA(obj, lock, d1);
Runnable b = new ThreadB(obj, lock, d1);
Thread t1 = new Thread(a, "Thread-A");
Thread t2 = new Thread(b, "Thread-B");
t1.start();
t2.start();
}
}
输出结果
这样就实现了顺序打印
总结:如果需要让多个线程按条件顺序执行,就需要使用锁对象的wait
,notify
方法
关于生成者消费者模型,看我的另外一篇博客
最后
以上就是成就洋葱为你收集整理的Java多线程探究-Lock对象锁条件变量的全部内容,希望文章能够帮你解决Java多线程探究-Lock对象锁条件变量所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复