2019独角兽企业重金招聘Python工程师标准>>>
volatile和synchronized比较
1. volatile只能保证共享变量的可见性,不能保证共享变量操作的原子性;volatile不需要锁,比synchronized轻量级,不会阻塞线程。
2.从内存可见性来说,volatile读相当于对共享变量加锁,写相当于对共享变量解锁。
3.synchronized既可以保证共享变量的可见性,也可以保证原子性。
4.除了用synchronized,也可以用 java.util.concurrent.locks.Lock。
几个概念
1.共享变量: 一个变量如果在多个线程中都存有副本,那么这个变量就是这几个线程的共享变量。共享变量的访问权限用private修饰。
2.可见性:一个线程对共享变量值的修改,能够及时地被其它线程看到。volatile修饰的变量不允许线程内部缓存和重排序,即直接修改内存,所以对其他线程是可见的。比如 volatile int a = 0;之后有一个操作 a++;这个变量a具有可见性,但是a++ 依然是一个非原子操作,依然可能导致线程不安全。
3.原子性:不可分割的操作,可以保证线程运行的安全。
volatile导致线程不安全的示例
运行程序几次程序,可以看出volatileNumber最终可能并不是200,线程不安全。
public class VolatileExample {
// 线程共享变量须定义成private访问权限,volatile不能保证原子性
private volatile int volatileNumber = 0;
public static void main(String[] args) {
final VolatileExample ve = new VolatileExample();
for (int i = 0; i < 200; i++) {
new Thread(new Runnable() {
@Override
public void run() {
ve.increase();
}
}).start();
}
// 如果还有子线程在运行,主线程就让出资源。
// 等所有的子线程运行结束,主线程继续执行
while (Thread.activeCount() > 1) {
Thread.yield();
}
// 经过500个循环,发现volatileNumber输出结果可能不是200
System.out.println("num is:\t" + ve.getVolatileNumber());
}
/**
* 对volatileNumber进行++操作
*/
public void increase() {
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.volatileNumber++;
}
public int getVolatileNumber() {
return this.volatileNumber;
}
synchronized可以保证原子性
public class SynExample {
// 线程共享变量须定义成private访问权限
private int synNumber = 0;
public static void main(String[] args) {
final SynExample example = new VolatileExample();
for (int i = 0; i < 200; i++) {
new Thread(new Runnable() {
@Override
public void run() {
example.increaseSynNumber();
}
}).start();
}
// 如果还有子线程在运行,主线程就让出资源。
// 等所有的子线程运行结束,主线程继续执行
while (Thread.activeCount() > 1) {
Thread.yield();
}
// 使用synchronized关键字后,是原子操作,synNumber 输出结果一定200
System.out.println("synNumber is:\t" + example.getSynNumber());
}
/**
* 对synNumber进行++操作,使用synchronized
*/
public void increaseSynNumber() {
synchronized (this) {
this.synNumber++;
}
}
public int getSynNumber() {
return this.synNumber;
}
}
java.util.concurrent.locks.Lock保证原子性
java.util.concurrent.locks.Lock封装了synchronized,提供了更丰富的功能。
public class LockExample {
private int lockNumber = 0;
public static void main(String[] args) {
final LockExample example= new LockExample();
for (int i = 0; i < 200; i++) {
new Thread(new Runnable() {
@Override
public void run() {
example.increaseLockNumber();
}
}).start();
}
// 如果还有子线程在运行,主线程就让出资源。
// 等所有的子线程运行结束,主线程继续执行
while (Thread.activeCount() > 1) {
Thread.yield();
}
// 发现使用Lock后,是原子操作,lockNumber输出结果一定200
System.out.println("locknumber is:\t" + example.getLockNumber());
}
/**
* 对lockNumber进行++操作,使用lock
*/
public void increaseLockNumber() {
lock.lock();
try {
this.lockNumber++;
} finally {
lock.unlock();
}
}
public int getLockNumber() {
return this.lockNumber;
}
}