单例模式原理
通过私有化构造函数,通过静态公共方法/枚举方式返回对象。
注意:确保实例只有一个,尤其是多线程环境。
饿汉式
/**
* 饿汉式(早new对象准备好,担心饿死)
* @author:eddyjoe
* @date:2019/3/7
*/
public class HungrySingle {
/**为严谨,加final指向的引用不能再做修改*/
private static final HungrySingle hungrySingle = new HungrySingle();
private HungrySingle(){
}
public static HungrySingle getHungrySingle(){
return hungrySingle;
}
}
懒汉式
/**
* 懒汉式(因为懒,需要时再new),线程不安全
* @author:eddyjoe
* @date:2019/3/7
*/
public class LazySingle {
private static LazySingle lazySingle;
private LazySingle(){
}
public static LazySingle getLazySingle(){
if(null == lazySingle){
lazySingle = new LazySingle();
}
return lazySingle;
}
}
双重锁式
/**
* 双重锁式【推荐】,针对懒汉式优化了线程安全
* 将同步内容下放到if内部,提高了执行的效率,不必每次获取对象时都进行同步,
* 只有第一次才加锁同步,创建了以后就不用了。
* @author:eddyjoe
* @date:2019/3/7
*/
public class DoubleLockSingle {
/**也可以加上volatile,各个线程之间使之内存可见,禁止指令重排,造成的bug
* 多线程主要围绕可见性和原子性两个特性而展开,使用volatile关键字修饰的变量,保证了其在多线程之间的可见性*/
// private static volatile DoubleLockSingle doubleLockSingle;
private static DoubleLockSingle doubleLockSingle;
private DoubleLockSingle(){
}
public static DoubleLockSingle getDoubleLockSingle(){
if(null == doubleLockSingle){
//一开始对象为空可能有多个线程同时进来,12345...等线程1创建对象后,后面的线程就进不来了
//注意静态方法同步不能用this
System.out.println(Thread.currentThread().getName()+":Ready to grab");
synchronized (DoubleLockSingle.class){
//线程23456在傻傻排队拿锁,拿到锁也没它什么事了
System.out.println(Thread.currentThread().getName()+":Line up to take the lock");
if(null == doubleLockSingle){
System.out.println(Thread.currentThread().getName()+":I Won");
//线程1在建对象,线程2在候着,等线程1执行完到线程2拿到锁,判断对象不为空这是也进不来了
doubleLockSingle = new DoubleLockSingle();
}
}
}
return doubleLockSingle;
}
}
测一下
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* @author:eddyjoe
* @date:2019/3/7
*/
public class Clint {
public static void main(String[] args) {
int count = 1500;//并发数
CyclicBarrier cyclicBarrier = new CyclicBarrier(count);
ExecutorService executorService = Executors.newFixedThreadPool(count);
long now = System.currentTimeMillis();
for (int i = 0; i < count; i++)
executorService.execute(new Clint().new Task(cyclicBarrier));
executorService.shutdown();
while (!executorService.isTerminated()) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
long end = System.currentTimeMillis();
System.out.println("All is finished!---------"+(end-now));
}
public class Task implements Runnable {
private CyclicBarrier cyclicBarrier;
public Task(CyclicBarrier cyclicBarrier) {
this.cyclicBarrier = cyclicBarrier;
}
@Override
public void run() {
try {
// 等待所有任务准备就绪
cyclicBarrier.await();
// 测试内容
DoubleLockSingle single = DoubleLockSingle.getDoubleLockSingle();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
只打印一行 I Won 说明实现了线程安全
内部类式
/**
* 静态内部类式【推荐】
* 通过类加载机制来保证只创建一个instance实例。和饿汉模式一样,不存在多线程并发的问题。
* 只要应用中不使用内部类,JVM就不会去加载这个单例类,也就不会创建单例对象,从而实现懒汉式的延迟加载。
* 可以同时保证延迟加载和线程安全。
* @author:eddyjoe
* @date:2019/3/7
*/
public class InternalClassSingle {
private static class InternalClass{
public static InternalClassSingle internalClassSingle = new InternalClassSingle();
}
private InternalClassSingle(){
}
public static InternalClassSingle getInstance(){
return InternalClass.internalClassSingle;
}
}
枚举式(这种方式不常用,略)