多线程的中断和等待唤醒机制

文章目录

    • 线程的中断
      • 什么是中断 它意味着什么
        • 前提
        • 详细解释
      • 三个中断相关方法的解释
        • void interrupt
        • static boolean interrupted
        • boolean interrupted
      • 怎么响应中断停止线程
        • 通过volatitle变量实现
        • 原子变量操作 (他的值也是volatitle标识的)
        • 通过Thread类自带的中断api方法实现
      • 中断停止的坑
    • 线程的等待和唤醒
      • 三种机制
        • synchronized的wait和notify方法
        • Condition的await和signal方法
        • LockSupport类的park和unpark方法

线程的中断

什么是中断 它意味着什么

前提

首先,一个线程就不应该由其他线程来进行强制的中断或者停止,而是应该由线程自己自行停止.所以Thread类中的停止方法都已经被废弃了 不允许在调用了.
其次,既然在Java中没有办法立即停止一条线程,但是停止线程这个需求也是一个刚需,非常重要(比如取消掉耗时的操作),那Java怎么办呢? Java给大家提供了一种用于停止线程的机制 -中断.

详细解释

中断只是一种协作机制,Java没有给中断增加任何语法,中断的过程去由线程的设计者自己去实现.
若要中断一个线程,你需要手动的调用该线程的interrupt方法,该方法也仅仅是将线程对象的中断标识设成true;
接着你需要自己写代码不断地检测当前线程的标识位,如果为true,表示有线程要求你停止.完全由你自主决定怎么做(自己也可以对自己进行中断操作)

三个中断相关方法的解释

void interrupt

实例方法,仅仅是设置线程的中断状态为true,不会停止线程

static boolean interrupted

静态方法 Thread.interrupted() 他做了两件事

  • 返回当前线程的中断状态
  • 将当前线程的中断状态设置为false

boolean interrupted

实例方法 判断当前线程是否被中断

怎么响应中断停止线程

通过volatitle变量实现

static volatile boolean isStop = false;

    public static void main(String[] args) {
        new Thread(() -> {
            while (true) {
                if (isStop) {
                    System.out.println("线程终止");
                    break;
                }
                System.out.println("走走走");
            }
        }, "aa").start();
        try {
            TimeUnit.SECONDS.sleep(1L);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }

        new Thread(() -> {
            isStop = true;
        }, "555").start();


    }

原子变量操作 (他的值也是volatitle标识的)

static AtomicBoolean atomicBoolean = new AtomicBoolean(false);

    public static void main(String[] args) {
        new Thread(() -> {
            while (true) {
                if (atomicBoolean.get()) {
                    System.out.println("线程终止");
                    break;
                }
                System.out.println("走走走");
            }
        }, "aa").start();
        try {
            TimeUnit.SECONDS.sleep(1L);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }

        new Thread(() -> {
            atomicBoolean.set(true);
        }, "555").start();
    }

通过Thread类自带的中断api方法实现

Thread thread = new Thread(() -> {
            while (true) {
                if (Thread.currentThread().isInterrupted()) {
                    System.out.println("线程终止");
                    break;
                }
                System.out.println("走走走");
            }
        }, "aa");
        thread.start();
        try {
            TimeUnit.SECONDS.sleep(2L);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }

        new Thread(() -> {
            thread.interrupt();
        }, "555").start();

中断停止的坑

对线程发起中断时,如果此线程在调用Object类的wait() 、 wait(long)或wait(long, int)方法或join() 、 join(long) 、 join(long, int) int) 方法时被阻塞, sleep(long) , or sleep(long, int) , 这个类的方法,那么它的中断状态将被清除,它会收到一个InterruptedException 。

这个异常会导致中断状态被复位为false,导致收到中断信号的线程永远也无法停止 所以必须处理InterruptedException这个异常(通常就是在异常捕获里本线程在调用一下自己的中断方法)

    try {
        TimeUnit.SECONDS.sleep(1L);
    } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
    }

线程的等待和唤醒

三种机制

synchronized的wait和notify方法

来自于Object基类的本地方法 必须在synchronized修饰的方法或代码块中调用,否则会抛出IllegalMonitorStateException异常,所以他们是强绑定的关系.并且wait和notify方法顺序不能错误 否则会造成阻塞

static Object objectLock = new Object();
new Thread(() -> {
            synchronized (objectLock){
                try {
                    objectLock.wait();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                System.out.println("被唤醒啦");
            }
        }, "aaa").start();
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }

        new Thread(()->{
            synchronized (objectLock){
                objectLock.notify();
                System.out.println("发出来通知了");
            }
        }).start();

Condition的await和signal方法

juc包的类,也需要包裹在Lock类的lock和unlock代码块内,否则会抛出IllegalMonitorStateException异常,await和signal的调用顺序也不能反否则会一直阻塞

    static Lock lock = new ReentrantLock();

    static Condition condition = lock.newCondition();
	new Thread(() -> {
            lock.lock();
            try {
                System.out.println("come in");
                condition.await();
                System.out.println("被唤醒");
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            } finally {
                lock.unlock();
            }
        }, "aaa").start();

        new Thread(() -> {
            lock.lock();
            try {
                condition.signal();
                System.out.println("发出通知");
            } finally {
                lock.unlock();
            }
        }, "bbbb").start();
	

LockSupport类的park和unpark方法

优势是不需要给代码上锁,park和unpark方法的调用顺序可以是相反的不会阻塞

是因为他底层采用的是通行证的原理 有一个值只能为0或者1的标志位 先执行unpark相当于先发了通行证,后再去执行park的时候也不会阻塞,直接放行 通行证的值上限就是1 多次重复的unpark也不会增加上限

Thread t1 = new Thread(() -> {
            System.out.println("come in");
            LockSupport.park();
            System.out.println("被唤醒");
        }, "aaa");
        t1.start();
        new Thread(() -> {
            System.out.println("发出通知");
            LockSupport.unpark(t1);
        }, "ffffsf").start();

你可能感兴趣的:(juc和锁多线程编程,java,jvm,开发语言)