什么是单例?
一个类仅有一个实例 解决了全局使用的类频繁的创建与销毁
饿汉式
public class SingletonOne {
// 私有化构造方法
private SingletonOne(){};
// 性能不高 在程序启动的时候 就创建好了对象 对象一直在内存中
// 线程是安全的
public static final SingletonOne hungry = new SingletonOne();
public static SingletonOne getHungry(){
return hungry;
}
}
测试
public class Test {
public static void main(String[] args) throws IOException, ClassNotFoundException {
// 200 个线程并发访问
int count = 200;
CountDownLatch latch = new CountDownLatch(count);
for (int i = 0; i < count; i++){
new Thread(){
@Override
public void run() {
Object register = SingletonOne.getHungry();
System.out.println(System.currentTimeMillis() + ":" + register);
try {
latch.await(); // 所有线程堵塞在这里 等到停止循环的时候 全部放行
}catch (Exception e){
e.printStackTrace();
}
}
}.start();
latch.countDown();
}
}
可以看出饿汉式都是同一个对象 线程是安全的
懒汉式
// 性能比饿汉式高 需要的时候才创建 延时加载
// 多个线程并发访问 线程不安全 解决方法就是加锁 加锁后性能低
public class SingletionTwo {
private SingletionTwo(){
}
public static SingletionTwo lazyLoad = null;
public static SingletionTwo getLazyLoad(){
if(lazyLoad == null){
lazyLoad = new SingletionTwo();
}
return lazyLoad;
}
}
还是用上面的测试方法
可以看出懒汉式并不能保证每次创建同一个实例 线程并发访问的时候 会创建新的对象 解决方法只能加锁
// 性能比饿汉式高 需要的时候才创建 延时加载
// 多个线程并发访问 线程不安全 解决方法就是加锁 加锁后性能低
public class SingletionTwo {
private SingletionTwo(){
}
public static SingletionTwo lazyLoad = null;
// 这里加了锁
public static synchronized SingletionTwo getLazyLoad(){
if(lazyLoad == null){
lazyLoad = new SingletionTwo();
}
return lazyLoad;
}
}
注册登记式
// 注册登记式
// 每使用一次 都往Map中添加 下次取对象的时候 先从Map中取 以保证获取的都是同一个对象
// 注册登记式 每使用一次 都往Map中添加 下次取对象的时候 先从Map中取 以保证获取的都是同一个对象
public class Register {
private static Map<String,Object> map = new ConcurrentHashMap<>();
private Register(){};
public static Object getRegister(String name){
if(map.containsKey(name)){
return map.get(name);
}else{
Object o = null;
try {
o = Class.forName(name).newInstance();
map.put(name,o);
}catch (Exception e){
e.printStackTrace();
}
return o;
}
}
}
先随便创建一个对象
public class Tgg {
}
测试代码
public class Test {
public static void main(String[] args) throws IOException, ClassNotFoundException {
int count = 200;
CountDownLatch latch = new CountDownLatch(count);
for (int i = 0; i < count; i++){
new Thread(){
@Override
public void run() {
Object register = Register.getRegister("Singe.Tgg"); // 刚刚创建类的路径
System.out.println(System.currentTimeMillis() + ":" + register);
try {
latch.await(); // 所有线程堵塞在这里 等到停止循环的时候 全部放行
}catch (Exception e){
e.printStackTrace();
}
}
}.start();
latch.countDown();
}
可以看出 这个还离谱一些 有得甚至没拿到值 解决这个问题只能加锁
// 注册登记式 每使用一次 都往Map中添加 下次取对象的时候 先从Map中取 以保证获取的都是同一个对象
public class Register {
private static Map<String,Object> map = new ConcurrentHashMap<>();
private Register(){};
// 这里加了锁
public static synchronized Object getRegister(String name){
if(map.containsKey(name)){
return map.get(name);
}else{
Object o = null;
try {
o = Class.forName(name).newInstance();
map.put(name,o);
}catch (Exception e){
e.printStackTrace();
}
return o;
}
}
}
执行完测试类后 可以看到创建的都是同一个对象
内部类单例
// 在外部类调用时候 内部类才会被加载 在方法之前就初始化
public class SingletionThree {
private static boolean init = false;
private SingletionThree(){
synchronized (SingletionThree.class){
if(init == false){
init = !init;
}else{
throw new RuntimeException("单例被侵犯");
}
}
}
public static final SingletionThree getInstance(){return LayHodler.LAZY;}
private static class LayHodler{
private static final SingletionThree LAZY = new SingletionThree();
}
}
内部类单例是线程安全的 因为只有在外部类被调用的时候内部类才会被加载 不过通过反射可以修改构造方法 创建新的实例
序列化与反序列化保证单例
// 序列化与反序列化保证单例 重写readResolve 方法
public class Seriable implements Serializable {
public final static Seriable INSTANCE = new Seriable();
private Seriable(){};
public static Seriable getInstance(){
return INSTANCE;
}
private Object readResolve(){
return INSTANCE;
}
}
public class Test {
public static void main(String[] args) throws IOException, ClassNotFoundException {
Seriable seriable = null;
Seriable seriable1 = Seriable.getInstance();
FileOutputStream outputStream = new FileOutputStream("F://Seriable.obj");
ObjectOutput objectOutput = new ObjectOutputStream(outputStream);
objectOutput.writeObject(seriable1);
objectOutput.flush();
objectOutput.close();
FileInputStream fileInputStream = new FileInputStream("F://Seriable.obj");
ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);
Seriable seriable2 = (Seriable) objectInputStream.readObject();
objectInputStream.close();
System.out.println(seriable1);
System.out.println(seriable2);
System.out.println(seriable2 == seriable1);*/
}
}
来源:CSDN
作者:qq_42913304
链接:https://blog.csdn.net/qq_42913304/article/details/103943241