synchronized和volatile
对于并发编程不能避免的就是内存可见性和线程安全的问题。
比如这个例子:
class unsafeClass {
private int mark;
public int getMark() {
return mark;
}
public void setMark(int mark) {
this.mark = mark;
}
}
在多线程操作和读取mark变量的时候就会遇到问题,线程1读取mark,然后线程2读取mark又写入mark,但是这时线程1操作的mark就已经过期了。这样就会出现脏数据或者其他不可见的问题。
所以使用synchronized和volatile解决这个问题。
package day0128;
/**
* @Author Braylon
* @Date 2020/1/28 9:35
*/
public class JavaAtomicity {
class unsafeClass {
private int mark;
public int getMark() {
return mark;
}
public void setMark(int mark) {
this.mark = mark;
}
}
class safeClass {
private int mark;
public synchronized int getMark() {
return mark;
}
public synchronized void setMark(int mark) {
this.mark = mark;
}
}
class safeClass2 {
private volatile int mark;
public int getMark() {
return mark;
}
public void setMark(int mark) {
this.mark = mark;
}
}
}
synchronized时java提供的一种原子性内置锁,线程的执行代码在进入synchronized代码块前会自动获取内部锁,这时候同步代码块会被阻塞挂起。
但是由于java中的线城市与操作系统的原生线程一一对应 ,所以当阻塞一个线程时需要从用户态切换到内核态,非常的耗费时间。如何避免呢?
这里我们就提到了volatile关键字。
由于使用synchronized锁太笨重了。于是java提供了一种弱的同步,这个关键字确保对一个变量的更新对其他线程马上可见。当一个变量被用volatile声明,线程在写入变量的时候不会把值缓存在寄存器或者别的地方,而是直接写穿,刷新回内存。
两种关键字优缺点比较
相同点是,synchronized和volatile都解决了共享变量的内存可见性问题。
不同点是,前者是独占锁同时只能有一个线程调用get方法,其他调用的线程都会被阻塞。缺点就是增加了切换上下文的开销。
可是volatile虽然没有上下文切换的开销,却不能保证原子性。
怎么选择使用这两种关键字
- 当写入变量值不依赖于变量的当前值的时候。我们可以使用volatile来提高效率
- 当对线程安全要求较高同时需要依赖于变量的当前值的时候,就是用synchronized。
有没有更好的方法呢?
我之前学习的时候就疑惑过,依然这两种方法在实际应用中都不太靠谱,那么现在以先的技术是如何解决这个问题的呢。
我在下一章中将会给大家分享,CAS操作。