线程安全问题与volatile字段
线程安全问题
/** * 出现有些没加入list的原因:线程一拿走了这个list当时里面有10个数据,添加一个变成11个 * 线程二同时拿走了这个list,当时里面有10个数据,添加一个变成11个(但是正常应该是12个,也就是线程1添加的那个数据没有了) */ public class Ticket implements Runnable{ private List<String> list = new ArrayList<>(); public void run(){ for(int i=0;i<300;i++){ try { // 调用Thread类的sleep方法,休眠50ms,由于父接口没有throws异常,so我们只能用try...catch Thread.sleep(50); } catch (InterruptedException e) { e.printStackTrace(); } list.add("xxx,"); System.out.println(list.size()); } } }
结论:多线程操作同一个全局变量,加上线程锁吧
volatile字段
volatile字段的作用:往常线程都是将某个变量复制一份到自己的线程中,经过volatile字段修饰后,就统一的从内存中读取
原理:volatile本质是在告诉jvm当前变量在寄存器(工作内存)中的值是不确定的,需要从主存中读取;
Volatile 变量具有 synchronized
的可见性特性,但是不具备原子性。这就是说线程能够自动发现 volatile 变量的最新值。
- volatile 变量不能用作线程安全计数器。虽然增量操作(
x++
)看上去类似一个单独操作,实际上它是一个由(读取-修改-写入)操作序列组成的组合操作,必须以原子方式执行,而 volatile 不能提供必须的原子特性。
用处:如果读操作远远大于写操作,volatile 变量还可以提供优于锁的性能优势。
两个字段的区别
- volatile本质是在告诉jvm当前变量在寄存器(工作内存)中的值是不确定的,需要从主存中读取; synchronized则是锁定当前变量,只有当前线程可以访问该变量,其他线程被阻塞住。
- volatile仅能使用在变量级别;synchronized则可以使用在变量、方法、和类级别的
- volatile仅能实现变量的修改可见性,不能保证原子性;而synchronized则可以保证变量的修改可见性和原子性
- volatile不会造成线程的阻塞;synchronized可能会造成线程的阻塞。
- volatile标记的变量不会被编译器优化;synchronized标记的变量可以被编译器优化