1、并发关键字
synchronized
在java中,synchronized关键字是用来控制线程同步的,就是在多线程的环境下,控制synchronized代码段不被多个线程同时执行。synchronize可以修饰类,方法,代码块,在早起版本,属于重量级锁。
2、synchronized关键字的三种使用方法
- synchronized关键字加到实例方法上是给对象加锁。
- synchronized关键字加到static静态方法是给class类加锁。
- synchronized关键字修饰代码块上,就可以给对象synchronized(this|object)或者当前class类加锁synchronized(类.class)
- 单例模式就是用到synchronize,双重检验锁方式
3、构造方法可以使用synchronize关键字修饰吗
不可以,因为构造方法本来就属于线程安全的,不存在同步的构造方法一说。
4、synchronize关键字的底层原理
- synchronize同步语句块的实现使用的是monitorenter和monitorexit指令,其中monitorenter指令指向同步代码块的开始位置,monitorexit指令指向同步代码块结束的位置。
- 当执行monitorenter指令时,线程试图获取锁,也就是获取对象监视器monitor的持有权。
5、synchronize可重入的原理
重入锁是指线程获取了锁之后,该线程可以继续获得该锁。底层会有一个计数器,第一次获得锁,计数器从0变为1,继续获得该锁,计数器就加1.释放锁时,计数器减1,这样当计数器为0时,该锁就没有被任何线程所持有,其他线程可以竞争获取锁。
6、说说JDK1.6之后synchronized关键字底层做了哪些优化
- JDK1.6对锁的实现做了大量的优化,如偏向锁、轻量级锁、自旋锁、适应性自旋锁、锁消除、锁粗化等技术减少锁操作的开销。
- 锁主要存在4种状态:无锁状态、偏向锁状态、轻量级锁、重量级锁,随着竞争的激烈锁会不断升级,锁可以升级但不可以降级,这种策略是为了提高获得锁和释放锁的效率。
7、谈谈synchronized和reentrantlock的区别
(1)两者都是可重入锁
(2)synchronize是依赖JVM实现的,reentrantlock是基于JDK实现的
(3)reentrantlock加了一些高级功能
- 等待可中断:当持有锁的线程长期不释放锁的时候,正在等待的线程可以放弃等待,转而处理其他事情。
- 可实现公平锁:reentrantlock可以指定是公平锁还是非公平锁。公平锁是指先等待的线程先获得锁。
- 锁可以绑定多个条件:一个reentrantlock可以绑定多个condition对象。
8、简述下JVM的内存模型JMM
(1)JVM中存在一个主内存(Main memory),java中所有变量都保存在主内存中,所有线程共享。而每个线程都存在自己的工作内存,也就是本地内存。工作内存中保存的是主内存中某些变量的拷贝,线程对所有变量的操作不是发生在主存中,而是在工作内存中,而线程之间是不能直接访问的,变量在程序中的传递,是依赖主存完成的。这就导致线程本地内存和主内存数据不一致的情况,即可见性。
volatile关键字底层:volatile关键字修饰的变量在汇编阶段,会多出一个lock前缀指令,这会导致当前处理器缓存的数据写回到主存中,且让其他改数据的处理器缓存失效,这就保障了可见性。volatile修饰的变量,虚拟机会使用内存屏障禁止指令重排序,保障它的有序性。
13、JMM内存模型的三个特性
(1)原子性(用synchronized保证):一个操作或者多次操作,要么所有的操作全部被执行,且不会被任何因素的干扰所中断,要么就都不执行。
(2)可见性:当一个线程对共享变量进行修改之后,另外的线程可以立即看到修改后的最新值。JMM内存模型是靠修改后的变量值同步回到主存中,在线程读取前从主存中刷新变量值。volatile关键字保证可见性。synchronize也可以
(3)有序性:程序执行的顺序会按照代码的先后顺序执行,volatile关键字可以禁止指令重排序。
14、说说synchronize关键字和volatile关键字的区别
synchronize关键字和volatile关键字是互补的存在
(1)volatile关键字是线程同步的轻量级实现,因此它的性能肯定比synchronize关键字要好,但是volatile关键字只能修饰变量,而synchronize关键字还可以修饰方法和代码块。
(2)volatile关键字可以保证数据的可见性,但是不能保证原子性。
(3)volatile关键字主要用于保证线程之间的可见性,而synchronize关键字解决的是多个线程之间访问资源的同步性。
15、ThreadLocal的基本概念
ThreadLocal是线程本地存储,ThreadLocal类主要解决的是让每个线程绑定自己的值,当创建了一个ThreadLocal变量,那么访问这个变量的每个线程都会有这个变量的本地副本。可以用set或者get方法,来获取默认值或者把值更改为当前线程所村的副本的值,从而避免了线程安全问题。
16、ThreadLocal的底层原理
ThreadLocal底层是靠ThreadLocalMap是实现的。每个线程都有一个ThreadLocalMap,而ThreadLocalMap可以存储以ThreadLocal为key,Object对象为value的键值对。
16.1、内存泄漏
ThreadLocal对象在使用完之后,就要进行回收。ThreadLocalMap的entry节点中的key指向ThreadLocal是弱引用,但是value是强引用,所以进行垃圾回收的时候,key会被清理,但是value不会,所以ThreadLocalMap中就会出现key为null的Entry,就会发生内存泄漏。所以使用完ThreadLocal()方法之后,要调用remove()方法清除value。
18、java中有哪些方法可以保证线程安全
- 使用synchronized/reentrantlock加锁
- 使用volatile声明变量,轻量级同步,不能保证原子性。
- 使用线程安全类,比如原子类AtomicXXX等
- 使用线程安全的集合容器,比如:CopyOnWriteArrayList/ConcurrenHashMap