线程之间的数据共享——线程通信(面试重点)、synchronized锁

线程通信


前言


一、线程通信

在这里插入图片描述

1.1JVM下的内存区域划分

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

在这里插入图片描述

1.2 代码中的存储理解

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

二、线程安全

在这里插入图片描述

2.1线程不安全举例


// 演示线程不安全的现象
public class Main {
   
    // 定义一个共享的数据 —— 静态属性的方式来体现
    static int r = 0;

    // 定义加减的次数
    // COUNT 越大,出错的概率越大;COUNT 越小,出错的概率越小
    static final int COUNT = 100;

    // 定义两个线程,分别对 r 进行 加法 + 减法操作
    static class Add extends Thread {
   
        @Override
        public void run() {
   
            for (int i = 0; i < COUNT; i++) {
   
                r++;
            }
        }
    }

    static class Sub extends Thread {
   
        @Override
        public void run() {
   
            for (int i = 0; i < COUNT; i++) {
   
                r--;
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
   
        Add add = new Add();
        add.start();

        Sub sub = new Sub();
        sub.start();

        add.join();
        sub.join();

        // 理论上,r 被加了 COUNT 次,也被减了 COUNT 次
        // 所以,结果应该是 0
        //但是每次输出结果都不同
        System.out.println(r);
    }
}

2.2线程不安全原因(重点知识、面试重点)

2.2.1站在开发者的角度

在这里插入图片描述

2.2.2系统角度解释

在这里插入图片描述

2.2.2.1原子性被破坏

在这里插入图片描述

2.2.2.2 内存可见性,导致某些线程读取“脏数据”

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

2.2.2.3 代码重排序

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

2.3线程安全问题小结

在这里插入图片描述

2.4常见类的线程安全问题

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

三、保护线程安全的措施——锁(lock)

3.1synchronized 锁 /同步锁/monitor锁(监视器锁)

3.1.1语法

在这里插入图片描述
在这里插入图片描述
一个是对当前对象加锁,一个是对当前类加锁

3.1.2什么是锁

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

3.1.3判断一下那些会互斥

在这里插入图片描述

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

3.1.4 JVM中的锁

在这里插入图片描述

3.1.5synchronized 的作用

加锁操作使得互斥(synchronized和我们一起配合(我们需要真正的使用synchronized))
在这里插入图片描述

3.1.5.1保证临界区的原子性

在这里插入图片描述
在这里插入图片描述
看4/24的开头和00:58~1:18
在这里插入图片描述

3.1.5.2内存可见性

在这里插入图片描述

3.1.5.3增加约束代码的重排序

在这里插入图片描述

3.2JUC下的锁

在这里插入图片描述

3.2.1lock()

public class Main1 {
   
    private static final Lock lock = new ReentrantLock();

    static class MyThread extends Thread {
   
        @Override
        public void run() {
   
            lock.lock();
            System.out.println("子线程进入临界区");     // 理论上,这句代码永远到达不了
        }
    }

    public static void main(String[] args) throws InterruptedException {
   
        lock.lock();

        MyThread t = new MyThread();
        t.start();

        t.join();
    }
}

上述代码中的永远加不到锁的理解
在这里插入图片描述
在这里插入图片描述

3.2.2.lockInterruptibly()

使用lock.lockInterruptibly
输出结果:收到停止信号,停止运行

public class Main3 {
   
    private static final Lock lock = new ReentrantLock();
    static class MyThread extends Thread {
   
        @Override
        public void run() {
   
            try {
   
                lock.lockInterruptibly();//允许停止的情况
                System.out.println("子线程进入临界区");     // 理论上,这句代码永远到达不了
            } catch (InterruptedException e) {
   
                System.out.println("收到停止信号,停止运行");
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
   
        lock.lock();
        MyThread t = new MyThread();
        t.start();
        TimeUnit.SECONDS.sleep(2);
        t.interrupt();//直接使用尝试让子线程停下来,但实际会徒劳无功,所以用上面的捕获
        t.join();
    }
}

在这里插入图片描述

3.2.3try.lock()

使用lock.try.lock返回一个boolean,加锁失败后还可以有其他操作,更加灵活
输出:加锁失败

static class MyThread extends Thread {
   
        @Override
        public void run() {
   
            boolean b = lock.tryLock();
            if (b == true) {
   
                // 加锁成功了
                System.out.println("加锁成功");
                System.out.println("子线程进入临界区");     // 理论上,这句代码永远到达不了
            } else {
   
                System
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值