您的位置:首页 > 编程语言 > Java开发

Java多线程编程(5)-volatile和synchronized比较

2015-04-02 15:48 609 查看

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;
}

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: