首页 > 编程语言 >Java线程安全

Java线程安全

时间:2022-11-09 17:04:31浏览次数:48  
标签:Java Monitor Synchronized 安全 000 线程 保证 多线程

线程安全的本质

其实第一张图的例子是有问题的,主内存中的变量是共享的,所有线程都可以访问读写,而线程工作内存又是线程私有的,线程间不可互相访问。那在多线程场景下,图上的线程 A 和线程 B 同时来操做共享内存里的同一个变量,那么主内存内的此变量数据就会被破坏。也就是说主内存内的此变量不是线程安全的。我们来看个代码小例子帮助理解。

public class ThreadDemo {
private int x = 0;

private void count() {
x++;
}

public void runTest() {
new Thread() {
@Override
public void run() {
for (int i = 0; i < 1_000_000; i++) {
count();
}
System.out.println("final x from 1: " + x);
}
}.start();
new Thread() {
@Override
public void run() {
for (int i = 0; i < 1_000_000; i++) {
count();
}
System.out.println("final x from 2: " + x);
}
}.start();
}

public static void main(String[] args) {
new ThreadDemo().runTest();
}
}

示例代码中 runTest 方法2个线程分别执行 1_000_000 次 count() 方法, count() 方法中只执行简单的 x++ 操作,理论上每次执行 runTest 方法应该有一个线程输出的 x 结果应该是2_000_000。但实际的运行结果并非我们所想:

final x from 1: 989840
final x from 2: 1872479

我运行了10次,其中一个线程输出 x 的值为 2_000_000 只出现了2次。

final x from 1: 1000000
final x from 2: 2000000

出现这样的结果的原因也就是我们上面所说的,在多线程环境下,我们主内存的 x 变量的数据被破坏了。我们都知道完成一次 i++ 相当于执行了:

int tmp = x + 1;
x = tmp;

在多线程环境下就会出现在执行完 int tmp = x + 1; 这行代码时就发生了线程切换,当线程再次切回来的时候,x 就会被重复赋值,导致出现上面的运行结果,2个线程都无法输出 2_000_000。

下图描述了示例代码的执行时序:

Java线程安全_多线程

那么 Java 是如何来解决上述问题来保证线程安全,保证共享内存的原子性、可见性、有序性的呢?

线程同步

Java 提供了一系列的关键字和类来保证线程安全。

Synchronized 关键字

Synchronized 作用

保证方法或代码块操作的原子性

Synchronized 保证⽅法内部或代码块内部资源(数据)的互斥访问。即同⼀时间、由同⼀个 Monitor(监视锁) 监视的代码,最多只能有⼀个线程在访问。

话不多说来张动图描述一下 Monitor 工作机制:


Java线程安全_多线程_02



Java线程安全_有序性_03


被 Synchronized 关键字描述的方法或代码块在多线程环境下同一时间只能由一个线程进行访问,在持有当前 Monitor 的线程执行完成之前,其他线程想要调用相关方法就必须进行排队,知道持有持有当前 Monitor 的线程执行结束,释放 Monitor ,下一个线程才可获取 Monitor 执行。

如果存在多个 Monitor 的情况时,多个 Monitor 之间是不互斥的。

多个 Monitor 的情况出现在自定义多个锁分别来描述不同的方法或代码块,Synchronized 在描述代码块时可以指定自定义 Monitor ,默认为 this 即当前类。


Java线程安全_多线程_04



Java线程安全_有序性_05


保证监视资源的可见性

保证多线程环境下对监视资源的数据同步。即任何线程在获取到 Monitor 后的第⼀时 间,会先将共享内存中的数据复制到⾃⼰的缓存中;任何线程在释放 Monitor 的第⼀ 时间,会先将缓存中的数据复制到共享内存中。

保证线程间操作的有序性

Synchronized 的原子性保证了由其描述的方法或代码操作具有有序性,同一时间只能由最多只能有一个线程访问,不会触发 JMM 指令重排机制。

Volatile 关键字

Volatile 作用

保证被 Volatile 关键字描述变量的操作具有可见性和有序性(禁止指令重排)。

注意:

Volatile 只对基本类型 (byte、char、short、int、long、float、double、 boolean) 的赋值 操作和对象的引⽤赋值操作有效。对于 i++ 此类复合操作, Volatile 无法保证其有序性和原子性。相对 Synchronized 来说 Volatile 更加轻量一些。

java.util.concurrent.atomic

包提供了一系列的 AtomicBoolean、AtomicInteger、AtomicLong 等类。使用这些类来声明变量可以保证对其操作具有原子性来保证线程安全。

实现原理上与 Synchronized 使用 Monitor(监视锁)保证资源在多线程环境下阻塞互斥访问不同,java.util.concurrent.atomic 包下的各原子类基于 CAS(CompareAndSwap) 操作原理实现。

CAS 又称无锁操作,一种乐观锁策略,原理就是多线程环境下各线程访问共享变量不会加锁阻塞排队,线程不会被挂起。通俗来讲就是一直循环对比,如果有访问冲突则重试,直到没有冲突为止。

标签:Java,Monitor,Synchronized,安全,000,线程,保证,多线程
From: https://blog.51cto.com/u_14573321/5837348

相关文章

  • Caused by: java.lang.NoClassDefFoundError: net/minidev/asm/FieldFilter 报错的解
    Causedby:org.springframework.beans.factory.BeanCreationException:Errorcreatingbeanwithname'requestMappingHandlerAdapter'definedinclasspathresourc......
  • JavaScript 清空对象的值
      functionclearObj(obj){//判断是不是对象if(!Object.prototype.toString.call(obj)=="[objectObject]"){returnfalse;}for(constkey......
  • javascript基础知识之多维数组扁平化去重排序
    vararr=[1,2,3,4,5,[1,2,6,8]]=>[1,2,3,4,5,6,8]vararr=[1,2,3,4,5,[1,2,6,8]]//js方法实现varflatArr=arr.toString().split(",")//扁平functionun......
  • 2022 年你需要掌握的 7 种关于 JavaScript 的数组方法(下)
    4.Array.forEach()现在这是一个经典。那个 forEach() 方法的工作原理很像常规 for 循环。它在数组上循环并对每个项目执行一个函数。 .forEach() 的第一个参数是......
  • 系统接口安全设计
    1系统接口安全设计接口运行环境为互联网环境,接口安全尤为重要,我们设计三个机制保障接口数据安全。1.1客户端授权机制第三方单位需要向地环总站申请对接appKey客户端系......
  • 20-jmeter-SetUp线程组批量登录并保存token文件
    前言我们在压测接口的时候,需批量获取多个用户登录后返回的token值,那么在setUp线程组可以先批量登录后把token保存到本地csv文件,后面的接口引用这个csv文件的数据参数化。......
  • javascript基础知识之闭包和递归
    一,什么是闭包,会出现什么问题?如何避免?1、函数里面包含的子函数,子函数访问父函数的局部变量2、通过return将子函数暴露在全局作用域,子函数就形成闭包3、通过闭包,父函数的局......
  • 读者-写者(多线程)
    同步互斥问题-读者写者问题之写者优先(一)问题要求1.读者-写者问题的读写操作限制(仅读者优先或写者优先)写-写互斥,即不能有两个写者同时进行写操作。读-写互斥,即不能......
  • Kafka Java客户端Stream API
    KafkaStream概念及初识高层架构图KafkaStream是ApacheKafka从0.10版本引入的一个新Feature,它提供了对存储于Kafka内的数据进行流式处理和分析的功能。简而言之,KafkaStre......
  • ElasticSearch Java API之索引操作
    背景:​​ElasticSearchJava客户端连接ElasticSearch​​以这篇博客为基础​​ElasticSearch:简单介绍以及使用Docker部署ElasticSearch和Kibana​​这篇博客简单部署了E......