一、synchronized关键字
Synchronized修饰整个方法
java的每个对象都有一个内置锁,当用此关键字修饰方法时, 内置锁会保护整个方法。synchronized关键字也可以修饰静态方法,此时如果调用该静态方法,将会锁住整个类
synchronized修饰具体的代码块
同步是一种高开销的操作,因此应该尽量减少同步的内容。通常没有必要同步整个方法,使用synchronized代码块同步关键代码即可。
二、重入锁
ReentrantLock类是可重入、互斥、实现了Lock接口的锁,它与使用 synchronized 方法和块具有相同的基本行为和语义,并且扩展了其能力。.如果synchronized关键字能满足用户的需求,就用synchronized,因为它能简化代码 。如果需要更高级的功能,就用ReentrantLock类,此时要注意及时释放锁,否则会出现死锁,通常在finally代码释放锁 。
ReentrantLock() : 创建一个ReentrantLock实例
lock() : 获得锁
unlock() : 释放锁
三、volatile关键字
1.volatile 是变量修饰符,其修饰的变量具有可见性。
可见性就是说一旦某个线程修改了被 volatile 修饰的变量,它会保证修改的值会立即被更新到主存,当有其他线程需要读取的时候,可以立即获取修改之后的值。
在Java中为了加快程序的运行效率,对一些变量的操作通常是在该线程的寄存器或CPU缓存上进行的,之后才会同步到主存中,而加了 volatile 修饰符的变量则是直接读写主存。
2. volatile可以禁止指令重排
指令重排是指编译器或者CPU为了提高程序的运行效率,可能会对输入的代码进行优化,它不保证各个语句的执行顺序同代码中的顺序一致,但是它会保证程序最终执行结果和代码中的执行结果是一致的,应用条件是单线程条件,对于并发多线程的场景下,指令重排会产生不确定的结果。
volatile不会提供任何原子操作,它也不能用来修饰 final 类型的变量
四、使用局部变量实现线程同步
如果使用ThreadLocal管理变量,则每一个使用该变量的线程都获得该变量的副本,副本之间相互独立,这样每一个线程都可以随意修改自己的变量副本,而不会对其他线程产生影响。
ThreadLocal() : 创建一个线程本地变量
get() : 返回此线程局部变量的当前线程副本中的值
initialValue() : 返回此线程局部变量的当前线程的"初始值"
set(T value) : 将此线程局部变量的当前线程副本中的值设置为value