论坛首页 Java企业应用论坛

问下关于ReentrantLock中线程读某个变量是否需要加锁

浏览 4515 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2014-12-01   最后修改:2014-12-03
我在使用ReentrantLock类对变量进行多线程累加时,调用了lock()和unlock()方法,但读取该变量时我未加锁,结果是能正确执行,代码如下:

public class Main {
	private long count;
	private ExecutorService pool;
	private Lock lock = new ReentrantLock();

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		new Main().countThread();
	}

	public Main() {
		pool = Executors.newFixedThreadPool(50);
	}

	public void countThread() {
		for (int i = 0; i < 3000; i++) {
			pool.execute(new Runnable() {
				@Override
				public void run() {
					lock.lock();
					try {
						count++;
					} catch (Exception e) {
						e.printStackTrace();
					} finally {
						lock.unlock();
					}
				}
			});
		}
		while (true) {
			System.out.println(count);
			if (count == 3000) {
				break;
			}
		}
		
		System.out.println(count);
		pool.shutdown();
	}
}

http://www.infoq.com/cn/articles/java-memory-model-1
这篇文章介绍了Java内存模型定义了线程和主内存之间的抽象关系:线程之间的共享变量存储在主内存(main memory)中,每个线程都有一个私有的本地内存(local memory),本地内存中存储了该线程以读/写共享变量的副本。
能不能这样理解,类中的全局变量是存储在主内存。也就是读操作不需要锁,不知道理解是否正确。
   发表时间:2014-12-05  
主存的变量,线程的本地内存会有一个副本这没错.可是两者有一个同步时机的问题,所以你这样用是不安全的.

要么两边都同步保护,要么你增加"volatile"关键字提示这个变量不能创建本地副本.
0 请登录后投票
   发表时间:2014-12-08  
etnet 写道
主存的变量,线程的本地内存会有一个副本这没错.可是两者有一个同步时机的问题,所以你这样用是不安全的.

要么两边都同步保护,要么你增加"volatile"关键字提示这个变量不能创建本地副本.

这几天看了下这方面的知识,其实这个程序虽然读是非线程安全,但是写是线程安全的,但站在业务角度来说,此程序是完全没有问题的,因为读线程每次读到的count值会一直是小于3000,但只要等写线程执行完毕,读线程还是能够取到正确值。
0 请登录后投票
   发表时间:2014-12-08  
2楼正解

只需要将
private long count;
改写为
private volatile long count;   
即可

要说本质 就需要看jmm了,主要是因为各个cpu都有L1 等缓存,缓存跟内存间会出现不同步问题,而volatile会保证各个cpu缓存内容跟内存一致,而sychonized 则会在退出同步区时将缓存数据刷回内存从而保证 内存一致性。
哈哈 如有不对,多多指教啦
0 请登录后投票
   发表时间:2015-01-08  
如果你的业务场景对一致性没有要求。也就是说,可以容忍count增加后另外一根线程读到增加之前的count值,那么是没有问题的。
0 请登录后投票
   发表时间:2015-01-10  
smartgear 写道
如果你的业务场景对一致性没有要求。也就是说,可以容忍count增加后另外一根线程读到增加之前的count值,那么是没有问题的。


呵呵,是的,我现在明白了我这个写法其实是使用了下面这篇文章里面描述的“开销较低的读-写锁策略”
http://www.ibm.com/developerworks/cn/java/j-jtp06197.html
0 请登录后投票
   发表时间:2015-03-27   最后修改:2015-03-27
MauerSu 写道
2楼正解

只需要将
private long count;
改写为
private volatile long count;   
即可

要说本质 就需要看jmm了,主要是因为各个cpu都有L1 等缓存,缓存跟内存间会出现不同步问题,而volatile会保证各个cpu缓存内容跟内存一致,而sychonized 则会在退出同步区时将缓存数据刷回内存从而保证 内存一致性。
哈哈 如有不对,多多指教啦


你说的我也同意,但是我看java concurrency in practice中介绍说 必须满足以下所有条件才能使用
volatile关键字

1:只有一个线程对变量进行修改;或者 对变量的写入不依赖当前值
2:该变量不会与其他变量一起纳入不变性条件
3:访问该变量时候不需要加锁

你这样在lock的代码中用volatile关键字   显然违法了第三点啊。
还是我理解有误 请指正
0 请登录后投票
   发表时间:2015-03-28  
oncelonly 写道
MauerSu 写道
2楼正解

只需要将
private long count;
改写为
private volatile long count;   
即可

要说本质 就需要看jmm了,主要是因为各个cpu都有L1 等缓存,缓存跟内存间会出现不同步问题,而volatile会保证各个cpu缓存内容跟内存一致,而sychonized 则会在退出同步区时将缓存数据刷回内存从而保证 内存一致性。
哈哈 如有不对,多多指教啦


你说的我也同意,但是我看java concurrency in practice中介绍说 必须满足以下所有条件才能使用
volatile关键字

1:只有一个线程对变量进行修改;或者 对变量的写入不依赖当前值
2:该变量不会与其他变量一起纳入不变性条件
3:访问该变量时候不需要加锁

你这样在lock的代码中用volatile关键字   显然违法了第三点啊。
还是我理解有误 请指正


第三条 3:访问该变量时候不需要加锁

这句话我认为 其语境 意思 表达的是 :‘当不存在 多个线程并发访问的时候’ 也就是书中这句话的意思 没有多线程访问情况,也就不需要加锁,感觉翻译有问题
0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics