浏览 3029 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
|
|
---|---|
作者 | 正文 |
发表时间:2012-08-30
最后修改:2012-09-24
Entrance.run()方法,++number时,作者进行了同步。我认为,此处根本无需同步,因为,对于每个 Entrance对象,肯定都有自己的 number 域,这个 number 域除了 Entrance对象以外,根本没有其他线程,需要修改它。每个Entrance对象修改自己的 number,各个Entrance对象间互不打扰 即使,在其他线程(即main线程)OrnamentalGarden调用了Entrance.sumEntrances()(从而调用了Entrance.getValue()),这也是 Entrance.run()线程完成后才调用的,所以,顶多 Entrance.getValue()同步下可以理解 有人有同感嘛?难道我SB啦,或者Bruce出错了 package tij4.concurrency; //: concurrency/OrnamentalGarden.java import static net.mindview.util.Print.print; import java.util.ArrayList; import java.util.List; import java.util.Random; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; class Count { private int count = 0; private Random rand = new Random(47); // Remove the synchronized keyword to see counting fail: public synchronized int increment() { int temp = count; if (rand.nextBoolean()) // Yield half the time Thread.yield(); return (count = ++temp); } public synchronized int value() { return count; } } class Entrance implements Runnable { private static Count count = new Count(); private static List<Entrance> entrances = new ArrayList<Entrance>(); private int number = 0; // Doesn't need synchronization to read: private final int id; private static volatile boolean canceled = false; // Atomic operation on a volatile field: public static void cancel() { canceled = true; } public Entrance(int id) { this.id = id; // Keep this task in a list. Also prevents // garbage collection of dead tasks: entrances.add(this); } public void run() { while (!canceled) { synchronized (this) { ++number; } print(this + " Total: " + count.increment()); try { TimeUnit.MILLISECONDS.sleep(100); } catch (InterruptedException e) { print("sleep interrupted"); } } print("Stopping " + this); } public synchronized int getValue() { return number; } public String toString() { return "Entrance " + id + ": " + getValue(); } public static int getTotalCount() { return count.value(); } public static int sumEntrances() { int sum = 0; for (Entrance entrance : entrances) sum += entrance.getValue(); return sum; } } public class OrnamentalGarden { public static void main(String[] args) throws Exception { ExecutorService exec = Executors.newCachedThreadPool(); for (int i = 0; i < 5; i++) exec.execute(new Entrance(i)); // Run for a while, then stop and collect the data: TimeUnit.SECONDS.sleep(3); Entrance.cancel(); exec.shutdown(); if (!exec.awaitTermination(250, TimeUnit.MILLISECONDS)) print("Some tasks were not terminated!"); print("Total: " + Entrance.getTotalCount()); print("Sum of Entrances: " + Entrance.sumEntrances()); } } --------------------分割线-------------------- 2012/9/24更新: OrnamentalGarden调用了Entrance.sumEntrances()(从而调用了Entrance.getValue()),由于这次执行 和 Entrance.run()的执行是不同的线程,而且,他们都访问 number, 所以,将Entrance.getValue()设置为同步,这是正确的。那么,为什么要将 ++number 设置为同步呢? 原因如下: “对于一个存在data race的变量,不能只同步写或只同步读,因为这样不能保证得到最新值。对其的所有写和所有读必须要都同步,这样才能读取到该最新的数据”。根据这条原理,原书中代码没错,这确实是需要同步的 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2012-08-30
最后修改:2012-08-30
Bruce 没错。这个synchronize不是同步修改,而是同步读取。
这个应该是与java的内存模型有关,也与平台有关。如果是在32位或以上的平台上运行,++中的+1操作是原子的。就是说对一个数值的操作不会被分为2个16位的操作。但是如果在16位系统上运行,就可能会出现低16位被更新了,但是高16位还没有更新,此时有个程序来取这个值,就会得到错误的结果。 |
|
返回顶楼 | |
发表时间:2012-08-30
wumingshi 写道 Bruce 没错。这个synchronize不是同步修改,而是同步读取。
这个应该是与java的内存模型有关,也与平台有关。如果是在32位或以上的平台上运行,++中的+1操作是原子的。就是说对一个数值的操作不会被分为2个16位的操作。但是如果在16位系统上运行,就可能会出现低16位被更新了,但是高16位还没有更新,此时有个程序来取这个值,就会得到错误的结果。 说的没错,但是假设不成立。 但就这个程序来说,没有对Entrance对象的number字段进行并发操作。我也不理解为什么会需要同步。 |
|
返回顶楼 | |
发表时间:2012-08-30
blizzard213 写道 wumingshi 写道 Bruce 没错。这个synchronize不是同步修改,而是同步读取。 这个应该是与java的内存模型有关,也与平台有关。如果是在32位或以上的平台上运行,++中的+1操作是原子的。就是说对一个数值的操作不会被分为2个16位的操作。但是如果在16位系统上运行,就可能会出现低16位被更新了,但是高16位还没有更新,此时有个程序来取这个值,就会得到错误的结果。 说的没错,但是假设不成立。 但就这个程序来说,没有对Entrance对象的number字段进行并发操作。我也不理解为什么会需要同步。 对。 只有一个线程(Entrance.run()所在的线程)对number进行操作,所以,没必要同步。 OrnamentalGarden调用了Entrance.sumEntrances()(从而调用了Entrance.getValue()),由于这次执行 和 Entrance.run()的执行是不同的线程,而且,他们都访问 number, 所有,将Entrance.getValue()设置为同步,这里是正确的。。。但是,这和 ++number设置为同步没有一毛钱关系啊也 |
|
返回顶楼 | |
发表时间:2012-08-30
感觉对线程了解比较OK的兄弟,出来解释下啊......
|
|
返回顶楼 | |
发表时间:2012-08-30
我觉得还是需要同步, 因为如果另外一个线程正在调用getValue尝试去获取值时,刚好 Entrance 本线程需要对它进行修改,那么读线程就会阻塞,直到本线程(写线程)完成对它修改完后,读线程才读到最新的值,如果不进行同步,读线程就会读到旧值...
|
|
返回顶楼 | |
发表时间:2012-08-30
有一种写法叫
e =new Entrance(i) ; new Thread(e).start() new Thread(e).start() new Thread(e).start() new Thread(e).start() 虽然int是原子性的..这是个例子吧..只是标准用法..具体写法和场景有关..也许在真实环境下人家不这么写的... 16位机器有点牵强... |
|
返回顶楼 | |
发表时间:2012-09-24
iceman1952 写道 blizzard213 写道 wumingshi 写道 Bruce 没错。这个synchronize不是同步修改,而是同步读取。
这个应该是与java的内存模型有关,也与平台有关。如果是在32位或以上的平台上运行,++中的+1操作是原子的。就是说对一个数值的操作不会被分为2个16位的操作。但是如果在16位系统上运行,就可能会出现低16位被更新了,但是高16位还没有更新,此时有个程序来取这个值,就会得到错误的结果。 说的没错,但是假设不成立。 但就这个程序来说,没有对Entrance对象的number字段进行并发操作。我也不理解为什么会需要同步。 对。 只有一个线程(Entrance.run()所在的线程)对number进行操作,所以,没必要同步。 OrnamentalGarden调用了Entrance.sumEntrances()(从而调用了Entrance.getValue()),由于这次执行 和 Entrance.run()的执行是不同的线程,而且,他们都访问 number, 所有,将Entrance.getValue()设置为同步,这里是正确的。。。但是,这和 ++number设置为同步没有一毛钱关系啊也 上面红色部分,说法不正确 “对于一个存在data race的变量,不能只同步写或只同步读,因为这样不能保证得到最新值。对其的所有写和所有读必须要都同步,这样才能读取到该最新的数据”。从而,根据这条原理,原书中代码没错,这确实是需要同步的 |
|
返回顶楼 | |