java JUC并发编程 第三章 中断机制

系列文章目录

第一章 java JUC并发编程 Future: link
第二章 java JUC并发编程 多线程锁: link
第三章 java JUC并发编程 中断机制: link
第四章 java JUC并发编程 java内存模型JMM: link
第五章 java JUC并发编程 volatile与JMM: link
第六章 java JUC并发编程 CAS: link
第七章 java JUC并发编程 原子操作类增强: link
第八章 java JUC并发编程 ThreadLocal: link
第九章 java JUC并发编程 对象内存布局与对象头: link
第十章 java JUC并发编程 Synchronized与锁升级: link
第十一章 java JUC并发编程 AbstractQueuedSynchronizer之AQS: link



1 LockSupport前言

在这里插入图片描述
在这里插入图片描述

interrupt:中断此线程
interrupted:测试当前线程是否已被中断
isInterrupted:测试此线程是否已被中断
什么是中断机制
在这里插入图片描述

1.1 如何停止中断运行中的线程

1.1.1 通过volatile变量实现

package com.util.interrup;

import java.util.concurrent.TimeUnit;

public class InterrupDemo {
    static  volatile  boolean isStop = false;

    public static void main(String[] args) {
        new Thread(()->{
            while (true){
                if(isStop){
                    System.out.println(Thread.currentThread().getName()+"isStop被修改为true,程序停止");
                    break;
                }
                System.out.println("----hello");
            }
        },"t1").start();
        try {TimeUnit.MILLISECONDS.sleep(20);} catch (InterruptedException e) {e.printStackTrace();}

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

1.1.2 通过AtomicBoolean实现

package com.atguigu.springcloud.util.interrup;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;

public class InterrupDemo {

    static AtomicBoolean atomicBoolean = new AtomicBoolean(false);

    public static void main(String[] args) {
        new Thread(()->{
            while (true){
                if(atomicBoolean.get()){
                    System.out.println(Thread.currentThread().getName()+"isStop被修改为true,程序停止");
                    break;
                }
                System.out.println("----hello");
            }
        },"t1").start();
        try {TimeUnit.MILLISECONDS.sleep(20);} catch (InterruptedException e) {e.printStackTrace();}

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

}

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

package com.atguigu.springcloud.util.interrup;

import java.util.concurrent.TimeUnit;

public class InterrupDemo {

    public static void main(String[] args) {
        Thread t1 = new Thread(()->{
            while (true){
                if(Thread.currentThread().isInterrupted()){
                    System.out.println(Thread.currentThread().getName()+"isInterrupted被修改为true,程序停止");
                    break;
                }
                System.out.println("t1 ---- hello interrupt api");
            }
        },"t1");
        t1.start();

        try {TimeUnit.MILLISECONDS.sleep(20);} catch (InterruptedException e) {e.printStackTrace();}
        //t2向t1发出协商,将t1的中断标志位设为true希望t1停下来
        Thread t2 = new Thread(()->{
            t1.interrupt();
        },"t2");
        t2.start();
        //t1.interrupt();
    }
}

1.1.3.1 interrupt()源码分析

在这里插入图片描述
底层调用的是interrupt0() native的方法
在这里插入图片描述

1.1.3.2 isInterrupted源码分析

返回线程是否被中断过,中断的状态是否被重置,取决于当前ClearInterrupted的入参
在这里插入图片描述
在这里插入图片描述

1.2当前线程的中断标识为true,是不是线程就立刻停止

不会的

package com.atguigu.springcloud.util.interrup;

import java.util.concurrent.TimeUnit;

public class InterrupDemo2 {
    public static void main(String[] args) {
        //实例方法interrupt(),仅仅是设置线程的中断状态设置为true,不会停止线程。
        Thread t1  = new Thread(()->{
            for (int i = 0; i < 300; i++) {
                System.out.println("----i="+i);
            }
        },"t1");
        t1.start();
        System.out.println("t1线程默认的中断标志02:"+Thread.currentThread().isInterrupted());//false
        try {TimeUnit.MILLISECONDS.sleep(2);} catch (InterruptedException e) {e.printStackTrace();}
        t1.interrupt();//true
        System.out.println("t1线程调用interrupt()后的中断标志01:"+t1.isInterrupted());//true

        try {TimeUnit.MILLISECONDS.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}
        System.out.println("t1线程调用interrupt()后的中断标志03:"+t1.isInterrupted());//false:2秒t1线程结束了不活动的线程不会产生任何影响
    }
}

在这里插入图片描述
在这里插入图片描述

package com.atguigu.springcloud.util.interrup;

import java.util.concurrent.TimeUnit;

public class InterrupDemo3 {
    public static void main(String[] args) {
        /**
         * 1 中断标志位,默认false
         * 2 t2--->t1发出了中断协商,t2调用t1.interrupt(),中断标志位true
         * 3 中断标志位true,正常情况,程序停止
         * 4 中断标志位true,异常情况,InterruptedException,将会把中断状态被清除,并且将收到InterruptedException.中断标志位false
         * 导致无线循环
         * 5 在catch块中,需要再次给中断标志位设置为true,2次调用停止程序才ok
         */
        Thread t1 = new Thread(()->{
            while (true){
                if(Thread.currentThread().isInterrupted()){
                    System.out.println(Thread.currentThread().getName()+"线程----isInterrupted()=true,自己退出了");
                    break;
                }
                try {
                    Thread.sleep(200);
                }catch (InterruptedException e) {
                    Thread.currentThread().interrupt();//没有它,程序不会停止,中断不打断
                    e.printStackTrace();
                }
                System.out.println("----hello InterruptDemo3");
            }
        },"t1");
        t1.start();
        try {TimeUnit.SECONDS.sleep(1);}catch (InterruptedException e) {e.printStackTrace();}
        t1.interrupt();
    }

}

中断只是一种协商机制,修改中断标志位而已,不是立刻stop打断

1.3 静态方法Thread.interrupted()的理解

在这里插入图片描述
说明
在这里插入图片描述

package com.atguigu.springcloud.util.interrup;

public class InterrupDemo4 {
    public static void main(String[] args) {
        //测试当前线程是否被中断(检查中断标志),返回一个boolean并清楚中断状态。
        //第二次调用时中断状态已经被清除,将返回一个False.
        System.out.println(Thread.currentThread().getName()+"\t"+Thread.interrupted());//false  main线程的中断状态false
        System.out.println(Thread.currentThread().getName()+"\t"+Thread.interrupted());//false  main线程的中断状态false
        System.out.println("----1");
        Thread.currentThread().interrupt();//中断标志设置为true  main线程的中断状态设置为true
        System.out.println("----2");
        System.out.println(Thread.currentThread().getName()+"\t"+Thread.interrupted());//true 会把当前的中断状态清零并重新设置为false
        System.out.println(Thread.currentThread().getName()+"\t"+Thread.interrupted());//false main线程的中断状态false
    }
}

interrupt:是否需要清理中断标志位传的是true清理
isInterrupted:是否需要清理中断标志位传的是false不清理
在这里插入图片描述
总结
在这里插入图片描述

2 LockSuppor

在这里插入图片描述
在这里插入图片描述

2.1 3种让线程等待和唤醒的方法

2.1.1 方式1:使用Object中的wait()方法让线程等待,使用Object中的notify方法唤醒线程

package com.ge.healthcare.cn.apm.gateway.util;
public class LockSupportDemo {
    static Object objectLock = new Object();
    public static void main(String[] args) {
        new Thread(()->{
            synchronized (objectLock){
                System.out.println(Thread.currentThread().getName()+"  --------come in");
                try {
                    objectLock.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+" -----------------被唤醒");
            }
        },"A").start();
        new Thread(()->{
            synchronized (objectLock){
                objectLock.notify();
                System.out.println(Thread.currentThread().getName()+"  ------------通知");
            }
        },"B").start();
    }
}

2.1.1.1 异常情况1
package com.atguigu.springcloud.util;

public class LockSupportDemo {
    static Object objectLock = new Object();
    public static void main(String[] args) {
        new Thread(()->{
//            synchronized (objectLock){
                System.out.println(Thread.currentThread().getName()+"  --------come in");
                try {
                    objectLock.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+" -----------------被唤醒");
//            }
        },"A").start();
        new Thread(()->{
//            synchronized (objectLock){
                objectLock.notify();
                System.out.println(Thread.currentThread().getName()+"  ------------通知");
//            }
        },"B").start();
    }
}

如果要使用wait和notify必须将它包在synchronized锁块之间
在这里插入图片描述

2.1.1.2 异常情况2

先notify再wait,程序无法执行,无法唤醒

package com.atguigu.springcloud.util;

import java.util.concurrent.TimeUnit;

public class LockSupportDemo {
    static Object objectLock = new Object();
    public static void main(String[] args) {
        new Thread(()->{
            try {
            TimeUnit.SECONDS.sleep(1);}catch (InterruptedException e) {e.printStackTrace();}
            synchronized (objectLock){
                System.out.println(Thread.currentThread().getName()+"  --------come in");
                try {
                    objectLock.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+" -----------------被唤醒");
            }
        },"A").start();

        new Thread(()->{
            synchronized (objectLock){
                objectLock.notify();
                System.out.println(Thread.currentThread().getName()+"  ------------通知");
            }
        },"B").start();
    }
}

总结:wait和notify方法必须要在同步块或者方法里面,且成对出现使用
先wait后notify才行

2.1.2 方式2:使用JUC包中的Condition的await方法让线程等待,使用signal方法唤醒线程

package com.atguigu.springcloud.util;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class LockSupportDemo {
    static Lock lock = new ReentrantLock();
    static Condition condition = lock.newCondition();
    public static void main(String[] args) {
        new Thread(()->{
            lock.lock();
            try {
                System.out.println(Thread.currentThread().getName()+"----------come in");
                try {
                    condition.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+"----------被唤醒");
            } finally {
                lock.unlock();
            }
        },"A").start();
        new Thread(()->{
            lock.lock();
            try {
                condition.signal();
                System.out.println(Thread.currentThread().getName()+"----------通知");
            } finally {
                lock.unlock();
            }
        },"B").start();
    }
}

2.1.2.1 异常情况1

注掉lock,unlock
同2.1.1.1

2.1.2.2 异常情况2

先signal后await
同2.1.1.2

总结:Condition中的线程等待和唤醒方法,需要先获取锁
一定要先await后signal,不要反了。

2.1.3 方式3: LockSupport类可以阻塞当前线程以及唤醒指定被阻塞的线程

通过park和unpark(thread)方法来实现阻塞和唤醒线程的操作
LockSupport是用来创建锁和其他同步类的基本线程阻塞原语
LockSupport类使用了一种名为Permit(许可)的概念来做到阻塞和唤醒线程的功能,每个线程都有一个许可(permit),但与Semaphore不同的是,许可的累加上限是1.
0L:如果没有通行证就永远不放行
在这里插入图片描述

在这里插入图片描述

package com.atguigu.springcloud.util;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.LockSupport;
import java.util.concurrent.locks.ReentrantLock;

public class LockSupportDemo {
    static Lock lock = new ReentrantLock();
    public static void main(String[] args) {

        Thread a = new Thread(()->{
            System.out.println(Thread.currentThread().getName()+"--------------come in");
            LockSupport.park();//被阻塞....等待通知放行,它要通过时需要许可证
            System.out.println(Thread.currentThread().getName()+"--------------被唤醒");
        },"a");
        a.start();
        try {
            TimeUnit.SECONDS.sleep(3L);} catch (InterruptedException e) { e.printStackTrace();}

        Thread b = new Thread(()->{
            LockSupport.unpark(a);
            System.out.println(Thread.currentThread().getName()+"--------------通知了");
        },"b");
        b.start();
    }
}

如果b线程先发通知,a线程后被阻塞运行结果:正常
并且无锁块的要求
原因是先执行了unpark(a)导致上面的park方法形同虚设无效。

package com.atguigu.springcloud.util;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.LockSupport;
import java.util.concurrent.locks.ReentrantLock;

public class LockSupportDemo {
    static Lock lock = new ReentrantLock();
    public static void main(String[] args) {

        Thread a = new Thread(()->{
            try {TimeUnit.SECONDS.sleep(3L);} catch (InterruptedException e) { e.printStackTrace();}
            System.out.println(Thread.currentThread().getName()+"--------------come in");
            LockSupport.park();//被阻塞....等待通知放行,它要通过时需要许可证
            System.out.println(Thread.currentThread().getName()+"--------------被唤醒");
        },"a");
        a.start();
        Thread b = new Thread(()->{
            LockSupport.unpark(a);
            System.out.println(Thread.currentThread().getName()+"--------------通知了");
        },"b");
        b.start();
    }
}

2.1.3.1 问题多个park和unpark会阻塞么:会

因为许可证不会累计,不要出现多对多

package com.atguigu.springcloud.util;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.LockSupport;
import java.util.concurrent.locks.ReentrantLock;

public class LockSupportDemo {
    static Lock lock = new ReentrantLock();
    public static void main(String[] args) {

        Thread a = new Thread(()->{
            try {TimeUnit.SECONDS.sleep(3L);} catch (InterruptedException e) { e.printStackTrace();}
            System.out.println(Thread.currentThread().getName()+"--------------come in");
            LockSupport.park();//被阻塞....等待通知放行,它要通过时需要许可证
            LockSupport.park();//被阻塞....等待通知放行,它要通过时需要许可证
            System.out.println(Thread.currentThread().getName()+"--------------被唤醒");
        },"a");
        a.start();
        Thread b = new Thread(()->{
            LockSupport.unpark(a);
            LockSupport.unpark(a);
            System.out.println(Thread.currentThread().getName()+"--------------通知了");
        },"b");
        b.start();
    }
}

2.1.3.2 总结

在这里插入图片描述
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值