zoukankan      html  css  js  c++  java
  • java线程总结2--wait/notify(all)/sleep以及中断概念

    上一篇关于线程的博客简单梳理了一下多线程的一些基本概念,今天这篇博客再进行多线程编程中一些核心的方法进行简单的梳理和总结,主要是wait,sleep和notify方法以及中断的概念

    一、中断概念。

      在多线程中,中断可以理解为线程之间一种特殊的通讯手段或者说相互控制的一种方式。调用某个线程对象的中断方法--interrupt可以控制该线程,使其抛出interruptedException,从而中断线程的执行。

      中断很多时候发生在线程之间需要同步或者线程执行之间有关联时(个人理解),例如:线程A要等到线程B执行完某段代码方可正常执行,但是,线程B有可能会出现因为阻塞问题(例如某个时候网络不好,流操作阻塞时间长)执行时间过长,而线程A这时不想等到线程B执行这么久,直接中断B线程以获得较快的响应速度(这种情况可以类比网络不好时,一般只是等待一定时间,而不会无限等待下去)。

      总的来说,中断提供了一种除了外界线程控制线程执行去向的手段。

    二、sleep方法

      相信对多线程有哪怕一点了解的伙伴估计都知道sleep方法,它可以让当前线程进入休眠状态,该状态就是,什么都不做的状态,当时却不会释放持有的对象锁。

    sleep方法较为简单,简单上个例子吧:

    public static void main(String[] args) {
            new Thread(new Runnable() {
                public void run() {
                    System.out.println("即将调用sleep方法");
                    try {
                        /*说明:sleep方法是Thread中的静态方法,在该线程中调用该方法表示的是使该线程陷入睡眠状态,参数的数值是睡眠的时间长度
                         * 同时,该方法会抛出异常,所以需要进行捕获或者抛出,该异常的抛出是因为外部线程程序对该线程进行了interrupt方法调用,中断了线程,这个后面会具体讲
                         */
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }).start();
        }

    当然,sleep方法如果持有锁是,sleep期间是不会释放的,这里不举例子测试了。

    下面简单说说,sleep方法被中断的操作过程。线程的中断,一般是在外部线程直接调用该线程对象的interrupt方法使得当前线程抛出异常而中断,具体参考代码,注意看注释:

    public class SynchRnizedAndLock {
        public static void main(String[] args) throws InterruptedException {
            Thread t1 = new Thread(new DemoSleepMethodTest());
            t1.start();
            //调用interrupt方法
            Thread.sleep(1000);
            System.out.println("调用了interrupt方法");
            t1.interrupt();//在主线程执行中断方法
          
        }
    }
    //sleep方法中断测试例子
    class DemoSleepMethodTest implements Runnable{
        public void run() {
            System.out.println("执行了sleep方法");
            try {
                Thread.sleep(10000);//调用sleep方法
                System.out.println("sleep方法后面的代码,由于被中断了,不会执行到");
            } catch (InterruptedException e1) {
                System.out.println("sleep方法被中断");
            }
        }
    }

    sleep方法较为简单,相信看完上面的例子也差不多理解了。sleep方法一般是在等待某些资源的时候,程序检测到有可能需要等待的时间较长,所以使自己陷入一种休眠的状态,腾出CPU的资源给其他线程使用。当然,线程的休眠不会影响到其他的线程,这点和wait方法就有所差别了。

    搞定sleep方法,下面进行wait方法的简单总结。

    三、wait方法。

      和sleep方法一样的是,wait方法也会使得当前线程停止执行,但是,wait方法的具体特性和sleep方法还是有很多区别的,下面一一讲解。

      首先,简单介绍下wait方法的用法,它和sleep用法差别还是蛮大的,具体请看代码注释:

    //测试用例对象
    class Test{
        //定义两个对象
        static Object object1 = new Object();
        static Object object2 = new Object();
    }
    //主类
    public class SynchRnizedAndLock { public static void main(String[] args) throws InterruptedException { Thread t1 = new Thread(new DemoSleepMethodTest()); t1.start(); //调用interrupt方法 Thread.sleep(1000); System.out.println("调用了interrupt方法"); t1.interrupt(); } }//wait中断方法测试例子 class DemoWaitMethodTest implements Runnable{ public void run() { System.out.println("准备执行wait方法"); synchronized(Test.object1){ try { System.out.println("调用了wait方法"); //调用wait方法,注意的是,wait方法必须在同步代码块中执行,因为它是调用同步数据对象的wait方法而不是线程本身的wait方法 Test.object1.wait();
              System.out.println("wait后面的方法由于被中断了,不会被执行到"); }
    catch (InterruptedException e) { //当其他线程调用了被线程的interrupt方法时,就会中断该线程,此时会抛出异常 System.out.println("wait方法被中断"); } } } }

    上面的代码可以看到(注释也说了):wait方法调用的并不是线程的wait方法,而是该线程同步快中的参数对象即被同步的数据对象的wait方法;所以,wait方法必须是放在同步代码块里面的。

    和sleep方法一样,wait方法也需要捕获处理异常,抛出异常的情况和sleep类似:有外部线程调用了该线程对象的interrupt方法。

    四、notify(all)方法和wait方法的关系

      notify方法用于唤醒一个之前调用了的wait方法而陷入等待的一个线程。同样的,该方法也必须要在同步代码块里面执行,调用的是同步对象的notify方法。具体的话看下面一大坨代码吧,注意看注释了:

    public class NotifyTest {
        public static void main(String[] args) throws InterruptedException {
            new Thread(new WaitNotifyTestDemo()).start();
            Thread.sleep(1000);
            new Thread(new NotifyTestDemo()).start();
        }
    
    }
    //notify方法测试
    class NotifyTestDemo implements Runnable{
        public static void notifyTestMethod(){
            //notify方法也必须在synchronized代码块里面
            synchronized(Test.object1){
                System.out.println("即将执行notify方法");
                /*注意,这里和wait方法一样,notify方法对应的是锁定的对象(这里是Test.object1)的方法
                 * notify的意思是:在执行完该同步代码块之后,唤醒一个调用了wait方法的线程并从wait处继续
                 * 执行wait方法下面的代码,如果没有调用notify方法,对应的线程将一直等待
                 */
                Test.object1.notify();
                try {
                    Thread.sleep(10000);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                System.out.println("我是notify后面的代码,在notify后面所有代码执行完才会释放Test.object1的锁");
            }
        }
        public void run() {
            notifyTestMethod();
        }
    }
    //对应的wait测试类,主要用于测试wait和notify之间的关系
    class WaitNotifyTestDemo implements Runnable{
        public static void waitNotifyMethod(){
            synchronized (Test.object1) {
                System.out.println("即将执行wait方法");
                try {
                    /*这里执行了wait方法后,线程进入等待状态,释放锁,此时该锁被执行了notifyTestMethod方法线程获得
                     * 这里需要注意的是:该线程调用了wait方法之后,将一直等待直到notify方法或者notifyAll唤醒它
                     */
                    Test.object1.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("我是wait后面的代码,我需要被外部线程的notify方法唤醒才能继续执行,否则,我所在线程是无法继续执行的,它将会一直处于等待状态!");
            }
        }
    
        public void run() {
            waitNotifyMethod();
        }
    }

    代码比较长,不过很多问题注释中也说清楚了,下面在进行简单的总结吧:

    notify方法就是一个可以唤醒处于wait状态的线程的方法,至于唤醒的是哪个线程,一般是等待队列中的首个线程(这里补充下:线程调用了wait方法会自动被放进一个等待队列中的),然后该线程接着从wait代码块之后继续往下执行。

    然后,这里再讲讲notifyAll的方法,notifyAll方法和notify方法大体上性质差不多,只是notifyAll方法表示唤醒wait队列中的所有线程,让这些线程进行锁的抢夺,谁抢到了,谁就拥有执行权,其他线程进入阻塞状态。

    今天这篇博客代码量有点多,但是我发觉,多线程的东西,不用代码例子是难以理解的,最重要的是自己敲代码试试,这样学习效果比较明显,之前一直在看书,效果的确比不上代码中实验来得直观和易于理解。

  • 相关阅读:
    Datatable导出到Excel
    C# 连接EXCEL和ACCESS字符串2003及2007版字符串说明
    C#-读取写入Excel
    简易的命令行入门教程:
    日志记录
    python环境管理器的选择
    go语言的模块处理
    pip 使用国内源 安装类库
    go 实现单链表并使用一种常规实现翻转,一种使用递归实现翻转
    数据库产品选型
  • 原文地址:https://www.cnblogs.com/lcplcpjava/p/6723559.html
Copyright © 2011-2022 走看看