走走停停

JAVA 线程安全 非线程安全

2019-08-23  本文已影响0人  一个忙来无聊的人

前段时间写了一个多线程,具体的功能的就是同一个请求对象去做远程调用获取数据,单线程顺序执行没有任何问题,在多线程状况下,直接执行报错,然后想到了前辈说的由于框架中使用的单例比较多,是非线程安全的 -- (当时就有一个疑问,单例模式不是线程安全的么???),使用单例的好处也是显而易见的。在此整理下那些事线程安全的,那些是非线程安全的

线程安全 非线程安全

非线程安全是指多线程操作同一个对象时,可能会出现某些异常问题;而线程安全时多个线程操作同一个对象不会出现问题。
非线程安全 =!不安全,只要在多线程情况下,不操作同一个对象,使用非线程安全是不会出现问题的。

线程安全的与非线程安全的整理汇总

序号 非线程安全类 线程安全类
1 ArrayList 、LinkedList Vector
2 HashSet、TreeSet ConcurrentHashSet
3 HashMap HashTable
4 StringBuilder StringBuffer

collection 知识点

collection 的类继承图


collection类.png
  1. Arraylist 、linkedList 和 vector 区别
  1. HashSet 、LinkedHashSet 和 treeSet 异同
  1. HashTable和hashMap 异同

StringBuilder 和 StringBuffer 异同

spring为啥默认把bean设计成单例

spring 提供了5种scope分别是singleton, prototype, request, session,global session
spring 官方文档介绍如下图 ps 链接:bean socpes

官网截图.png 图一.png
图2.png

单例模式

  1. 一个类能返回对象一个引用(永远是同一个)和一个获得该实例的方法(必须是静态方法,通常使用getInstance这个名称);
  2. 当我们调用这个方法时,如果类持有的引用不为空就返回这个引用,如果类保持的引用为空就创建该类的实例并将实例的引用赋予该类保持的引用;
  3. 同时我们还将该类的构造函数定义为私有方法,这样其他处的代码就无法通过调用该类的构造函数来实例化该类的对象,只有通过该类提供的静态方法来得到该类的唯一实例

饿汉式

// 饿汉式,单例实例在类装载时进行创建
public class SingletonExample {
    // 私有构造函数
    private SingletonExample() { }
    // 单例对象
    private static SingletonExample instance = new SingletonExample();
    // 静态的工厂方法
    public static SingletonExample getInstance() {
        return instance;
    }
}

饿汉式的缺点是把实例对象放到堆内存中,应用加载就创建对应实例,极大的浪费内存

public class Singleton {  
    private static Singleton instance;  
      // 私有构造方法 
    private Singleton (){}  
    // 通过该类提供的静态方法来得到该类唯一实例
    public static Singleton getInstance() {
  // 这个地方如果在多线程情况下,会导致实例化两次  
    if (instance == null) {  
        instance = new Singleton();  
    }  
    return instance;  
    }  
} 

上面单例别不是线程安全的单例,上述单例为懒汉模式,懒汉只得是只有需要对象的时候采取实例化

可以通过加锁的方式来实现线程安全的单例模式

public class Singleton {  
    private static Singleton instance;  
    private Singleton (){}  
    public static synchronized Singleton getInstance() {  
    if (instance == null) {  
        instance = new Singleton();  
    }  
    return instance;  
    }  
}

上述方式虽然能很好进行并发控制,但是效率太低,每次获取都要进行加锁,实际上只需要在第一次获取时加锁即可。
改进 双重锁 线程不安全

public class SingletonExample {
    // 私有构造函数
    private SingletonExample() {}
    // 单例对象
    private static SingletonExample instance = null;
    // 静态的工厂方法
    public static SingletonExample getInstance() {
        // 双重检测机制
        if (instance == null) {         
             // 同步锁
            synchronized (SingletonExample.class) {
                // 双重检测机制
                if (instance == null) {
                    // 下面方法并不是线程安全的
                    instance = new SingletonExample();
                }
            }
        }
        return instance;
    }
}

上述方法在执行到“instance = new SingletonExample();”时,JVM会进行如下操作
1.memory = allocate() 分配对象的内存空间
2.ctorInstance() 初始化对象
3.instance = memory 设置instance指向刚分配的内存
在多线程情况下,JVM和CPU的优化中可能会执行指令重排。上面的第二步和第三步中,由于没有前后必然关系,cpu可能随时调换第二步和第三步的执行顺序。也就是会发生132这种顺序

下面给出常用线程安全单例模式写法
静态内部类

class Singleton {
    public static Singleton instance;
    private static class SingletonWrapper {
        static Singleton instance = new Singleton();
    }
    private Singleton() {
    }
    public static Singleton getInstance() {
      // 类的加载过程是单线程执行的,他的并发安全是JVM保证的
        return SingletonWrapper.instance;
    }
}

枚举

 // 枚举模式:最安全
public class SingletonExample {
    // 私有构造函数
    private SingletonExample() { }
    public static SingletonExample getInstance() {
        return Singleton.INSTANCE.getInstance();
    }
    private enum Singleton {
        INSTANCE;
        private SingletonExample singleton;
        // JVM保证这个方法绝对只调用一次
        Singleton() {
            singleton = new SingletonExample();
        }
        public SingletonExample getInstance() {
            return singleton;
        }
    }
}
上一篇下一篇

猜你喜欢

热点阅读