基于CAS的单例模式

常见的单例模式中,能实现懒加载和线程安全的,一般有枚举模式、静态内部类模式、双重检查锁模式,但是枚举模式和静态内部类模式都是利用了类加载机制实现的。但是由于每次获取实例都要先获取锁,所以在性能上没有什么优势,而双重检查锁模式可以避免每次获取实例时都要加锁,性能上相对来说比较有优势。但是双重检查锁模式在创建实例的过程中,还是要对对象上锁的,是否可以无需上锁,就实现线程安全的懒加载的单例模式呢?


基于CAS的单例模式:

import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
import java.util.concurrent.RunnableFuture;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.TimeUnit;

public class Singleton {

    private static final RunnableFuture<Singleton> buildTask = new FutureTask<Singleton>(() -> new Singleton());

    private Singleton() throws InterruptedException, ExecutionException, TimeoutException {
        assert buildTask.get(0, TimeUnit.NANOSECONDS) == null;
    }

    public static Singleton getInstance(long timeout, TimeUnit unit) throws TimeoutException {
        buildTask.run();
        try {
            return buildTask.get(timeout, unit);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.getCause().printStackTrace();
        }
        return null;
    }
}

下面的测试版的CAS单例模式及其测试代码:

import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
import java.util.concurrent.RunnableFuture;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

public class Singleton {

    private static final RunnableFuture<Singleton> buildTask = new FutureTask<Singleton>(() -> new Singleton());
    private static final AtomicInteger number = new AtomicInteger();

    private Singleton() throws InterruptedException, ExecutionException, TimeoutException {
        Thread.sleep(100);
        assert buildTask.get(0, TimeUnit.NANOSECONDS) == null;
        number.addAndGet(1);
    }

    @Override
    public String toString() {
        return "Singleton 个数:" + number.get();
    }

    public static Singleton getInstance(long timeout, TimeUnit unit) throws TimeoutException {
        buildTask.run();
        try {
            return buildTask.get(timeout, unit);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.getCause().printStackTrace();
        }
        return null;
    }
}


import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

public class SingletonTest {

    public static void main(String[] args) {
        final CountDownLatch countDownLatch = new CountDownLatch(1);
        Runnable testCase = () -> {
            try {
                countDownLatch.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            try {
                System.out.println(Singleton.getInstance(3, TimeUnit.SECONDS));
            } catch (TimeoutException e) {
                e.printStackTrace();
            }
        };
        final int COUNT = 1000;
        Thread[] testThreads = new Thread[COUNT];
        for (int i = 0; i < COUNT; i++) {
            testThreads[i] = new Thread(testCase);
            testThreads[i].start();
        }
        countDownLatch.countDown();
        System.out.println("************* 万箭齐发 *************");
    }
}

运行结果最后一行为:

Singleton 个数:1


说明该单例模式是可以实现高并发下的。这种模式相比双重检查锁模式,由于通过CAS避免使用了Java内建锁,程序更加轻量级,同时具有超时中断的功能。但是也有一个小小的缺点,如果实例化的线程出现了异常或者被中断,后面的线程将会抛出同样的ExecutionException异常,而不是重新创建单例对象

猜你喜欢

转载自blog.csdn.net/qq_34011299/article/details/81044858