wait、notify、notifyAll都要求当前线程拥有该object的monitor锁
//创建俩线程,一个wait之后,另一个notify唤醒
public class Wait {
public final static Object object = new Object();
static class Thread1 extends Thread{
@Override
public void run(){
synchronized (object){
System.out.println("线程"+Thread.currentThread().getName()+"开始执行了");
try {
System.out.println("线程"+Thread.currentThread().getName()+"进入wait");
object.wait(); //wait执行后,就会释放synchronization锁
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程"+Thread.currentThread().getName()+"重新获取了锁");
}
}
}
static class Thread2 extends Thread{
@Override
public void run(){
synchronized (object){
object.notify(); //Thread2获取到synchronization锁后,唤醒wait
System.out.println("线程"+Thread.currentThread().getName()+"调用了notify()");
}
}
}
public static void main(String[] args) {
Thread1 thread1 = new Thread1();
Thread2 thread2 = new Thread2();
thread1.start();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
thread2.start();
}
}
//创建三个线程,俩wait,另一个用notifyAll唤醒
public class WaitNotifyAll implements Runnable{
private static final Object resourceA = new Object();
public static void main(String[] args) {
Runnable runnable = new WaitNotifyAll();
Thread threadA = new Thread(runnable);
Thread threadB = new Thread(runnable);
Thread threadC = new Thread(new Runnable() {
@Override
public void run() {
synchronized (resourceA){
resourceA.notifyAll(); //唤醒所有线程
System.out.println("ThreadC notified.");
}
}
});
threadA.start();
threadB.start();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
threadC.start();
}
@Override
public void run() {
synchronized (resourceA){
System.out.println(Thread.currentThread().getName()+" get resourceA lock.");
try {
System.out.println(Thread.currentThread().getName()+" wait to start.");
resourceA.wait();
System.out.println(Thread.currentThread().getName()+" is going to end.");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
若这里使用notify代替notifyAll唤醒线程,则最后的结果是随即唤醒一个线程。
public class WaitNotifyPrintOddEvenSyn {
private static int count;
private static final Object lock=new Object();
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
while(count<100){
//随机的竞争锁,如果拿到锁,就执行if语句
synchronized (lock){
if ((count & 1)==0){ //二进制相与(num & 1)即(num & 000···0001)
System.out.println(Thread.currentThread().getName()+" count:"+count);
count++;
}else {
System.out.println("偶数,资源浪费");
}
}
///
}
}
},"偶数").start();
new Thread(new Runnable() {
@Override
public void run() {
while(count<100){
//随机的竞争锁,如果拿到锁,就执行if语句
synchronized (lock){
if ((count & 1)==1){
System.out.println(Thread.currentThread().getName()+" count:"+count);
count++;
}else {
System.out.println("奇数,资源浪费");
}
}
}
}
},"奇数").start();
}
}
由于两个线程在竞争锁的时候是随机的,因此,会造成大量的浪费。
结果:
public class WaitNotifyPrintOddEvenWait {
//拿到锁就打印
//打印完,唤醒其他线程,自己休眠
private static int count = 0;
private static final Object lock = new Object();
public static void main(String[] args) {
new Thread(new TurningRunner(),"偶数").start();
//中间加10ms休眠时间,确保偶数线程先执行
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(new TurningRunner(),"奇数").start();
}
static class TurningRunner implements Runnable{
@Override
public void run() {
while(count<=100){
synchronized (lock){
System.out.println(Thread.currentThread().getName()+" : "+count);
count++;
lock.notify(); //先去唤醒其他线程
if (count<=100){
try {
lock.wait(); //自己进入休眠,释放lock锁
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
}
}
执行结果:
public class WaitNotifyReleaseOwnMonitor {
private static volatile Object resourceA = new Object();
private static volatile Object resourceB = new Object();
public static void main(String[] args) {
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
synchronized (resourceA) {
System.out.println("ThreadA 获取resourceA锁");
synchronized (resourceB) {
System.out.println("ThreadA 获取resourceB锁");
try {
System.out.println("ThreadA 释放 resourceA 锁");
resourceA.wait(); //线程A释放A锁
System.out.println("已释放"); //此处不会执行,因为一旦A锁释放,线程A就进入WAITING状态
// resourceB.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
});
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (resourceA){ //ThreadB获取A锁
System.out.println("ThreadB 获取 resourceA 锁");
synchronized (resourceB){ //
System.out.println("ThreadB 获取 resourceB 锁");
}
}
}
});
thread1.start();
thread2.start();
}
}
sleep方法不释放锁,等sleep时间到了以后,正常结束才会释放锁,而wait方法释放锁
public class SleepDontReleaseMonitor implements Runnable{
public static void main(String[] args) {
SleepDontReleaseMonitor sleepDontReleaseMonitor = new SleepDontReleaseMonitor();
new Thread(sleepDontReleaseMonitor).start();
new Thread(sleepDontReleaseMonitor).start();
}
@Override
public void run() {
syn();
}
private synchronized void syn(){
System.out.println("线程"+Thread.currentThread().getName()+"获取到了monitor");
try {
Thread.sleep(5000); //sleep中不会释放锁
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程"+Thread.currentThread().getName()+"退出了同步代码块");
}
}
public class SleepDontReleaseLock implements Runnable{
private static final Lock lock= new ReentrantLock();
@Override
public void run() {
lock.lock();
System.out.println("线程" + Thread.currentThread().getName()+"获取到锁");
try {
Thread.sleep(5000);
System.out.println("线程" + Thread.currentThread().getName()+"已经苏醒");
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
public static void main(String[] args) {
SleepDontReleaseMonitor sleepDontReleaseMonitor = new SleepDontReleaseMonitor();
new Thread(sleepDontReleaseMonitor).start();
new Thread(sleepDontReleaseMonitor).start();
}
}
//每一秒打印一次时间,休眠三秒后,在休眠中进行中断
public class SleepInterrupted implements Runnable{
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(new SleepInterrupted());
thread.start();
Thread.sleep(3500);
thread.interrupt();
}
@Override
public void run() {
for (int i=0;i<10;i++){
System.out.println(new Date());
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
System.out.println("我被中断了!");
e.printStackTrace();
}
}
}
}
结果:
sleep方法可以让线程进入Timed_Waiting状态,并且不占用CPU资源,但是不释放锁,直到规定时间后再执行,休眠期间如果被中断,会抛出异常并清除中断状态。
作用:因为新线程的加入,所以要等新线程执行完再继续
用法:通常是主线程等待子线程执行完
public class Join {
public static void main(String[] args) throws InterruptedException {
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"执行完毕");
}
});
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"执行完毕");
}
});
thread1.start();
thread2.start();
System.out.println("开始等待子线程执行完毕");
thread1.join(); //子线程加入,主线程等子线程执行完后,才继续执行
thread2.join();
System.out.println("所有子线程执行完毕"); //因此,这句话要等子线程执行完
}
}
//子线程触发主线程中断,看主线程和子线程的执行顺序
public class JoinInterrupt {
public static void main(String[] args) {
Thread mainThread = Thread.currentThread();
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
try {
mainThread.interrupt(); //子线程执行过程中将主线程中断
Thread.sleep(5000);
System.out.println("Thread1 执行完毕");
} catch (InterruptedException e) {
System.out.println("子线程被中断");
e.printStackTrace();
}
}
});
thread1.start();
System.out.println("等待子线程运行完毕"); //主线程等子线程执行
try {
thread1.join();
} catch (InterruptedException e) {
System.out.println(Thread.currentThread().getName()+"被中断"); //捕获到主线程被中断=》导致join失效,主线程直接执行最后打印语句
thread1.interrupt(); //将中断传递给子线程,保证数据一致性
e.printStackTrace();
}
System.out.println("子线程执行完毕!");
}
}
由于join,主线程等待子线程执行,子线程触发主线程的中断,但是主线程的中断并不影响子线程,这会导致数据的不一致性,因此,在主线程中断时,要加上thread1.interrupt(),将中断传递给子线程。
结果:
主线程执行到join后,自动获取子线程的锁,等待子线程,在子线程run方法结束后,jvm自动唤醒主线程。
thread.join(); <=> synchronized (thread){ thread.wait(); }
在子线程join后,主线程进入WAITING状态,等待子线程执行完毕。
作用:释放CPU时间片,线程状态是Runnable。yield()释放时间片,并不会释放掉锁,也不会陷入阻塞,下一次CPU调度依然可能将其调度。
定位:JVM不保证遵循(CPU资源不紧张,这时调用yield(),JVM也不会讲CPU资源释放掉)(具体的实现,不同的JVM实现不同,没办法保证代码的执行效果,为了保证程序的稳定性,一般开发中不使用yield)