1.Synchronized
Synchronized关键字保证了数据读写一致和可见性等问题,但是他是一种阻塞的线程控制方法,在关键字使用期间,所有其他线程不能使用此变量。(同步机制采用了“以时间换空间”的方式)
修饰一个类
class ClassName {
public void method() {
synchronized(ClassName.class) {
// todo
}
}
}
修饰一个方法
public synchronized void method()
{
// todo
}
修饰一个代码块
public void method()
{
synchronized(this){
......
}
}
修饰一个静态的方法
public synchronized static void method() {
// todo
}
2.volatile
volatile如何实现可见性?
volatile变量每次被线程访问时,都强迫线程从主内存中重读该变量的最新值,而当该变量发生修改变化时,也会强迫线程将最新的值刷新回主内存中。这样一来,不同的线程都能及时的看到该变量的最新值。
但是volatile不能保证变量更改的原子性:
比 如number++,这个操作实际上是三个操作的集合(读取number,number加1,将新的值写回number),volatile只能保证每一 步的操作对所有线程是可见的,但是假如两个线程都需要执行number++,那么这一共6个操作集合,之间是可能会交叉执行的,那么最后导致number 的结果可能会不是所期望的。
所以对于number++这种非原子性操作,推荐用synchronized:
synchronized(this){
number++;
}
3.synchronized和volatile比较
-
volatile不需要同步操作,所以效率更高,不会阻塞线程,但是适用情况比较窄
-
volatile读变量相当于加锁(即进入synchronized代码块),而写变量相当于解锁(退出synchronized代码块)
-
synchronized既能保证共享变量可见性,也可以保证锁内操作的原子性;volatile只能保证可见性
4.ThreadLocal
ThreadLocal不是为了解决多线程访问共享变量,而是为每个线程创建一个单独的变量副本,提供了保持对象的方法和避免参数传递的复杂性。
顾名思义它是local variable(线程局部变量)。它的功用非常简单,就是为每一个使用该变量的线程都提供一个变量值的副本,是每一个线程都可以独立地改变自己的副本,而不会和其它线程的副本冲突。从线程的角度看,就好像每一个线程都完全拥有该变量。(ThreadLocal采用了“以空间换时间”的方式)