关于AtomicInteger类的问题

hellen_99010 2014-01-15 02:28:28
我写的测试例子代码:


import java.util.concurrent.atomic.AtomicInteger;

public class AtomicCounter {

private AtomicInteger c = new AtomicInteger(0);

public void increment() {
c.incrementAndGet();
}

public void decrement() {
c.decrementAndGet();
}

public int value() {
return c.get();
}


/**
* @param args
*/
public static void main(String[] args) {
final AtomicCounter ac = new AtomicCounter();

new Thread(new Runnable() {

@Override
public void run() {
int i = 0;
while(i<100) {
ac.increment();
System.out.println(Thread.currentThread().getName() +"--" + ac.value());
i++;
}

}
}).start();

new Thread(new Runnable() {

@Override
public void run() {
int i = 0;
while(i<100) {
ac.decrement();
System.out.println(Thread.currentThread().getName() +"--" + ac.value());
i++;
}

}
}).start();
}

}



在我的机器上运行的结果:

Thread-0--1
Thread-0--2
Thread-0--3
Thread-0--4
Thread-0--5
Thread-0--6
Thread-0--7
Thread-0--8
Thread-0--9
Thread-0--10
Thread-0--11
Thread-0--12
Thread-0--13
Thread-0--14
Thread-0--15
Thread-0--16
Thread-0--17
Thread-0--18
Thread-0--19
Thread-0--20
Thread-0--21
Thread-0--22
Thread-0--23
Thread-0--24
Thread-0--25
Thread-0--26
Thread-0--27
Thread-0--28
Thread-0--29
Thread-0--30
Thread-0--31
Thread-0--32
Thread-0--33
Thread-0--34
Thread-0--35
Thread-0--36
Thread-0--37
Thread-0--38
Thread-0--39
Thread-0--40
Thread-0--41
Thread-0--42
Thread-0--43
Thread-0--44
Thread-0--45
Thread-0--46
Thread-0--47
Thread-0--48
Thread-0--49
Thread-0--50
Thread-0--51
Thread-0--52
Thread-0--53
Thread-0--54
Thread-0--55
Thread-0--56
Thread-0--57
Thread-0--58
Thread-0--59
Thread-0--60
Thread-0--61
Thread-0--62
Thread-0--62
Thread-0--63
Thread-0--64
Thread-0--65
Thread-0--66
Thread-0--67
Thread-0--68
Thread-0--69
Thread-0--70
Thread-0--71
Thread-0--72
Thread-0--73
Thread-0--74
Thread-0--75
Thread-0--76
Thread-0--77
Thread-0--78
Thread-0--79
Thread-0--80
Thread-0--81
Thread-0--82
Thread-0--83
Thread-0--84
Thread-1--61
Thread-1--84
Thread-1--83
Thread-1--82
Thread-1--81
Thread-1--80
Thread-1--79
Thread-1--78
Thread-1--77
Thread-1--76
Thread-1--75
Thread-1--74
Thread-1--73
Thread-1--72
Thread-1--71
Thread-1--70
Thread-1--69
Thread-1--68
Thread-1--67
Thread-1--66
Thread-1--65
Thread-1--64
Thread-1--63
Thread-1--62
Thread-1--61
Thread-1--60
Thread-1--59
Thread-1--58
Thread-1--57
Thread-1--56
Thread-1--55
Thread-1--54
Thread-1--53
Thread-1--52
Thread-1--51
Thread-1--50
Thread-1--49
Thread-1--48
Thread-1--47
Thread-1--46
Thread-1--45
Thread-1--44
Thread-1--43
Thread-1--42
Thread-1--41
Thread-1--40
Thread-1--39
Thread-1--38
Thread-1--37
Thread-1--36
Thread-0--85
Thread-0--37
Thread-0--38
Thread-0--39
Thread-0--40
Thread-0--41
Thread-0--42
Thread-0--43
Thread-0--44
Thread-0--45
Thread-0--46
Thread-0--47
Thread-0--48
Thread-0--49
Thread-0--50
Thread-1--49
Thread-1--48
Thread-1--47
Thread-1--46
Thread-1--45
Thread-1--44
Thread-1--43
Thread-1--42
Thread-1--41
Thread-1--40
Thread-1--39
Thread-1--38
Thread-1--37
Thread-1--36
Thread-1--35
Thread-1--34
Thread-1--33
Thread-1--32
Thread-1--31
Thread-1--30
Thread-1--29
Thread-1--28
Thread-1--27
Thread-1--26
Thread-1--25
Thread-1--24
Thread-1--23
Thread-1--22
Thread-1--21
Thread-1--20
Thread-1--19
Thread-1--18
Thread-1--17
Thread-1--16
Thread-1--15
Thread-1--14
Thread-1--13
Thread-1--12
Thread-1--11
Thread-1--10
Thread-1--9
Thread-1--8
Thread-1--7
Thread-1--6
Thread-1--5
Thread-1--4
Thread-1--3
Thread-1--2
Thread-1--1
Thread-1--0



我现在的问题主要是在结果这里
Thread-0--83
Thread-0--84
Thread-1--61
Thread-1--84

Thread-1--36
Thread-0--85
Thread-0--37

从结果看,不是线程安全的.在网上也找了些资料,也没有找到合理的说法.各位大神解答下.在线.谢谢!
...全文
321 15 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
15 条回复
切换为时间正序
请发表友善的回复…
发表回复
hellen_99010 2014-02-27
  • 打赏
  • 举报
回复
引用 8 楼 kakashi8841 的回复:
ac.increment();
System.out.println(Thread.currentThread().getName() +"--" + ac.value());
有两个问题: 问题1: 假设ac.increment()只后值是100,ac.value()不一定会拿到100,因为有可能在这个操作之前,另一个线程修改了ac的值。如果你想获得增加后的值,应该修改AC的类,让他计算完返回值:

public int increment() {
    return c.incrementAndGet();
}
然后那两行代码就改成:
int val = ac.increment();
System.out.println(Thread.currentThread().getName() +"--" + val);
减存在一样的问题。 问题2: 就算val的值是正确了,打印的顺序却可能是不正确的。因为有可能这个线程计算完值,还没执行打印,JVM突然就去执行另一个线程了,而且另一个线程也打印了值。然后你这个线程才打印。那顺序也会出错咯。 为了重现这种情况。你可以在那两行代码中间加入sleep更容易观察:

int val = ac.increment();
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName() +"--" + val);
注意:只要在加或减加一个sleep就行啦,不要两个都加。
你分析得很对,谢谢!先前回家了,没有上来.现在才来结贴,抱歉~
lcf 2014-01-20
  • 打赏
  • 举报
回复
引用 12 楼 raistlic 的回复:
[quote=引用 10 楼 lcf 的回复:] 楼上的同学们不要误人子弟啊。。 这里是ac.increment()/decrement()和print中间出现了context switch,导致读到的值延后打印的结果。 Thread 0 Thread 1 AtomicIntValue value = IncrementAndGet(84) 85 value = decrementAndGet(85) 84 print 84 value = decrementAndGet(84) 83 print 83 .... print 36 36 print 84 value = IncrementAndGet(36) 37 print 37 ...
好吧,是我看贴不认真。 —— 但是8楼说的完全正确,即使 ac.increment()/decrement() 和 print 中间没有context switch,8楼说的【问题1】仍然存在。[/quote] 是我之前看帖不认真。现在我觉得你之前说的就是指8楼的问题一,不好意思。。
raistlic 2014-01-20
  • 打赏
  • 举报
回复
引用 10 楼 lcf 的回复:
楼上的同学们不要误人子弟啊。。 这里是ac.increment()/decrement()和print中间出现了context switch,导致读到的值延后打印的结果。 Thread 0 Thread 1 AtomicIntValue value = IncrementAndGet(84) 85 value = decrementAndGet(85) 84 print 84 value = decrementAndGet(84) 83 print 83 .... print 36 36 print 84 value = IncrementAndGet(36) 37 print 37 ...
好吧,是我看贴不认真。 —— 但是8楼说的完全正确,即使 ac.increment()/decrement() 和 print 中间没有context switch,8楼说的【问题1】仍然存在。
  • 打赏
  • 举报
回复
因为打印输出是在每个线程中运行的,又没同步过,所以出来的顺序就是乱的喽
raistlic 2014-01-17
  • 打赏
  • 举报
回复
你写的例子有问题,所以得到这种结果,正常

for (int i = ac.c.incrementAndGet(); i < 100; i = ac.c.incrementAndGet()) {
  System.out.println(i);
}
你自己加的那层壳破坏了 “自加并读取当前值” 的原子性
yongger520 2014-01-17
  • 打赏
  • 举报
回复
引用 10 楼 lcf 的回复:
楼上的同学们不要误人子弟啊。。 这里是ac.increment()/decrement()和print中间出现了context switch,导致读到的值延后打印的结果。 Thread 0 Thread 1 AtomicIntValue value = IncrementAndGet(84) 85 value = decrementAndGet(85) 84 print 84 value = decrementAndGet(84) 83 print 83 .... print 36 36 print 84 value = IncrementAndGet(36) 37 print 37 ...
你的context switch 是一个新的说法而已 不能说楼上的同学们都误人子弟,大家的说法有些很有道理的
lcf 2014-01-17
  • 打赏
  • 举报
回复
楼上的同学们不要误人子弟啊。。 这里是ac.increment()/decrement()和print中间出现了context switch,导致读到的值延后打印的结果。 Thread 0 Thread 1 AtomicIntValue value = IncrementAndGet(84) 85 value = decrementAndGet(85) 84 print 84 value = decrementAndGet(84) 83 print 83 .... print 36 36 print 84 value = IncrementAndGet(36) 37 print 37 ...
kakashi8841 2014-01-16
  • 打赏
  • 举报
回复
ac.increment();
System.out.println(Thread.currentThread().getName() +"--" + ac.value());
有两个问题: 问题1: 假设ac.increment()只后值是100,ac.value()不一定会拿到100,因为有可能在这个操作之前,另一个线程修改了ac的值。如果你想获得增加后的值,应该修改AC的类,让他计算完返回值:

public int increment() {
    return c.incrementAndGet();
}
然后那两行代码就改成:
int val = ac.increment();
System.out.println(Thread.currentThread().getName() +"--" + val);
减存在一样的问题。 问题2: 就算val的值是正确了,打印的顺序却可能是不正确的。因为有可能这个线程计算完值,还没执行打印,JVM突然就去执行另一个线程了,而且另一个线程也打印了值。然后你这个线程才打印。那顺序也会出错咯。 为了重现这种情况。你可以在那两行代码中间加入sleep更容易观察:

int val = ac.increment();
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName() +"--" + val);
注意:只要在加或减加一个sleep就行啦,不要两个都加。
lawrenpan 2014-01-15
  • 打赏
  • 举报
回复
引用 6 楼 yongger520 的回复:
[quote=引用 5 楼 hellen_99010 的回复:] [quote=引用 4 楼 yongger520 的回复:] Thread-0--84 Thread-1--61 Thread-1--84
建议加上序号:下面两部分的操作是分开的 ac.increment(); System.out.println(Thread.currentThread().getName()+" " +i+ " add " + ac.value()); 线程1减少之后 打印的操作没有接着,而是被线程0占用了 线程0打印了Thread-0--84 之后 线程1的打印操作才继续打印 Thread-1--61 接下来线程1再接着打印的时候发现值已经是84了 [/quote] 那这个是不是意味着,其实取值的时候取得还是线程自己缓存的值,只有当进行读写操作时才会取对象的值
yongger520 2014-01-15
  • 打赏
  • 举报
回复
引用 5 楼 hellen_99010 的回复:
[quote=引用 4 楼 yongger520 的回复:] Thread-0--84 Thread-1--61 Thread-1--84
建议加上序号:下面两部分的操作是分开的 ac.increment(); System.out.println(Thread.currentThread().getName()+" " +i+ " add " + ac.value()); 线程1减少之后 打印的操作没有接着,而是被线程0占用了 线程0打印了Thread-0--84 之后 线程1的打印操作才继续打印 Thread-1--61 接下来线程1再接着打印的时候发现值已经是84了
hellen_99010 2014-01-15
  • 打赏
  • 举报
回复
引用 4 楼 yongger520 的回复:
这个线程0 加1 线程1 减1 然后得到下面的结果很正常吧,最后结果为0了,说明线程安全啊 Thread-0--83 Thread-0--84 Thread-1--61 Thread-1--84 Thread-1--36 Thread-0--85 Thread-0--37
我想知道的是为什么中间为什么会出现 Thread-0--84 Thread-1--61 Thread-1--84 而不是 Thread-0--84 Thread-1--83 Thread-1--82 或 Thread-1--36 Thread-0--37 Thread-0--38
yongger520 2014-01-15
  • 打赏
  • 举报
回复
这个线程0 加1 线程1 减1 然后得到下面的结果很正常吧,最后结果为0了,说明线程安全啊 Thread-0--83 Thread-0--84 Thread-1--61 Thread-1--84 Thread-1--36 Thread-0--85 Thread-0--37
hellen_99010 2014-01-15
  • 打赏
  • 举报
回复
引用 2 楼 copy202 的回复:
AtomicInteger的线程安全并不能保证多个线程的执行顺序,只能保证多个线程对同一个变量不会同时修改
那我上面两个不同线程得到的值不是同一个变量嘛
copy202 2014-01-15
  • 打赏
  • 举报
回复
AtomicInteger的线程安全并不能保证多个线程的执行顺序,只能保证多个线程对同一个变量不会同时修改
copy202 2014-01-15
  • 打赏
  • 举报
回复
AtomicInteger的线程安全并能保证多个线程的执行顺序,只能保证多个线程对同一个变量不会同时修改

62,634

社区成员

发帖
与我相关
我的任务
社区描述
Java 2 Standard Edition
社区管理员
  • Java SE
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧