Object调用的wait和notify Condition调用的await和single对线程加锁及等待唤醒顺序都有要求,违反规则将发生线程异常情况;
LockSupport调用的park和unpark对线程没有加锁需求,且可以先发生唤起再执行等待(先unpark再park),底层用的permit(0,1) least 0,most 1;严格要求唤起与等待成对出现
Object和Condition对等待唤醒的约束:Ⅰ线程要先获取并持有锁,即代码必须在同步代码块中 Ⅱ必须先等待后唤醒,不然阻塞
wait和notify(Object方法)
wait synchronized notify
/**
* wait和notify方法必须再同步代码块中执行,且需要成对有序出现(先wait再notify)
*/
public class Test {
static Object objectLock = new Object();
public static void main(String[] args)
{
new Thread(() -> {
//暂停几秒钟线程
try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); }
synchronized (objectLock){
System.out.println(Thread.currentThread().getName()+"\t"+"---come in");
try {
objectLock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"\t"+"---被唤醒");
}
},"t1").start();
//暂停几秒钟线程
try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }
new Thread(() -> {
synchronized (objectLock){
objectLock.notify();
System.out.println(Thread.currentThread().getName()+"\t"+"---发出通知");
}
},"t2").start();
}
}
-
如果没有在同步代码(sync锁)块中执行
-
如果先运行notify再运行wait将会发生阻塞
await和signal(Condition方法)
await lock/lock signal
/**
*
*Condtion中线程等待和唤醒方法之前需要获取锁 await和signal要有先后,不然发生阻塞
*/
public class Test2 {
static Lock lock = new ReentrantLock();
static Condition condition = lock.newCondition();
public static void main(String []args)
{
new Thread(() -> {
//暂停几秒钟线程
try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); }
lock.lock();
try
{
System.out.println(Thread.currentThread().getName()+"\t"+"---come in");
condition.await();
System.out.println(Thread.currentThread().getName()+"\t"+"---被唤醒");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
},"t1").start();
new Thread(() -> {
lock.lock();
try
{
condition.signal();
System.out.println(Thread.currentThread().getName()+"\t"+"---发出通知");
}finally {
lock.unlock();
}
},"t2").start();
}
}
-
await前没加 lock.lock()
-
如果先执行signal将发生阻塞
park与unpark(LockSupport)
/**
* 无锁块要求 先唤醒还是先等待不影响执行结果 park与unpark要成双成对
* 通信证只用一次 permit (0, 1) 默认是0 累加上限是1
*/
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
System.out.println(Thread.currentThread().getName() + "\t" + "---come in");
LockSupport.park();
LockSupport.park();
System.out.println(Thread.currentThread().getName() + "\t" + "---被唤醒");
}, "t1");
t1.start();
new Thread(() -> {
LockSupport.unpark(t1);
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
LockSupport.unpark(t1);
System.out.println(Thread.currentThread().getName() + "\t" + "---发出通知");
}, "t2").start();
}
正确使用LockSupport提供的通行证可最大程度避免线程阻塞 每个线程只能使用一个通行证,permit值域(0,1),因此park与unpark要成对出现