不使用synchronized和lock,如何实现一个线程安全的单例?(二)

195 篇文章 2 订阅
149 篇文章 2 订阅
本文探讨了不使用synchronized和lock的线程安全单例模式,通过介绍乐观锁技术CAS,展示了如何利用AtomicReference在Java中实现并发下的单例。通过避免传统锁带来的阻塞,CAS利用忙等机制提高性能,但需注意其可能导致CPU开销。
摘要由CSDN通过智能技术生成

如果不那么吹毛求疵的话,可以使用枚举、静态内部类以及饿汉模式来实现单例模式。见:不使用synchronized和lock,如何实现一个线程安全的单例?

但是,上面这几种方法其实底层也都用到了synchronized,那么有没有什么办法可以不使用synchronizedlock,如何实现一个线程安全的单例?

答案是有的,那就是CAS。关于CAS,我博客中专门有一篇文章介绍过他,很多乐观锁都是基于CAS实现的。这里简单介绍一下,详细内容见 乐观锁的一种实现方式——CAS

CAS是项乐观锁技术,当多个线程尝试使用CAS同时更新同一个变量时,只有其中一个线程能更新变量的值,而其它线程都失败,失败的线程并不会被挂起,而是被告知这次竞争中失败,并可以再次尝试。

在JDK1.5 中新增java.util.concurrent(J.U.C)就是建立在CAS之上的。相对于对于synchronized这种阻塞算法,CAS是非阻塞算法的一种常见实现。所以J.U.C在性能上有了很大的提升。

借助CAS(AtomicReference)实现单例模式:

public class Singleton {
    private static final AtomicReference<Singleton> INSTANCE = new AtomicReference<Singleton>(); 

    private Singleton() {}
//java学习交流:737251827  进入可领取学习资源及对十年开发经验大佬提问,免费解答!
    public static Singleton getInstance() {
        for (;;) {
            Singleton singleton = INSTANCE.get();
            if (null != singleton) {
                return singleton;
            }

            singleton = new Singleton();
            if (INSTANCE.compareAndSet(null, singleton)) {
                return singleton;
            }
        }
    }
}

用CAS的好处在于不需要使用传统的锁机制来保证线程安全,CAS是一种基于忙等待的算法,依赖底层硬件的实现,相对于锁它没有线程切换和阻塞的额外消耗,可以支持较大的并行度。
CAS的一个重要缺点在于如果忙等待一直执行不成功(一直在死循环中),会对CPU造成较大的执行开销。

(全文完)

下面是一个示例,演示了如何在Java实现并发代码,而不使用Atomic、synchronizedLock。 ```java public class NoLockConcurrencyExample { private static volatile int counter = 0; public static void main(String[] args) throws InterruptedException { Thread t1 = new Thread(new IncrementTask()); Thread t2 = new Thread(new IncrementTask()); t1.start(); t2.start(); t1.join(); t2.join(); System.out.println("Counter value: " + counter); } static class IncrementTask implements Runnable { @Override public void run() { for (int i = 0; i < 10000; i++) { incrementCounter(); } } } private static void incrementCounter() { int oldValue; int newValue; do { oldValue = counter; newValue = oldValue + 1; } while (!compareAndSet(oldValue, newValue)); } private static boolean compareAndSet(int expect, int update) { if (counter == expect) { counter = update; return true; } return false; } } ``` 在这个示例中,我们使用一个volatile修饰的counter变量来存储计数器的值。volatile关键字确保了多线程之间的可见性,即一个线程修改了counter的值,其他线程能够立即看到最新的值。 在IncrementTask任务中,我们使用一个自定义的incrementCounter方法来递增计数器的值。该方法使用一个自旋的compare-and-set循环,不断尝试修改counter的值直到成功。compareAndSet方法通过比较当前counter的值和期望的值,如果相等则更新为新值。 这种实现方式利用了volatile关键字的可见性和CAS(Compare-and-Swap)操作的原子性,避免了使用显式,从而实现了无并发。 需要注意的是,这个示例只是一个简单的演示,并不适用于所有场景。在实际开发中,需要仔细评估使用并发的适用性和性能,并根据具体需求进行选择。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值