Java线程的阻塞和唤醒

94 阅读2分钟

1. 使用Object类的方法wait()和notify()实现


public class ObjectWait {

    public static void main(String[] args) {
        Object o = new Object();

        Thread t = new Thread(() -> {

            System.out.println("线程A被o.wait()阻塞前");
            synchronized(o){
                try {
                    o.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("线程A被线程B o.notify()唤醒");

        },"A");

        t.start();


        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        new Thread(() -> {
            System.out.println("线程B唤醒线程A");
            synchronized (o){
                o.notify();
            }
        },"B").start();
    }
}
  1. wait和notify需要在同步代码块synchronized中才能使用,获得锁之后才会执行。
  2. 线程一获得锁,执行到wait方法,释放锁,线程阻塞。线程二随后获得锁,notify方法执行,线程一被唤醒,等待线程二释放锁,线程一获得锁再执行。

2. 使用Lock里面的Condition的await和signal实现唤醒和阻塞


public class ConditionAwait {

    public static void main(String[] args) {
        Lock lock = new ReentrantLock();
        Condition condition = lock.newCondition();

        new Thread(() -> {
            lock.lock();
            try {
                Util.log("A start");
                condition.await();
                Util.log("A over");
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            lock.unlock();
        }).start();

        Util.sleep(100);

        new Thread(() -> {
            lock.lock();
            Util.log("B start");
            condition.signal();
            Util.log("B over");
            lock.unlock();
        }).start();
    }
}
  1. 借助Lock对象那个里面的newCondition()方法生成一个Condition对象
  2. 线程一获得锁之后就执行到await方法,线程阻塞,锁释放。
  3. 线程二获得锁之后执行到signal方法,线程一被唤醒,等线程二释放锁,线程一获得锁后继续执行。

3. 使用LockSupport里面的park和unpark方法实现阻塞和唤醒


public class LockSupportDemo {
    public static void main(String[] args) {
        Thread t = new Thread(() -> {
            Util.log("start");
            LockSupport.park();
            Util.log("over");
        });
        t.start();

        Util.sleep(100);

        new Thread(() -> {
            Util.log("start");
            LockSupport.unpark(t);
            Util.log("over");
        }).start();
    }
}
  1. 不需要在同步代码块中执行,不需要锁,能够随时随地的阻塞和唤醒线程,非常灵活
  2. 线程t执行到park方法就会阻塞,等待另一个线程来unpark(t)来唤醒该线程继续执行。
  3. 另一个线程执行unpark(t)之后,就和线程t同时继续执行

如何验证执行唤醒之后,两个线程是如何执行的?

分别在阻塞和唤醒的后面加上一个循环

for(int i=0;i<100;i++){
    Util.sleep(10);
    Util.log("A");
}

辅助工具类Util.java

public class Util {
    public static void sleep(int millis) {
        try {
            Thread.sleep(millis);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }

    public static void log(String msg) {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        System.out.println(sdf.format(new Date()) + " " + Thread.currentThread().getName() + " " + msg);
    }
}