1.volatile关键字的原理
是一个变量类型修饰符,被修饰后的变量具有以下特性:
可见性:保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的
实现缓存共享协议;
对于用volatile形容的变量,线程写入本地内存中的同时会将数据立即刷新到主内存中。其他线程读取该变量时,发现被volatile修饰,会将本地变量值置为无效,然后从主内存中读取。
有序性:volatile对写和读加不同的内存屏障,写操作是加一个箭头向上的屏障,阻止上面的代码下来,读变量相反。
禁止进行指令重排序。为提高执行效率,在不影响最终执行结果的前提下,代码在编译成字节码的时候有可能进行指令重新排序,这在单线程情况下是没有问题的,但是在多线程的情况下会出现问题。volatile修饰的变量则可以避免这个问题。
不保证原子性:volatile 只能保证对单次读/写的原子性。i++ 这种操作不能保证原子性。关于volatile 原子性可以理解为把对volatile变量的单个读/写,看成是使用同一个锁对这些单个读/写操作做了同步。
2.Threadlocal的作用?主要方法?实现原理?为什么存在内存泄漏问题?怎么解决?
Threadlocal:
他是一种除了加锁这种同步方法之外的另外一种可以规避出现多线程问题的一种思路。
Threadlocal是JDK包提供的,它提供线程本地变量,如果创建一个Threadlocal变量,那么访问该变量的每个线程都会有这个变量的一个副本。
在实际多线程操作的时候,操作的是自己本地内存中的变量,从而规避了线程安全问题。
主要方法:
get()、set()、remove()
实现原理:
每个线程都有属于自己的ThreadlocalMap,可通过线程获得,这个map存的是以Threadlocal对象为key,以设置的值为value的键值对。
方法都是操作这个map
内存泄漏:
对于线程池里面不会销毁的线程,里面总会存在着《threadlocal,localVarible》的强引用,因为final static修饰的ThreadLocal并不会释放,而ThreadLocalMap对于key虽然是弱引用,但强引用没有释放,弱引用会一直存在,同时创建的localVarible对象也不会释放造成内存泄漏;
ThreadLocalMap使用ThreadLocal的弱引用作为key,如果一个ThreadLocal不存在外部 强引用时,Key(ThreadLocal)势必会被GC回收,这样就会导致ThreadLocalMap中key为null, 而value还存在着强引用,只有thread线程退出以后,value的强引用链条才会断掉。但如果当前线程再迟迟不结束的话,这些key为null的Entry的value就会一直存在一条强引用链:Thread Ref -> Thread -> ThreaLocalMap -> Entry -> value,这就会永远无法回收,造成内存泄漏。
解决办法:
为了避免出现内存泄露的情况, ThreadLocal 提供了一个清除线程中对象的方法, 即remove, 其实内部实现就是调用 ThreadLocalMap 的 remove 方法。
3.单例模式的写法
1.懒汉式
直到使用前才会创建实例,线程不安全,延迟初始化
2.饿汉式
类加载的时候就创建实例 ,线程安全,一开始就初始化
懒汉式中保证线程安全: