文档章节

j.u.c.locks.condition

黑妹妹牙膏
 黑妹妹牙膏
发布于 2014/08/18 18:09
字数 1614
阅读 284
收藏 4

分享JUC中的Condition的比较少见,我见了大部分文章都是讲其中的一个例子BoundedBuffer。今天先从Condition接口的几个方法说起,然后在把BoundedBuffer搞死锁了。来看看Condition在使用的时候需要注意什么。

源代码

上源代码:(字数限时,原谅我把注释都去掉了)

public interface Condition {
    
    void await() throws InterruptedException;
    
    void awaitUninterruptibly();
    
    long awaitNanos(long nanosTimeout) throws InterruptedException;
    
    boolean await(long time, TimeUnit unit) throws InterruptedException;
    
    boolean awaitUntil(Date deadline) throws InterruptedException;
    
    void signal();
    
    void signalAll();
}

Condtion接口主要是用来描述一个锁的几个状态。具体创建方法如下

Condition notFull = lock.newCondition();

其实Condition的功能有点类似Object当中的wait和notify方法。但是稍做了加强。提供了多种wait的策略。另外用Condition的最大好处就是,一个锁是可以拥有多个状态的。如果用Object的wait和notify只能有一个。

具体看一下以下几个wait方法:

    void await() throws InterruptedException;
    
    void awaitUninterruptibly();
    
    long awaitNanos(long nanosTimeout) throws InterruptedException;
    
    boolean await(long time, TimeUnit unit) throws InterruptedException;
    
    boolean awaitUntil(Date deadline) throws InterruptedException;

await()

解读:会将当前线程设置成等待状态,一直到有signal方法的触发或者Interrupt的发生才会恢复状态。当然interrupt了就直接抛异常了,不会继续往下走

异常:InterruptedException

awaitUninterruptibly()

解读:会将当前线程设置成等待状态,一直到有signal方法的触发。不会被Interrupt阻断。

long awaitNanos(long nanosTimeout)

参数:最大的等待的时间

返回:实际的剩余时间的估算(nanosTimeout - 实际等待时间)正值可以用于后续的继续等待,例如延迟加载这样的场景,可以让程序继续等待剩下的时间,完成计时。如果为0或者负数时,表示没有剩余时间了。

解读:会将当前线程设置成等待状态一直到设定的最大等待时间。当遇到singal或者Interrupt时才会恢复。

异常:InterruptedException

boolean await(long time, TimeUnit unit) 和 boolean awaitUntil(Date deadline)

参数:具体时间一个是间断的时间,另一个是具体的时刻。但实质是一样的。

解读:具体等待一段时间或一个到一个时间点。如果遇到singal则返回True,否则返回false.

异常:InterruptedException

    void signal();
    
    void signalAll();

void signal()

解读:唤醒一个等待的线程,如果所有的线程都在等待此条件,则选择其中的一个唤醒。在从 await 返回之前,该线程必须重新获取锁。

void signalAll()

解读:唤醒所有线程,如果所有的线程都在等待此条件,则选择其中的一个唤醒。在从 await 返回之前,该线程必须重新获取锁。


看一个例子

public class BoundedBuffer {
	final Lock lock = new ReentrantLock();
	final Condition notFull = lock.newCondition();
	final Condition notEmpty = lock.newCondition();
	
	final Object[] items = new Object[2];
	int putptr, takeptr, count;
	
	public void put(Object x) throws InterruptedException {
		String threadName = Thread.currentThread().getName();
		System.out.println(threadName+" is waiting for lock");
		lock.lock();
		System.out.println(threadName+" got lock");
		try {
			while (count == items.length){
				System.out.println(threadName+" is waiting for notFull condition");
				notFull.await();
				System.out.println(threadName+" left notFull condition");
			}
			items[putptr] = x;
			if (++putptr == items.length){
				putptr = 0;
			}
			++count;
			notEmpty.signal();
		} finally {
			lock.unlock();
		}
	}
	
	public Object take() throws InterruptedException {
		String threadName = Thread.currentThread().getName();
		System.out.println(threadName+" is waiting for lock");
		lock.lock();
		System.out.println(threadName+" got lock");
		try {
			while (count == 0){
				System.out.println(threadName+" is waiting for notEmpty condition");
				notEmpty.await();
				System.out.println(threadName+" left notEmpty condition");
			}
			Object x = items[takeptr];
			if (++takeptr == items.length){
				takeptr = 0;
			}
			--count;
			notFull.signal();
			return x;
		} finally {
			lock.unlock();
		}
	}
}

问得最多的一个问题:这里的await有必要吗?

while (count == 0){
    System.out.println(threadName+" is waiting for notEmpty condition");
    notEmpty.await();
    System.out.println(threadName+" left notEmpty condition");
}

回答:有必要,当count等于0的时候.如果没有await释放锁,那么其他线程会认为lock一直被占有,将无法获得锁。


为了验证这一点,写了一个测试方法:

public class PutTask implements Runnable {
	BoundedBuffer bb = new BoundedBuffer();
	int count = 0;
	
	public PutTask(BoundedBuffer bb){
		this.bb = bb;
		this.count = 0;
	}
	
	@Override
	public void run() {
		try {
			String threadName = Thread.currentThread().getName();
			bb.put(count);
			System.out.println(threadName+" put "+count);
			count++;
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
}

public class TakeTask implements Runnable {
	BoundedBuffer bb = new BoundedBuffer();
	
	public TakeTask(BoundedBuffer bb){
		this.bb = bb;
	}
	
	@Override
	public void run() {
		try {
			System.out.println(Thread.currentThread().getName()+" take "+bb.take());
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
}

public class ConditionTest {
	
	public static void main(String[] args) throws InterruptedException{
		BoundedBuffer bb = new BoundedBuffer();
		
		PutTask putTask = new PutTask(bb);
		TakeTask takeTask1 = new TakeTask(bb);
		TakeTask takeTask2 = new TakeTask(bb);
		
		final ScheduledExecutorService service = Executors.newScheduledThreadPool(3);
		//service.scheduleAtFixedRate(putTask, 0, 1, TimeUnit.SECONDS);
		service.scheduleAtFixedRate(takeTask1, 0, 1, TimeUnit.SECONDS);
		service.scheduleAtFixedRate(takeTask2, 0, 1, TimeUnit.SECONDS);
		Thread.currentThread().sleep(3000l);
		Thread t = new Thread(putTask);
		t.start();
	}
}

将notEmpty.await()注释掉,执行的结果为

pool-1-thread-2 is waiting for lock
pool-1-thread-2 got lock
pool-1-thread-1 is waiting for lock
Thread-0 is waiting for lock

可以看到除了pool-1-thread-2获得了锁,其他线程都在等待锁。但这个时候pool-1-thread-2被while(count==0)锁死,无法跳出。程序进入死锁。


恢复notEmpty.await()的注释,执行的结果为

pool-1-thread-2 is waiting for lock
pool-1-thread-2 got lock
pool-1-thread-2 is waiting for notEmpty condition
pool-1-thread-1 is waiting for lock
pool-1-thread-1 got lock
pool-1-thread-1 is waiting for notEmpty condition
Thread-0 is waiting for lock
Thread-0 got lock
Thread-0 put 0
pool-1-thread-2 left notEmpty condition
pool-1-thread-2 take 0
pool-1-thread-2 is waiting for lock
pool-1-thread-2 got lock
pool-1-thread-2 is waiting for notEmpty condition



问题:

while (count == 0){
    System.out.println(threadName+" is waiting for notEmpty condition");
    notEmpty.await();
    System.out.println(threadName+" left notEmpty condition");
}

如果将while改成if可以吗?

回答:不可以,

notEmpty.signal();

如果将notEmpty.signal()改成notEmpty.signalAll()。然后按照上面的方法再来试一次,先放两个线程去take,然后一个线程去put,然后同时唤醒两个线程。数组会越界。必须重新等待一次notEmpty.


最后一个问题

public class ConditionTest {
	
	public static void main(String[] args) throws InterruptedException{
		BoundedBuffer bb = new BoundedBuffer();
		
		PutTask putTask = new PutTask(bb);
		TakeTask takeTask1 = new TakeTask(bb);
		TakeTask takeTask2 = new TakeTask(bb);
		
		final ScheduledExecutorService service = Executors.newScheduledThreadPool(2);
		service.scheduleAtFixedRate(putTask, 0, 1, TimeUnit.SECONDS);
		service.scheduleAtFixedRate(takeTask1, 0, 1, TimeUnit.SECONDS);
		service.scheduleAtFixedRate(takeTask2, 0, 1, TimeUnit.SECONDS);
	}
}

如果这样执行,结果会怎么样?在两个线程中跑3个任务。

结果就是死锁了:

pool-1-thread-1 is waiting for lock
pool-1-thread-1 got lock
pool-1-thread-1 put 0
pool-1-thread-1 is waiting for lock
pool-1-thread-1 got lock
pool-1-thread-1 take 0
pool-1-thread-1 is waiting for lock
pool-1-thread-1 got lock
pool-1-thread-1 is waiting for notEmpty condition
pool-1-thread-2 is waiting for lock
pool-1-thread-2 got lock
pool-1-thread-2 put 1
pool-1-thread-1 left notEmpty condition
pool-1-thread-1 take 1
pool-1-thread-1 is waiting for lock
pool-1-thread-1 got lock
pool-1-thread-1 is waiting for notEmpty condition
pool-1-thread-2 is waiting for lock
pool-1-thread-2 got lock
pool-1-thread-2 is waiting for notEmpty condition



© 著作权归作者所有

黑妹妹牙膏
粉丝 23
博文 35
码字总数 23081
作品 0
杭州
程序员
私信 提问

暂无文章

计算机网络

计算机网络体系结构 OSI 其中表示层和会话层用途如下: 表示层 :数据压缩、加密以及数据描述,这使得应用程序不必关心在各台主机中数据内部格式不同的问题。 会话层 :建立及管理会话。 五层...

一只小青蛙
今天
2
0
0.01-Win10安装linux子系统

一、安装Debian子系统 -1、控制面板设置: -1.1、打开“控制面板” —— “程序” —— “启用或关闭Windows功能” —— 勾选 “适用于Linux的Windows子系统” -2、设置: -2.1、打开“设置”...

静以修身2025
昨天
2
0
init 0-6 (启动级别:init 0,1,2,3,4,5,6)

启动级别: init 0,1,2,3,4,5,6 这是个很久的知识点了,只是自己一直都迷迷糊糊的,今天在翻出来好好理解下。。 0: 停机 1:单用户形式,只root进行维护 2:多用户,不能使用net file system...

圣洁之子
昨天
2
0
Android Camera HAL浅析

1、Camera成像原理介绍 Camera工作流程图 Camera的成像原理可以简单概括如下: 景物(SCENE)通过镜头(LENS)生成的光学图像投射到图像传感器(Sensor)表面上,然后转为电信号,经过A/D(模数转...

天王盖地虎626
昨天
2
0
聊聊Elasticsearch的ProcessProbe

序 本文主要研究一下Elasticsearch的ProcessProbe ProcessProbe elasticsearch-7.0.1/server/src/main/java/org/elasticsearch/monitor/process/ProcessProbe.java public class ProcessProb......

go4it
昨天
3
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部