【多线程】线程间通信及状态

文章目录

    • 1. 线程间的通信
      • 1.1 wait和notify
      • 1.2 notify随机唤醒
      • 1.3 notifyAll()
      • 1.4 join()
    • 2. 线程间的状态
    • 3. 验证线程的状态
      • 3.1 验证NEW、RUNNABLE、TERMINATED
      • 3.2 验证WAITING
      • 3.3 验证TIMED-WAITING
      • 3.4 验证BLOCKED
    • 4. 面试题:wait和sleep对比

1. 线程间的通信

1.1 wait和notify

由于线程之间是抢占式运行,所以无法预知线程的执行顺序,但是实际开发中我们需要协调线程间的执行先后顺序。所以我们引入了等待通知机制。
wait()方法:
会使一个线程进入堵塞,进入等待状态,等待被唤醒,不然永远不会执行,但必须搭配synchronized使用,不然就会抛出异常,执行后释放锁。
notify()方法:
会随机唤醒一个等待的线程(不是先来后到),也需要搭配synchronized使用,但是只有执行完同步代码块才会释放锁。
notifyAll()方法
一次将所有等待的线程唤醒,其他和wait方法相同。

创建线程类ThreadA.java:

public class MyThreadA extends Thread{
    private Object lock;
    public MyThreadA(Object lock){
        this.lock  = lock;
    }
    @Override
    public void run() {
        try{
            synchronized (lock){
                System.out.println(Thread.currentThread().getName() + ": begin");
                lock.wait();//死等,释放锁
                System.out.println(Thread.currentThread().getName() + ": end");
            }
        } catch (InterruptedException e){
            e.printStackTrace();
        }
    }
}

创建线程类ThreadB.java:

public class MyThreadB extends Thread{
    private Object lock;
    public MyThreadB(Object lock){
        this.lock = lock;
    }
    @Override
    public void run() {
        synchronized (lock){
            System.out.println("t2: begin");
            lock.notify();//唤醒一个线程,随机的,不释放锁
            System.out.println("t2: end");
        }
    }
}

创建运行类Running.java:

public class Running {
    public static void main(String[] args) throws InterruptedException {
        Object lock = new Object();
        MyThreadA t1 = new MyThreadA(lock);
        MyThreadB t2 = new MyThreadB(lock);
        t1.start();
        Thread.sleep(1000);
        t2.start();
    }
}

如果等待机制成立,那么运行过程应该是先启动线程t1,然后打印"Thread-0: begin",然后执行wait(),进入等待并释放锁,然后在启动t2后会先打印"t2: begin",然后唤醒t1,但是因为锁还没释放,会继续执行t2后面代码,打印"t2: end",最后释放锁,执行t1,打印"Thread-0: end",如果运行结果和我们分析相同,那么等待机制成立。
运行结果:
【多线程】线程间通信及状态_第1张图片

1.2 notify随机唤醒

修改运行类:Running.java:

public class Running {
    public static void main(String[] args) throws InterruptedException {
        Object lock = new Object();
        MyThreadA t1 = new MyThreadA(lock);
        MyThreadA t2 = new MyThreadA(lock);
        MyThreadA t3 = new MyThreadA(lock);
        MyThreadB t4 = new MyThreadB(lock);
        t1.start();
        t2.start();
        t3.start();
        Thread.sleep(1000);
        t4.start();
    }
}

多次运行结果如果不相同,则notify唤醒不遵循先来后到。
运行结果:
【多线程】线程间通信及状态_第2张图片
【多线程】线程间通信及状态_第3张图片

1.3 notifyAll()

修改线程类ThreadB.java:

public class MyThreadB extends Thread{
    private Object lock;
    public MyThreadB(Object lock){
        this.lock = lock;
    }
    @Override
    public void run() {
        synchronized (lock){
            System.out.println("t2: begin");
            lock.notifyAll();//唤醒全部线程
//            lock.notify();//唤醒一个线程,随机的,不释放锁
            System.out.println("t2: end");
        }
    }
}

运行结果:
【多线程】线程间通信及状态_第4张图片
很明显唤醒了全部线程。

1.4 join()

有时我们需要等待一个线程运行结束,在执行其他线程,这个时候我们就可以使用join()方法。

public class Test1 {
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(() -> {
            System.out.println("t1执行");
        });
        Thread t2 = new Thread(() -> {
            System.out.println("t2执行");
        });
        t2.start();
        t2.join();
        t1.start();
    }
}

分析代码,t2调用join(),那么只有t2线程执行结束才可以执行下面代码,也就是t1。那么如果是:t2执行,t1执行,那么join成立。
运行结果:
【多线程】线程间通信及状态_第5张图片

2. 线程间的状态

线程对象在不同时期有不同状态,这些状态都在枚举类State中,则:

public class MyThreadState {
    //线程状态,都在State枚举类中
    public static void main(String[] args) {
        for (Thread.State state :
                Thread.State.values()) {
            System.out.println(state);
        }
    }
}

运行结果:
【多线程】线程间通信及状态_第6张图片

NEW:已经安排了工作,但未启动的线程对象。
RUNNABLE:可以运行中的线程对象,可分为正在运行,即将运行。
BLOCKED:受阻塞,等待某个监视锁的线程对象。
WAITING:无期限的等待另一个线程执行特定操作的线程。
TIMED_WIATING:指定等待时间等待另一个线程的操作的线程对象。
TERMINATED:工作结束。

3. 验证线程的状态

3.1 验证NEW、RUNNABLE、TERMINATED

实例化一个线程未启动,这时线程就是NEW状态,当启动后,运行中就是RUNNABLE状态,等待线程运行结束,就是TERMINATED状态。

    //验证: NEW RUNNABLE TERMINATED
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(() -> {
            System.out.println("运行中: " + Thread.currentThread().getState());
        });
        System.out.println("main中: " + t1.getState());
        Thread.sleep(1000);
        t1.start();
        Thread.sleep(1000);
        System.out.println("运行结束: "  +t1.getState());
    }

运行结果:
【多线程】线程间通信及状态_第7张图片

3.2 验证WAITING

给一个线程上锁,不解锁,让它一直等待,则线程处于WAITING状态。

    //验证: WAITING
    public static void main(String[] args) throws InterruptedException {
        Object lock = new Object();
        Thread t1 = new Thread(() -> {
            System.out.println("运行中:");
            try{
                synchronized (lock){
                    lock.wait();
                }
            }catch (InterruptedException e){
                e.printStackTrace();
            }
        });
        t1.start();
        Thread.sleep(1000);
        System.out.println("无休止等待: " + t1.getState());
    }

运行结果:
【多线程】线程间通信及状态_第8张图片

3.3 验证TIMED-WAITING

给一个线程休眠,休眠中查看它的状态,则应是TIMED-WAITING状态。

    //验证: TIMED_WAITING
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(() -> {
            System.out.println("运行中: "  + Thread.currentThread().getState());
            try {
                Thread.sleep(10000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        });
        t1.start();
        Thread.sleep(1000);
        System.out.println("指定时间等待: " + t1.getState());
    }

运行结果:
【多线程】线程间通信及状态_第9张图片

3.4 验证BLOCKED

当一个线程等待另一个线程而堵塞,则应是BLOCKED状态。

 //验证: BLOCKED
    public static void main(String[] args) throws InterruptedException {
        Object lock = new Object();
        Thread t1 = new Thread(() -> {
            System.out.println("t1: " + Thread.currentThread().getState());
            try{
                synchronized (lock){
                    Thread.sleep(10000);
                }
            }catch (InterruptedException e){
                e.printStackTrace();
            }
        });
        Thread t2 = new Thread(() -> {
           synchronized (lock){
               System.out.println("t2: " + Thread.currentThread().getState());
           }
        });
        t1.start();
        Thread.sleep(1000);
        t2.start();
        Thread.sleep(1000);
        System.out.println("t2: " + t2.getState());
    }

运行结果:
【多线程】线程间通信及状态_第10张图片

4. 面试题:wait和sleep对比

  1. 一个用于线程通信,一个用于线程堵塞,唯一的相同点就是使线程放弃了一部分执行时间。
  2. wait需要搭配synchronized使用,sleep不用。
  3. wait使Object的方法,sleep使Thread的静态方法。

你可能感兴趣的:(Java多线程编程,开发语言,多线程)