线程安全问题和锁 - 线程安全问题

变量的线程安全问题

在这里插入图片描述

临界区: 多个线程对共享资源的读写操作时发生指令交错,就会出现问题,一段代码如果出现对共享资源的多线程读写操作,这段代码称为临界区

竞态条件: 多个线程在临界区内执行,由于代码的执行序列不同而导致结果无法预测,称之为发生了竞态条件

1 成员变量和静态变量是否是线程安全的

  • 如果没有共享,则是线程安全的

  • 如果被共享了,根据他们的状态是否能够改变,分为两种状态
    ① 只有读操作,则线程安全

② 有读写操作则这段代码是临界区,需要考虑线程安全

2 局部变量

  • 局部变量是线程安全的

  • 局部变量引用的对象不一定
    ① 如果该对象没有逃离方法的作用范围,则是线程安全的

② 如果逃离了方法的作用范围,则需要考虑线程安全

class ThreadSafe {
    public final void method1(int loopNumber) {
        ArrayList<String> list = new ArrayList<>();
        for (int i = 0; i < loopNumber; i++) {
            method2(list);
            method3(list);
        }
    }

    public void method2(ArrayList<String> list) {
        list.add("1");
    }

    public void method3(ArrayList<String> list) {
        list.remove(0);
    }
}

class ThreadSafeSubClass extends ThreadSafe {
    @Override
    public void method3(ArrayList<String> list) {
        new Thread(() -> {
            list.remove(0);
        }).start();
    }
}

public class Test {
    public static void main(String[] args) {
        ThreadSafeSubClass t = new ThreadSafeSubClass();
        for(int i = 0; i<100; i++){
            new Thread(()->{
               t.method1(100);
            }).start();
        }
    }
}

我们对调用子类的method1方法,但是子类重写了method3方法,在method3新起了线程,也就是说我们把成员变量的ArrayList的引用给暴露给了另外一个线程,出现了线程安全问题

这也就给了我们一个启示:
方法的访问修饰符实际上可以保护我们的线程安全问题

3 几个共享问题

在这里插入图片描述
多个线程会共享使用其中的userService,在service里面,有对count的改变操作,也就是临界区,所以会有线程安全问题

在这里插入图片描述
也会有线程安全问题

4. 从Java内存模型的方面分析一下线程安全问题

4.1 先来看一下jvm内存模型

在这里插入图片描述
可以看到类的class文件会被类加载器加载到jvm中,再由执行引擎执行,而对于jvm的内存结构如下
在这里插入图片描述
PC程序计数器

  • 每个线程对应有一个程序计数器。
  • 各线程的程序计数器是线程私有的,互不影响,是线程安全的。
  • 程序计数器记录线程正在执行的内存地址,以便被中断线程恢复执行时再次按照中断时的指令地址继续执行

Java栈JavaStack(虚拟机栈JVM Stack)

  • 每个线程会对应一个Java栈,每个Java栈由若干栈帧组成,每个方法对应一个栈帧

  • 栈帧在方法运行时,创建并入栈;方法执行完,该栈帧弹出栈帧中的元素作为该方法返回值,该栈帧被清除;

  • 栈顶的栈帧叫活动栈,表示当前执行的方法,才可以被CPU执行;

  • 线程请求的栈深度大于虚拟机所允许的深度,将抛出StackOverflowError异常;

  • 栈扩展时无法申请到足够的内存,就会抛出OutOfMemoryError异常;

方法区

  • 方法区是Java堆的永久区(PermanetGeneration

  • 方法区存放了要加载的类的信息(名称、修饰符等)、类中的静态常量、类中定义为final类型的常量·、类中的Field信息类中的方法信息`

  • 方法区是被Java线程共享的

  • 方法区要使用的内存超过其允许的大小时,会抛出OutOfMemoryError: PremGen space的错误信息。

常量池

  • 常量池是方法区的一部分

本地方法栈

  • 本地方法栈和Java栈所发挥的作用非常相似,区别不过是Java栈为JVM执行Java方法服务,而本地方法栈为JVM执行Native方法服务
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值