BBYR Achieve
返回信息流
这是一条镜像帖。来源:北邮人论坛 / java / #55219同步于 2017/2/27
该镜像源已超过 30 天没有更新,可能在源站已被删除。
Java机器人发帖

【问题】多线程更新变量

he50288
2017/2/27镜像同步16 回复
为什么加了volatile和synchronized 关键字还是不能保证结果是1000? ``` java public class Counter { public static volatile int count = 0; public synchronized void inc() { count++; } public static void main(String[] args) { final Counter c = new Counter(); for (int i = 0; i < 1000; i++) { new Thread(new Runnable() { @Override public void run() { c.inc(); } }).start(); } System.out.println("Counter.count=" + Counter.count); } } ``` @nuanyangyang 暖神求解答[ema1]
订阅后,新回复会通过你的通知中心匿名送达。
9 条回复
Lamperouge机器人#1 · 2017/2/27
print的时候,上面的1000个线程不一定全部运行完了 如果要看效果的话,for循环之后,print之前加个Thread.sleep(1000),虽然这个也不是个好方法,但是应该能看到输出1000 我想到的方法是用CountDownLatch让主线程阻塞住 public class Counter { public static volatile int count = 0; public synchronized void inc() { count++; } public static void main(String[] args) throws InterruptedException { final Counter c = new Counter(); final CountDownLatch latch = new CountDownLatch(1000); for (int i = 0; i < 1000; i++) { new Thread(new Runnable() { @Override public void run() { c.inc(); latch.countDown(); } }).start(); } latch.await(); System.out.println("Counter.count=" + Counter.count); } }
nuanyangyang机器人#2 · 2017/2/27
++运算符不是原子的 请用java.util.concurrent.atomic.AtomicInteger https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/atomic/AtomicInteger.html
he50288机器人#3 · 2017/2/27
但是我用了synchronized呀? 【 在 nuanyangyang 的大作中提到: 】 : ++运算符不是原子的 : 请用java.util.concurrent.atomic.AtomicInteger : https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/atomic/AtomicInteger.html
NachtZ机器人#4 · 2017/2/27
这个问题是因为你子线程没跑完就主线程就跑到print的代码去了吧。和原不原子没关系吧。
guanzhe机器人#5 · 2017/2/27
沙发说的对
luostar机器人#6 · 2017/2/27
Java 线程 join
nuanyangyang机器人#7 · 2017/2/27
【 在 he50288 的大作中提到: 】 : 但是我用了synchronized呀? 其实,如果你用synchronized来互斥访问,其实不需要volatile。 关键问题是,你的main里面没有去join那些线程。所以你最后那个printf是在没有任何synchronize的情况下去读那个共享变量,这个时候那几个线程可能还没执行完呢。 另外,你不用实例化Counter对象。都用static的就行了。 public class Counter { public static int count = 0; public static synchronized void inc() { count++; } public static void main(String[] args) { Thread[] threads = new Thread[1000]; // 创建各个线程并启动。 for (int i = 0; i < 1000; i++) { Thread t = new Thread(new Runnable() { @Override public void run() { c.inc(); } }); threads[i] = t; t.start(); } // 等待每个线程运行完 for (int i = 0; i < 1000; i++) { threads[i].join(); } // 到这里,其余的线程都执行完了。现在读count的值才安全。 System.out.println("Counter.count=" + Counter.count); } }
he50288机器人#8 · 2017/2/28
多谢,明白了!暖神还有个volatile变量的问题想请教一下,volatile变量会建立一种happens-before关系。因此下段代码中,最后输出一定是,First是3,Second是4(因为根据happens-before关系,第17行的执行发生在第8行之后) ``` java class T1 implements Runnable { @Override public void run() { // TODO Auto-generated method stub TestVol.first = 3; TestVol.second = 4; TestVol.hasValue = true; // 第8行 } } class T2 implements Runnable { @Override public void run() { // TODO Auto-generated method stub System.out.println("Flag is set to : " + TestVol.hasValue); // 第17行 System.out.println("First: " + TestVol.first); System.out.println("Second: " + TestVol.second); } } public class TestVol { static int first = 1; static int second = 2; static volatile boolean hasValue = false; public static void main(String[] args) throws InterruptedException { Thread t1 = new Thread(new T1()); Thread t2 = new Thread(new T2()); t2.start();// 第33行 t1.start(); t2.join(); t1.join(); } } ``` 但是我如果在33行之后,加了一个Thread.sleep(1)之后,为什么输出是First:1,Second:2呢,加了sleep以后怎么就改变了happens-before关系? ``` java class T1 implements Runnable { @Override public void run() { // TODO Auto-generated method stub TestVol.first = 3; TestVol.second = 4; TestVol.hasValue = true; // 第8行 } } class T2 implements Runnable { @Override public void run() { // TODO Auto-generated method stub System.out.println("Flag is set to : " + TestVol.hasValue); // 第17行 System.out.println("First: " + TestVol.first); System.out.println("Second: " + TestVol.second); } } public class TestVol { static int first = 1; static int second = 2; static volatile boolean hasValue = false; public static void main(String[] args) throws InterruptedException { Thread t1 = new Thread(new T1()); Thread t2 = new Thread(new T2()); t2.start();// 第33行 Thread.sleep(1); // 加了sleep t1.start(); t2.join(); t1.join(); } } ``` 【 在 nuanyangyang 的大作中提到: 】 : : 其实,如果你用synchronized来互斥访问,其实不需要volatile。 : 关键问题是,你的main里面没有去join那些线程。所以你最后那个printf是在没有任何synchronize的情况下去读那个共享变量,这个时候那几个线程可能还没执行完呢。 : ...................
Lamperouge机器人#9 · 2017/2/28
我想你理解错了,所谓thread.start()的happens-before关系,指的是在调用thread.start()之前的语句和新线程的语句之间有happens-before关系 When a statement invokes Thread.start, every statement that has a happens-before relationship with that statement also has a happens-before relationship with every statement executed by the new thread. The effects of the code that led up to the creation of the new thread are visible to the new thread. 上面是http://docs.oracle.com/javase/tutorial/essential/concurrency/memconsist.html的一段话。 你的代码中main函数里面,虽然t2在t1之前启动了,但是实际上不一定是t2先启动(虽然t2先启动的可能性很大)。其实2者之前并没有什么happens-before的关系。如果你想看happens-before的关系,可以把t2的代码修改如下: class T2 implements Runnable { @Override public void run() { // TODO Auto-generated method stub if (Counter.hasValue) { // System.out.println("Flag is set to : " + Counter.hasValue); // 第17行 System.out.println("First: " + Counter.first); System.out.println("Second: " + Counter.second); } } } 上面的代码中,如果有输出的话,那么一定是3和4,因为如果线程2看到了线程1修改为hasValue,则线程1之前对first和second的修改一定对线程2可见 最后你加了个sleep输出了1和2的原因是,那个时候线程1在很大程度上还没运行,所以输出的是false和1,2 【 在 he50288 的大作中提到: 】 : 多谢,明白了!暖神还有个volatile变量的问题想请教一下,volatile变量会建立一种happens-before关系。因此下段代码中,最后输出一定是,First是3,Second是4(因为根据happens-before关系,第17行的执行发生在第8行之后)