并发编程-Day06

本文探讨了Java内存模型(JMM)的原子性、可见性和有序性,并通过实例解释了volatile和synchronized如何确保线程安全。同时,介绍了两阶段终止模式和犹豫模式在多线程编程中的应用,以及如何利用volatile进行优化。
摘要由CSDN通过智能技术生成

一、JMM内存模型
1.1 概念
JMM即Java Memory Model,它定义了主存、工作内存抽象概念,底层对应着CPU寄存器、缓存、硬件内存、CPU指令优化等。

1.2 特性

  • 原子性-保证指令不会受到线程上下文切换的影响工
  • 可见性-保证指令不会受cpu缓存的影响
  • 有序性-保证指令不会受cpu指令并行优化的影响

1.3 可见性

Slf4j(topic = "c.Test04")
public class Test04 {
    static Boolean flag = true;
    public static void main(String[] args) throws InterruptedException {
        new Thread(()->{
            while ((flag)){
                //...
            }
        }).start();

        Thread.sleep(1000);
        flag = false;
    }
}

主线程改变了其他线程的结束条件,其他线程无法结束。
原因:JIT即时编译器发现这个flag经常读取,就把它放在工作内存中的高速缓存区,导致每次读取的都是true,外部的共享的主存变量不可见

解决:共享变量添加volatile修饰(每次读取都是在主存读取,保证可见性)
synchronize也能解决可见性问题。

1.4 synchronize和volatile

  • volatile可以保证可见性,可以在本线程内放置指令重排,但不能解决指令交错,即原子性。适用于一个线程写,多个线程读。
  • synchronize能解决可见性和原子性。性能较低。

二、设计模式
1.1 两阶段终止模式优化(采用volatile):

@Slf4j(topic = "c.Test05")
public class Test05 {
    static volatile Boolean flag = true;
    public static void main(String[] args) throws InterruptedException {


        Thread thread = new Thread(() -> {
            while (true) {
                if (!flag)
                {
                    log.debug("退出");
                    break;
                }
                try {
                    log.debug("执行中...");
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        thread.start();
        Thread.sleep(3000);
        stop(thread);

    }

    private static void stop(Thread thread){
        flag = false;
        thread.interrupt();
    }
}

interrupt可以在线程休眠的时候打断进入catch异常后重新循环。

结果:在这里插入图片描述
1.2 犹豫模式(Balking)

@Slf4j(topic = "c.Test06")
public class Test06 {
    private  static Boolean flag = false;
    public static void main(String[] args) {
        Test06 test06 = new Test06();
        test06.start();
        test06.start();
    }

    private  void start(){
        synchronized (this){
            if(flag)
                return;
            new Monitor().start();
            flag = true;
        }
    }
}
@Slf4j(topic = "c.Monitor")
class Monitor extends Thread{
    @Override
    public void run() {
        while(true){
            log.debug("监控中");
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

犹豫模式能够保证只有一个线程启动,因为要读取和操作flag的值,在多线程的情况下必须保证原子性,所以使用synchronize

优化:

@Slf4j(topic = "c.Test06")
public class Test06 {
    public static void main(String[] args) throws InterruptedException {
        Monitor monitor = new Monitor();
        monitor.start1();
        Thread.sleep(2000);
        monitor.start1();
        Thread.sleep(2000);
        monitor.stop1();
        Thread.sleep(2000);
        monitor.stop1();
        Thread.sleep(2000);
        monitor.start1();
    }


}
@Slf4j(topic = "c.Monitor")
class Monitor extends Thread{
    private volatile static Boolean stop = true;
    private volatile   static  Boolean start = false;
    @Override
    public void run() {
        while(!stop){
            log.debug("监控中");
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        log.debug("线程已经停止");
        start = false;

    }

    public void start1(){
        synchronized (this){
            if(start)
                return;
            log.debug("线程已经开启");
            start = true;
            stop = false;
            new Monitor().start();

        }
    }

    public void stop1(){
        stop = true;
    }
}

结果:在这里插入图片描述
stop1和start1都需要用volatile修饰,因为停止的线程是唯一的,所以停止只需要保证可见性。不需要加synchronize来保证原子性。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值