在Java最早期的时候,常常采用suspend()和resume()方法对线程进行阻塞和唤醒,但是现在不再推荐使用了,是因为:
suspend()方法在导致线程阻塞的过程中,不会释放任何锁资源。其他线程都无法访问被它占用的锁。直到对应的线程执行resume()方法后,被挂起的线程才能继续,从而其他被阻塞在这个锁的线程才能继续执行。
如果resume操作出现在suspend操作之前,那么线程就会一直处于阻塞状态–>产生死锁,但是对于被挂起的线程,他的线程仍旧是Runnable状态
class SuspendResumeTest {
public static Object object = new Object();
public static class TestThread extends Thread {
public TestThread(String name) {
super.setName(name);
}
@Override
public void run() {
synchronized (object) {
System.out.println(getName() + "占用。。。");
Thread.currentThread().suspend();
}
}
}
static TestThread t1 = new TestThread("我是线程1");
static TestThread t2 = new TestThread("我是线程2");
public static void main(String[] args) throws InterruptedException {
t1.start();
//Thread.sleep(200);
t2.start();
t1.resume();
t2.resume();
t1.join();
t2.join();
}
}
在Object类中,定义了wait(),notify()和notifyAll()等接口。wait方法的作用是让当前线程进入等待状态,同时wait也会让当前线程释放它所持有的锁。而notify()和notifyAll()的作用则是唤醒当前对象上的等待线程;notify()是唤醒单个线程,而notifyAll()是唤醒所有线程。
public class WaitTest {
public static void main(String[] args) {
ThreadA t1=new ThreadA("t1");
synchronized (t1){
try {
//启动线程1
System.out.println(Thread.currentThread().getName()+"start t1");
t1.start();
//主线程等待线程1通过notify唤醒
System.out.println(Thread.currentThread().getName()+"wait()");
t1.wait();
System.out.println(Thread.currentThread().getName()+"continue");
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
}
class ThreadA extends Thread{
public ThreadA(String name) {
super(name);
}
@Override
public void run() {
synchronized (this){
System.out.println(Thread.currentThread().getName()+"call notify()");
//唤醒当前的wait线程
notify();
}
}
}
####扩展一下wait()–>wait(long timeout)
class ThreadA extends Thread{
public ThreadA(String name) {
super(name);
}
public void run() {
System.out.println(Thread.currentThread().getName() + " run ");
// 死循环,不断运行。
while(true)
;
}
}
public class WaitTimeoutTest {
public static void main(String[] args) {
ThreadA t1 = new ThreadA("t1");
synchronized(t1) {
try {
// 启动“线程t1”
System.out.println(Thread.currentThread().getName() + " start t1");
t1.start();
// 主线程等待t1通过notify()唤醒 或 notifyAll()唤醒,或超过3000ms延时;然后才被唤醒。
System.out.println(Thread.currentThread().getName() + " call wait ");
t1.wait(3000);
System.out.println(Thread.currentThread().getName() + " continue");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
main start t1
main call wait
t1 run // 大约3秒之后...输出“main continue”
main continue
和上面的wait方法类似,只不过主线程不会立刻被唤醒,而是等待3000ms后唤醒
public class NotifyAllTest {
private static Object obj = new Object();
public static void main(String[] args) {
ThreadA t1 = new ThreadA("t1");
ThreadA t2 = new ThreadA("t2");
ThreadA t3 = new ThreadA("t3");
t1.start();
t2.start();
t3.start();
try {
System.out.println(Thread.currentThread().getName()+" sleep(3000)");
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized(obj) {
// 主线程等待唤醒。
System.out.println(Thread.currentThread().getName()+" notifyAll()");
obj.notifyAll();
}
}
static class ThreadA extends Thread{
public ThreadA(String name){
super(name);
}
public void run() {
synchronized (obj) {
try {
// 打印输出结果
System.out.println(Thread.currentThread().getName() + " wait");
// 唤醒当前的wait线程
obj.wait();
// 打印输出结果
System.out.println(Thread.currentThread().getName() + " continue");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
t1 wait
main sleep(3000)
t3 wait
t2 wait
main notifyAll()
t2 continue
t3 continue
t1 continue
Object中的wait(), notify()等函数,和synchronized一样,会对“对象的同步锁”进行操作。
wait()会使“当前线程”等待,因为线程进入等待状态,所以线程应该释放它锁持有的“同步锁”,否则其它线程获取不到该“同步锁”而无法运行!
这两个方法是基于suspend和resume这一组合的基础上演变过来的,提供避免死锁和竞态条件
public class ThreadParkTest {
public static void main(String[] args) {
MyThread mt = new MyThread();
mt.setName("mt");
mt.start();
try {
Thread.currentThread().sleep(10);
mt.park();
Thread.currentThread().sleep(30000);
mt.unPark();
Thread.currentThread().sleep(30000);
mt.park();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
static class MyThread extends Thread {
private boolean isPark = false;
public void run() {
System.out.println(" Enter Thread running.....");
while (true) {
if (isPark) {
System.out.println("Thread is Park.....");
LockSupport.park();
}
}
}
public void park() {
isPark = true;
}
public void unPark() {
isPark = false;
LockSupport.unpark(this);
System.out.println("Thread is unpark.....");
}
}
}
park和unpark方法控制的粒度更细,能够准确的决定线程在某个点停止
引入了许可机制,逻辑为:
1、park讲许可在等于0的时候阻塞,等于1的时候返回并将许可减为0
2、unpark尝试唤醒线程,许可加1。根据这两个逻辑,对于同一条线程,park与unpark先后操作的顺序似乎并不影响程序正确地执行,假如先执行unpark操作,许可则为1,之后再执行park操作,此时因为许可等于1直接返回往下执行,并不执行阻塞操作。
总结总结总结
suspend()、resume()已经被deprecated,不建议使用。wait与notify要保证必须有锁才能执行,而且执行notify操作释放锁后还要将当前线程扔进该对象锁的等待队列。LockSupport则完全不用考虑对象、锁、等待队列,真正解耦合线程之间的同步。