/**
* 饿汉式
* 类加载到内容后,就实例化一个实例,
* JVM保证线程安全: JVM保证每一个Class只会load到内存一次,static关键字是在Class load到内存之后马上就行初始化,也保证初始化一次。
* 简单实用,推荐使用
* 唯一缺点:不管用到与否,类装载时就完成实例化
*
*/
public class Mgr01 {
private static final Mgr01 INSTANCE = new Mgr01();
/**
* 注意这里的构造方法使用private修饰,说明该构造方法只有该类自己才能调用。别人无法调用。
*/
private Mgr01(){
}
public static Mgr01 getInstance(){
return INSTANCE;
}
public void m(){
System.out.println("m");
}
public static void main(String[] args) {
Mgr01 m1 = Mgr01.getInstance();
Mgr01 m2 = Mgr01.getInstance();
System.out.println(m1 == m2);
}
}
/**
* loay loading 懒汉模式
* 虽然达到了按需初始化的目的,但却带来线程不安全的问题
* 可以通过synchronized解决,但也带来效率下降
*/
public class Mgr03 {
private static Mgr03 INSTANCE;
private Mgr03(){
}
public static synchronized Mgr03 getInstance(){
if(null == INSTANCE){
try {
Thread.sleep(1);
}catch (InterruptedException e){
e.printStackTrace();
}
INSTANCE = new Mgr03();
}
return INSTANCE;
}
public void m(){
System.out.println("m");
}
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
//lambar表达式是对只能有一个方法的匿名内部类的简化写法
// new Thread(()->{
// System.out.println(Mgr03.getInstance().hashCode());
// }).start();
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Mgr03.getInstance().hashCode());
}
}).start();
}
}
}
public class Mgr06 {
//这里必须要加上volatile Java虚拟机内部优化,语句重排。 如果不加volatile,会在没有初始化的时候就返回这个INSTANCE
private static volatile Mgr06 INSTANCE;
private Mgr06(){
}
public static Mgr06 getInstance(){
//这里的第一次判断是有必要的,大多数情况下,INSTANCE不为空,就直接返回了,不需要上锁了。
if(null == INSTANCE){
//双重检查
synchronized(Mgr06.class){
if(null == INSTANCE) {
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
INSTANCE = new Mgr06();
}
}
}
return INSTANCE;
}
public void m(){
System.out.println("m");
}
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Mgr06.getInstance().hashCode());
}
}).start();
}
}
}
**
* 静态内部类方式--最完美的写法
* JVM保证单例,虚拟机加载一个class的时候,只加载一次。所以里面的Mgr07Holder只加载一次,里面的INSTANCE只加载一次。因此它只有一个对象
* 加载外部类时不会加载内部类,这样可以实现懒加载
*/
public class Mgr07 {
private Mgr07(){
}
//一个类的静态内部类在外面类被加载的时候,里面的静态内部类不会被加载,只有在调用getInstance的时候才会被加载。
private static class Mgr07Holder{
private final static Mgr07 INSTANCE = new Mgr07();
}
public static Mgr07 getInstance(){
return Mgr07Holder.INSTANCE;
}
public void m(){
System.out.println("m111111111");
}
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
new Thread(()->{
System.out.println(Mgr07.getInstance().hashCode());
}).start();
}
}
}
/**
* 不仅可以解决线程同步,还可以防止反序列化
* 做单例的时候为什么要防止反序列化:
* Java的反射是可以通过一个class文件把整个class load到内存,然后new一个实例出来
* 那么前面的写法都可以找到class文件通过反射,然后new一个实例出来, 通过反序列化的方式
* 枚举单例不会被反序列化的原因是枚举类是没有构造方法的(反编译之后枚举是一个abstract class)。
* 枚举单例
*/
public enum Mgr08 {
INSTANCE;
public void m(){
System.out.println("m111111111");
}
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
new Thread(()->{
System.out.println(Mgr08.INSTANCE.hashCode());
}).start();
}
}
}
不用加锁也能实现懒加载
/**
* 线程安全的单例模式
* 阅读文章 http://www.cnblogs.com/xudong-bupt/p/3433643.html
* 更好的方式采用下面的方式(使用内部类的单例模式),既不用加锁,也能实现懒加载,当需要的时候才加载。不用加锁也能实现懒加载
*/
import java.util.Arrays;
public class Singleton {
private Singleton(){
System.out.println("single");
}
private static class Inner{
private static Singleton s = new Singleton(); //内部类里new对象
}
public static Singleton getSingle(){
return Inner.s; //返回的是内部类的静态对象。
}
public static void main(String[] args) {
Thread[] ths = new Thread[200];
for (int i = 0; i < ths.length; i++) {
ths[i] = new Thread(()->{
Singleton.getSingle();
});
}
Arrays.asList(ths).forEach(o->o.start());
}
}