关于线程通信机制


一个线程的动作可以让另一个线程感知到。(在Java面试的情况中,大多数情况下,线程间的通信机制指的是线程间的交互,即线程的 唤醒塞。并不是字面意义上的多个线程之间互相共享和交换数据的“通信”。)

wait()方法:

wait():使线程停止运行,进入等待状态。

public class ThreadDemo38 {
    public static void main(String[] args) throws InterruptedException {

        Object lock = new Object();

        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("线程1:进入线程方法。");
                synchronized (lock) {
                    try {
                        // 线程休眠
                        lock.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println("线程1:执行完成!");
            }
        }, "t1");
        t1.start();

        Thread.sleep(1000);
        System.out.println("主线程唤醒线程1");
        synchronized (lock) {
            // 唤醒线程
            lock.notify();
        }
    }
}

notify()方法:

notify():使停止的线程继续运行(唤醒),该方法只能唤醒某一个等待线程。

notifyAll()方法:

notifyAll():使停止的线程继续运行(唤醒),但如果有多个线程都在等待中,那么该方法可以一次唤醒所有的等待线程。

public class ThreadDemo39 {
    public static void main(String[] args) throws InterruptedException {

        Object lock = new Object();
        Object lock2 = new Object();

        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("线程1:进入休眠");
                synchronized (lock) {
                    try {
                        // 线程休眠
                        lock.wait(0);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }

                System.out.println("线程1:执行完成");
            }
        }, "t1");
        t1.start();

        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("线程2:进入休眠");
                synchronized (lock) {
                    try {
                        lock.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println("线程2:执行结束");
            }
        }, "t2");
        t2.start();

        Thread t3 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("线程3:进入休眠");
                synchronized (lock2) {
                    try {
                        lock2.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println("线程3:结束执行");
            }
        }, "t3");
        t3.start();

        Thread.sleep(2000);
        System.out.println("主线程唤醒线程");
        synchronized (lock) {
            // 唤醒线程
            lock.notifyAll();
        }
    }
}

LockSupport.park()方法:

该方法也是唤醒线程,但该方法可以唤醒指定的线程,而notify()和notifyAll()不能唤醒指定的线程。

import java.util.concurrent.locks.LockSupport;



public class ThreadDemo42 {
    public static void main(String[] args) throws InterruptedException {

        Object lock = new Object();

        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("线程1:进入休眠");
                // 线程休眠
                LockSupport.park();
                System.out.println("线程1:执行完成");
            }
        }, "t1");


        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("线程2:进入休眠");
                LockSupport.park();
                System.out.println("线程2:执行结束");
            }
        }, "t2");


        Thread t3 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("线程3:进入休眠");
                LockSupport.park();
                System.out.println("线程3:结束执行");
            }
        }, "t3");

        t1.start();
        t2.start();
        t3.start();

        Thread.sleep(100);
        System.out.println("------------------");
        LockSupport.unpark(t3);

        Thread.sleep(100);
        LockSupport.unpark(t1);

        Thread.sleep(100);
        //唤醒线程t2
        LockSupport.unpark(t2);


    }
}

LockSupport()注意事项:
在这里插入图片描述

import java.util.Date;
import java.util.concurrent.locks.LockSupport;

public class ThreadDemo44 {
    public static void main(String[] args) {

        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("线程进入休眠:" + new Date());
                LockSupport.parkUntil(System.currentTimeMillis() + 1000);//需加上当前系统时间
                System.out.println("线程终止休眠:" + new Date());
            }
        }).start();

    }
}

LockSupport.park()相当于wait()的升级版;LockSupport().park()使用时虽然没有Interrupt()的try catch,但仍然可以正常感知到Interrupt()方法。

重点:

使用wait(),notify(),notifyAll()注意事项:

1. 在使用以上方法的时候必须要加锁。

2. 加锁对象和 wait/notify/notifyAll 的对象必须保持一致。

3. 一组wait和notfiy/notfiyAll 必须是同一对象。

5. notifyAll只能唤醒当前对象的所有等待线程。

wait()和LockSupport.park()的 区别:
相同点:

1. 两个都可以让线程进行休眠。

2. 二者都可以参数或者不传参,并且二者线程状态也是一致的。

不同点:

  1. wait必须要配合synchronized一起使用(必须加锁),而 LockSupport 不允许加锁。
  2. wait对应的唤醒方法只能唤醒全部或随机的一个线程,而LockSupport的对应唤醒方法可以唤醒指定的线程。

Thread.sleep(0) VS Obiect.wait(0):

  1. sleep()休眠的缺点是必须要有明确的休眠时间。

  2. wait来自于0bject中的一个方法,而sleep是Thread的静态方法。

  3. sleep(0)表示立即出发一次CPU资源的抢占,wait(0)表示永久的等待下去。

面试题1:wait()和sleep()的区别?

相同点:

1.都可以让当前线程休眠;

2.都必须要处理一个Interrupt异常。

不同点:

1. wait来自于0bject中的一个方法,而sleep是Thread的静态方法。

2. 传参不同,wait可以没有参数,而sleep必须有一个大于等于0的参数

3. wait使用是必须加锁,sleep使用时不用加锁。
证明代码如下:

/**
 * 证明wait会释放锁 sleep不会
 */
public class ThreadDemo40 {
    public static void main(String[] args) throws InterruptedException {
        Object lock = new Object();

        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (lock) {
                    System.out.println("线程1:拥有了锁 lock,进入休眠状态");
//                    try {
//                        lock.wait(999999 * 1000);
//                    } catch (InterruptedException e) {
//                        e.printStackTrace();
//                    }
                    try {
                        Thread.sleep(3 * 1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("线程1:执行完成,释放锁 l ock");
                }
            }
        });
        t1.start();

        Thread.sleep(1000);
        // 主线程试图获取锁
        synchronized (lock) {
            System.out.println("主线程获取到锁 lock");
        }


    }
}

4.wait使用时会释放锁,而sleep不会释放锁。

5. wait 默认不传参的情况下会进入 WAITING状态,而sleep会进入TIME_WAITING状态。

面试题2:为什么wait会放到Object(对象)中而不是Thread(线程)中?

wait操作必须要加锁和释放锁,而锁又是属于对象级别而非线程级别(线程和锁是一对多的关系,也就是一个线程可以拥有多把锁),为了灵活起见(一个线程当中会有多把锁),就把 wait放在0bject。

面试题3:wait()为什么要加锁?

因为wait()在使用的时候要释放锁。

面试题4:wait()为什么要释放锁?

wait()是默认不传递任何值的,当不传递任何值的时候表示永久的等待,这样就会造成一把锁一直被一个线程所持有,为了避免这种问题的发生,所以wait()需要释放锁。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值