Java线程安全解决方案(synchronized,ReentrantLock,Atomic)
作者:雨developer 发布时间:2022-06-13 12:51:09
线程安全解决方案
synchronized,ReentrantLock,Atomic 使用场景描述
在实际开发过程中如果服务量,请求频繁,就会经常碰见并发,这时候不做处理就会出现很多非法数据。这时候就需要解决线程安全的问题,这时候就可以使用java当中的锁机制。常用有java关键synchronized、可重入锁ReentrantLock,还有并发包下的Atomic 或者Concurrent的安全类型。
synchronized使用场景:
在资源竞争不是很激烈的情况下,偶尔出现并发,需要同步的情形下,synchronized是很合适的。原因在于,编译程序通常会尽可能的进行优化synchronized,另外可读性非常好,不管用没用过5.0多线程包的程序员都能理解。可以多对方法进行加锁(同步方法),也可以对对象进行加锁(同步代码快)。
/**
* synchronized用id
*/
private static volatile Long syncId=0L;
/**
* synchronized方式获取id 同步方法
* @return
*/
public static synchronized Long getSyncId1(){
syncId++;
return syncId;
}
/**
* synchronized方式获取id 同步代码块
* @return
*/
public static Long getSyncId2(){
synchronized (syncId){
syncId++;
return syncId;
}
}
代码可读性强,毕竟是java的关键字,执行优先级高。synchronized关键字一放,就解决线程安全的问题。
但是还有一个问题,当前资源竞争激烈时,对于部分线程迟迟获取不到锁,这时候会出现一个锁升级的过程,且锁升级的过程是不可逆的。当从轻量级锁到偏向锁,再到一个重量级锁。性能会大大的降低。
在资源竞争激烈可以使用其他方式来加锁。
ReentrantLock使用场景:
ReentrantLock提供了多样化的同步,比如有时间限制的同步,可以被Interrupt的同步(synchronized的同步是不能Interrupt的)等。在资源竞争不激烈的情形下,性能稍微比synchronized差点点。但是当同步非常激烈的时候,synchronized的性能一下子能下降好几十倍。而ReentrantLock还能保证正常的性能。
且这个锁可以定义成公平锁还可以定义成非公平锁。
/**
* ReentrantLock用id
*/
private static volatile Long lockId=0L;
/**
* ReentrantLock公平锁
*/
private static final ReentrantLock reentrantLock = new ReentrantLock(true);
/**
* ReentrantLock方式获取id
* @return
*/
public static Long getLockId(){
reentrantLock.lock();
try {
lockId++;
return lockId;
}catch (Exception e){
e.printStackTrace();
return getLockId();
}finally {
reentrantLock.unlock();
}
}
我这里以公平锁作为演示对象。ReentrantLock还可以查看锁的状态, 锁是否被锁上了.
可以查看当前有多少线程再等待锁。但是因为ReentrantLock是悲观锁,加锁时会对资源进行加锁,当读取频繁时性能会不如CAS的乐观锁。所以读取频繁使用乐观锁,写入频繁使用悲观锁。
Atomic或者Concurrent使用场景:
和上面的类似,不激烈情况下,性能比synchronized略逊,而激烈的时候,也能维持常态。激烈的时候,Atomic的性能会优于ReentrantLock一倍左右。但是其有一个缺点,就是只能同步一个值,一段代码中只能出现一个Atomic的变量,多于一个同步无效。因为他不能在多个Atomic之间同步。
/**
* Atomic用id
*/
private static volatile AtomicLong atomicId=new AtomicLong(0L);
/**
* Atomic方式获取id
* @return
*/
public static Long getAtomicId(){
return atomicId.addAndGet(1);
}
对于其他类型的比如和Map和Set可以使用用并发包下的ConcurrentHashMap和ConcurrentHashSet等线程安全的数据类型。
/**
* 线程安全的hashMap
*/
private static ConcurrentHashMap<String,String> hashMap = new ConcurrentHashMap<>();
public static void put(String key,String value){
hashMap.put(key,value);
}
public static String get(String key{
return hashMap.get(key);
}
ConcurrentHashMap内部的实现是CAS的乐观锁,当锁无法取得会开始自旋,直到下一次取得锁。
来源:https://blog.csdn.net/qq_42271561/article/details/108809073
猜你喜欢
- SpringBoot分离打Jar包的两种方式方式一:基于maven-jar-plugin此方式基于这个小伙伴的配置改的:https://ww
- JDK * :利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。CGlib * :利用AS
- 在谈 Volatile 之前,我们先回顾下 Java 内存模型 的三要素:原子性、可见性、有序性,也就是大家常提到的并发编程三要素。并发编程
- webp格式图片webp格式图片是google推出的,相比jpg png有着巨大的优势,同样质量的图片webp格式的图片占用空间更小,在像电
- 1.概述数据库开发一直是JAVA开发的核心之一,作为现在JAVA EE的基石框架,Spring Boot自身携带了一个JDBCTemplat
- 这篇文章主要介绍了JDBC自定义连接池过程详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参
- 在客户机和服务器之间建立单一的双向连接,这就意味着客户只需要发送一个请求到服务端,那么服务端则会进行处理,处理好后则将其返回给客户端,客户端
- Maven中建立的依赖管理方式基本已成为Java语言依赖管理的事实标准,Maven的替代者Gradle也基本沿用了Maven的依赖管理机制。
- Gradle修改默认的Build配置文件名Gradle默认使用build.gradle作为默认的配置文件文件名。如果我们在build.gra
- 这篇文章主要介绍了Java import导入及访问控制权限修饰符过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考
- 什么是JMMJMM全称Java Memory Model, 中文翻译Java内存模型,一种符合内存模型规范的,屏蔽了各种硬件和操作系统的访问
- 1. 每个编译单元(文件)都只能有一个public类。即每个编译单元都有单一的公共接口,用public类实现。此时,mian()就必须要包含
- 前言在java Thread类中,我们会看到interrupt()、interrupted()及isInterrupted(),在大多数情况
- JetBrains 系列产品(IDEA、Pycharm 等)使用本站破解教程 (opens new window),在输入激活码时,部分小伙
- 前言在工作中,比如要实现一个功能,前端传什么参数,后端的controller层中怎么接收参数 ,封装成了什么实体对象,有些参数是在URL上使
- java字段值为null,不返回该字段类上打注解@JsonSerialize(include = JsonSerialize.Inclusi
- Java 8支持动态语言,看到了很酷的Lambda表达式,对一直以静态类型语言自居的Java,让人看到了Java虚拟机可以支持动态语言的目标
- springboot+调用支付宝第三方接口(沙箱环境)大神勿喷!!网址:https://developers.alipay.com/plat
- 最近在研究android自定义控件属性,学到了TypedArray以及attrs。大家也可以结合《理解Android中的自定义属性》这篇文章
- 本文实例讲述了Java设计模式之抽象工厂模式。分享给大家供大家参考,具体如下:具体工厂类:生产创建某一类具体产品对象。抽象产品类可以使用接口