明白这几个概念,首先理解下“锁池”和“等待池”的概念,其中等待池主要针对的是wait方法。
一、锁池和等待池的概念
锁池:
所有需要竞争同步锁的线程都会放在锁池中,比如同步锁被其中一个线程所获得,其他想要竞争这个锁的线程需要在这个锁池中进行等待,前获取锁的线程释放锁的时候,锁池中的线程再去竞争锁。当某个线程获取到锁后就进入就绪队列,等待获取cpu资源分配。
等待池:
当执行wait方法后,该线程会被放入等待池中,等待池中的线程是不会去竞争锁的,只有调用了notify或者是notifyAll方法后等待池中的线程才会去竞争锁。
notify是随机从等待池中选取一个线程进入锁池中,而notifyAll则是将等待池所有线程放入到锁池中。
二、sleep和 wait方法的区别
1,sleep是Thread类的静态本地方法,wait是object的方法
2,sleep方法不会释放锁,而wait方法会释放锁并将该线程放入等待队列中只有唤醒后才能够再次竞争锁
更加详细的说:sleep是将cpu的执行资格和执行权释放出去,等定点时间再获取cpu资源,参与cpu的调度,获取cpu的资源后就可以继续运行了,但其所是不会释放的。如果再sleep期间,其他线程获取不到锁的,也就是无法执行程序,当有其他线程代用interrupt方法时,就会报interruptexception异常返回。
3,sleep方法不依赖于同步器synchronized,但是wait就依赖于synchronized关键字。
也就是不管有没有synchronized都可以写sleep方法,但是wait方法就不是。
4,sleep不用唤醒,wait方法需要唤醒(指wait不带参数的,现在也带了指定时间的参数就另说)
5,sleep一般用于当前线程休眠或者轮询暂停操作。wait多用于线程间的通信
6,sleep会让cpu时间执行且强制执行上下文切换,wait则不一定让出cpu资源的,它是可能还会有机会重新竞争到锁的。
三、yield和join
1,让出cpu执行权的yield方法
首先需要知道,操作系统是为每个线程分配一个时间片来占有cpu的,正常情况下当一个线程把分配给自己的时间片使用完后,线程调度才会进行下一轮的调度。
当一个线程调用yield方法,相当于就是告诉调度器自己还有剩余的时间片没有使用完的部分自己不想使用了,暗示线程调度器进行下一轮的线程调度,然后该线程就会处于就绪状态,线程调度器就会从就绪队列中获取一个优先级别最高的线程,当然也有可能调度到刚刚让出cpu的那个线程来获取cpu执行权。
理解:一般很少用这个方法,在调试或者测试时这个方法或许可以帮助复现由于竞争条件导致的问题。
1.1 yield与sleep的不同
sleep方法线程会被阻塞挂起指定时间,在这期间线程调度器不会去调度这个线程。而yield方法就不一样,线程调度器就会进行下一次调度。
2,等待线程执行终止的join方法
在项目实践中会遇到一个场景:就是需要等待某几件事执行完之后再继续向下执行,比如多个线程加载资源,需要多个线程全部加在完毕后再汇总处理。
针对这样的场景,Thread类中的join方法就可以处理,它是一个无参且返回值为void的方法。当然还面要讲解到的CountDownLatch也是个不错的选择,这个具体的下期会进行介绍,包括CycleBarrier和Semaphore。
如果A线程调用了B线程的join方法,A线程会阻塞,直到B线程执行完毕或中止。如下方法所示,在main线程中调了t1线程的join方法,则main线程就会阻塞,打印的结果也一定是先打印t1线程里的再打印main线程里的,无论t1线程休眠多长时间。
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("1111");
}
});
t1.start();
t1.join();
System.out.println("2222");
}
其输出的结果就是:1111 然后才是2222
补充:线程调join方法时,线程A调用线程B的interrupt方法会报interruptexception异常
下面代码展示,主线程里面启动了两个子线程执行完毕后进行汇总的代码:
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("1111");
}
});
Thread t2=new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("2222");
}
});
t1.start();
t2.start();
System.out.println("wait all child thread over");
t1.join();
t2.join();
System.out.println("all child thread over");
}
执行结果如下: