volatile关键字、ThreadLocal关键字、单例模式

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.饿汉式

                类加载的时候就创建实例 ,线程安全,一开始就初始化

        懒汉式中保证线程安全:

                在 getInstance() 方法添加 synchronized 关键字,可以解决线程安全问。
        双重检查模式:
                第一次校验:
                由于单例模式只需要创建一次实例,如果后面再次调用getInstance方法时,则直接返 回之前创建的实例, 因此大部分时间不需要执行同步方法里面的代码,大大提高了性能。 如果不加第一次校验的话,那跟上面的懒汉模式没什么区别,每次都要去竞争锁。
                
                第二次校验:
                如果没有第二次校验,假设线程t1执行了第一次校验后,判断为null,这时t2也获取了 CPU执行权,也执行了第一次校验,判断也为null。 接下来t2获得锁,创建实例。 这时t1又获得CPU执行权,由于之前已经进行了第一次校验,结果为null(不会再次判 断),获得锁后,直接创建实例。 结果就会导致创建多个实例。所以需要在同步代码里面进行第二次校验,如果实例为 空,则进行创建。
                

        

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值