如果保证线程安全?synchronized,ReentrantLock,Atomic使用场景

简介: 以下介绍

线程安全解决方案


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的乐观锁,当锁无法取得会开始自旋,直到下一次取得锁。


相关文章
|
3月前
|
存储 安全 Java
synchronized 王的后宫总管,线程是王妃
synchronized 王的后宫总管,线程是王妃
16 0
|
22天前
|
设计模式 安全 Java
Java并发编程实战:使用synchronized关键字实现线程安全
【4月更文挑战第6天】Java中的`synchronized`关键字用于处理多线程并发,确保共享资源的线程安全。它可以修饰方法或代码块,实现互斥访问。当用于方法时,锁定对象实例或类对象;用于代码块时,锁定指定对象。过度使用可能导致性能问题,应注意避免锁持有时间过长、死锁,并考虑使用`java.util.concurrent`包中的高级工具。正确理解和使用`synchronized`是编写线程安全程序的关键。
|
3月前
|
Java Linux 调度
剑指JUC原理-7.线程状态与ReentrantLock(中)
剑指JUC原理-7.线程状态与ReentrantLock
34 0
|
7月前
|
人工智能 安全 JavaScript
3. Java 的线程安全机制之`synchronized`
3. Java 的线程安全机制之`synchronized`
|
4天前
|
安全 Java 编译器
是时候来唠一唠synchronized关键字了,Java多线程的必问考点!
本文简要介绍了Java中的`synchronized`关键字,它是用于保证多线程环境下的同步,解决原子性、可见性和顺序性问题。从JDK1.6开始,synchronized进行了优化,性能得到提升,现在仍可在项目中使用。synchronized有三种用法:修饰实例方法、静态方法和代码块。文章还讨论了synchronized修饰代码块的锁对象、静态与非静态方法调用的互斥性,以及构造方法不能被同步修饰。此外,通过反汇编展示了`synchronized`在方法和代码块上的底层实现,涉及ObjectMonitor和monitorenter/monitorexit指令。
18 0
|
7月前
|
Arthas 测试技术
如何检测由synchronized或Lock引起的线程阻塞问题
如何检测由synchronized或Lock引起的线程阻塞问题
110 0
|
3月前
剑指JUC原理-7.线程状态与ReentrantLock(下)
剑指JUC原理-7.线程状态与ReentrantLock
39 0
|
3月前
剑指JUC原理-7.线程状态与ReentrantLock(上)
剑指JUC原理-7.线程状态与ReentrantLock
36 0
|
3月前
|
安全 Java 调度
线程学习(2)线程创建,等待,安全,synchronized(三)
线程学习(2)线程创建,等待,安全,synchronized(三)
26 0
|
3月前
|
安全 Java 调度
线程学习(2)线程创建,等待,安全,synchronized(二)
线程学习(2)线程创建,等待,安全,synchronized(二)
29 0