关于多线程的原子量的问题,希望大家一起讨论下,求结论

昆卡卡 2011-09-21 03:04:05
废话不多说,直接来代码,两个类
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.locks.Lock;

public class MyCountA implements Runnable
{
private static AtomicLong along = new AtomicLong(10000); //为什么这里要加static
private String name;
private int x;
private Lock lock;

public MyCountA(String name,int x,Lock lock)
{
this.name=name;
this.x=x;
this.lock=lock;
}
public void run(){
lock.lock();
System.out.println(name+"操作金额:"+x+"账户余额:"+along.addAndGet(x));
lock.unlock();
}
}



import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class TestM
{
public static void main(String[] args)
{
Lock lock = new ReentrantLock(false);
ExecutorService pool = Executors.newCachedThreadPool();
Runnable t1 = new MyCountA("张三", 1000,lock);
Runnable t2 = new MyCountA("李四", -1000,lock);
Runnable t3 = new MyCountA("王五", 5000,lock);
Runnable t4 = new MyCountA("赵六", -6000,lock);
Runnable t5 = new MyCountA("阿大", 8000,lock);
Runnable t6 = new MyCountA("阿三", -9000,lock);
pool.execute(t6);
pool.execute(t5);
pool.execute(t4);
pool.execute(t3);
pool.execute(t2);
pool.execute(t1);
pool.shutdown();
}
}

加static的话输出的答案正确:
阿三操作金额:-9000账户余额:1000
阿大操作金额:8000账户余额:9000
赵六操作金额:-6000账户余额:3000
张三操作金额:1000账户余额:4000
王五操作金额:5000账户余额:9000
李四操作金额:-1000账户余额:8000

不加static的话就得不到正确答案:
阿三操作金额:-9000账户余额:1000
阿大操作金额:8000账户余额:18000
赵六操作金额:-6000账户余额:4000
李四操作金额:-1000账户余额:9000
王五操作金额:5000账户余额:15000

不加的话每个人的操作全是针对10000这个值而言的,不知道是锁没有生效,这几个线程都在别的线程还没改变10000这个值的时候做的操作,还是每个线程对along 的值的修改无法对其他线程生效,还有就是原子量有什么用(说是可以保证变量操作的原子性,但整个程序还需要考虑线程安全的。)也就是在这里用原子量和普通的long 是没什么区别的,求达人解答
...全文
230 21 打赏 收藏 转发到动态 举报
写回复
用AI写文章
21 条回复
切换为时间正序
请发表友善的回复…
发表回复
昆卡卡 2011-09-23
  • 打赏
  • 举报
回复
谢谢大家的耐心解答,感觉比自己弄明白多了,如果还有什么可以要学习的讨论的一起学习下,今天晚上就结贴,谢谢大家
fainfy 2011-09-22
  • 打赏
  • 举报
回复
[Quote=引用 15 楼 gukuitian 的回复:]

引用 8 楼 gukuitian 的回复:
测试了下下代码,发现去掉lock扣,这个数值一直是对的,只是打印的顺序不一样,

我觉得这个原子量用处,只用保证这个数据的最后值,过程无所谓。楼主可以试试,
要是自己锁的话,即使只用普通的long型也是正确的顺序,正确的值,

使用了原子量,即使不锁,最后along的值也是固定的,最后都是8000。
Java code
import j……
[/Quote]


public static void main(String[] args) {
Lock lock = new ReentrantLock(false);
ExecutorService pool = Executors.newCachedThreadPool();
Runnable t1 = new MyCountA("张三", 1000, lock);
Runnable t2 = new MyCountA("李四", -1000, lock);
Runnable t3 = new MyCountA("王五", 5000, lock);
Runnable t4 = new MyCountA("赵六", -6000, lock);
Runnable t5 = new MyCountA("阿大", 8000, lock);
Runnable t6 = new MyCountA("阿三", -9000, lock);
pool.execute(t6);
pool.execute(t5);
pool.execute(t4);
pool.execute(t3);
// pool.execute(t2);
// pool.execute(t1);
pool.shutdown();
}


有问题的,有很多的问题。上面的代码朋友多运行几次,查看一下结果。
gukuitian 2011-09-22
  • 打赏
  • 举报
回复
[Quote=引用 8 楼 gukuitian 的回复:]
测试了下下代码,发现去掉lock扣,这个数值一直是对的,只是打印的顺序不一样,
[/Quote]
我觉得这个原子量用处,只用保证这个数据的最后值,过程无所谓。楼主可以试试,
要是自己锁的话,即使只用普通的long型也是正确的顺序,正确的值,

使用了原子量,即使不锁,最后along的值也是固定的,最后都是8000。
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class MyCountA implements Runnable
{
private static AtomicLong along =new AtomicLong(10000); // 为什么这里要加static
//public static Long along=(long)10000 ; // 为什么这里要加static

private String name;
private int x;
private Lock lock;

public MyCountA(String name, int x, Lock lock) throws InterruptedException
{
this.name = name;
this.x = x;
this.lock = lock;
}
public synchronized void run()
{
//lock.lock();
//along+=x;
along.addAndGet(x);
System.out.println(name+"操作金额:"+x+"账户余额:"+(along));
//lock.unlock();

}

public static void main(String[] args) throws InterruptedException
{
Lock lock = new ReentrantLock(false);
MyCountA t1 = new MyCountA("张三", 1000, lock);
MyCountA t2 = new MyCountA("李四", -1000, lock);
MyCountA t3 = new MyCountA("王五", 5000, lock);
MyCountA t4 = new MyCountA("赵六", -6000, lock);
MyCountA t5 = new MyCountA("阿大", 8000, lock);
MyCountA t6 = new MyCountA("阿三", -9000, lock);
ExecutorService pool = Executors.newCachedThreadPool();
pool.execute(t6);
pool.execute(t5);
pool.execute(t4);
pool.execute(t3);
pool.execute(t2);
pool.execute(t1);
pool.shutdown();
Thread.sleep(1000);
System.out.println(MyCountA.along);
}
}
lxbccsu 2011-09-22
  • 打赏
  • 举报
回复
[Quote=引用楼主 yyy269954107 的回复:]
不加的话每个人的操作全是针对10000这个值而言的,不知道是锁没有生效,这几个线程都在别的线程还没改变10000这个值的时候做的操作,还是每个线程对along 的值的修改无法对其他线程生效,还有就是原子量有什么用(说是可以保证变量操作的原子性,但整个程序还需要考虑线程安全的。)也就是在这里用原子量和普通的long 是没什么区别的,求达人解答[/Quote]

第一,锁是肯定生效了的;
第二,在多线程中,需要同步的是对共享资源的操作;
这里加static与下面代码等效:

public class MyCountA implements Runnable
{
private AtomicLong along = null;
...
public MyCountA(String name,int x,Lock lock, AtomicLong along){
this.name = name;
this.x = x;
this.lock = lock;
this.along = along;
}
...
}

public class TestM{
public static void main(String[] args){
Lock lock = new ReentrantLock(false);
AtomicLong along = new AtomicLong(10000);
ExecutorService pool = Executors.newCachedThreadPool();
Runnable t1 = new MyCountA("张三", 1000, lock, along);
Runnable t2 = new MyCountA("李四", -1000, lock, along);
...
}


第三,楼主理解原子量概念错误;
along.addAndGet(x));这就是个原子操作,这就是原子量的含义;

如果along是个long变量,我们的操作是这样的:
along = along + x;
这里就不是原子操作了,相当于会有3条指令处理
along = 10000;
temp = along + x;
along = temp;
上面在多线程操作中会有问题;

不知道楼主明白了没有。
没有明白楼下继续。


gukuitian 2011-09-22
  • 打赏
  • 举报
回复
我的意思是,不管当前有几个线程,只要操作的数值固定,当它们都执行完成后,最后的值就是固定的。
fainfy 2011-09-22
  • 打赏
  • 举报
回复
[Quote=引用 17 楼 gukuitian 的回复:]

Java code
Thread.sleep(1000);
System.out.println(MyCountA.along);


最后的结果都是一样的。
[/Quote]

Thread.sleep(1000);这算什么,加上这个还需要多线程么?
gukuitian 2011-09-22
  • 打赏
  • 举报
回复
        Thread.sleep(1000);
System.out.println(MyCountA.along);

最后的结果都是一样的。
fainfy 2011-09-21
  • 打赏
  • 举报
回复
软件包 java.util.concurrent.atomic 的描述
类的小工具包,支持在单个变量上解除锁的线程安全编程。事实上,此包中的类可将 volatile 值、字段和数组元素的概念扩展到那些也提供原子条件更新操作的类,其形式如下:

boolean compareAndSet(expectedValue, updateValue);
如果此方法(在不同的类间参数类型也不同)当前保持 expectedValue,则以原子方式将变量设置为 updateValue,并在成功时报告 true。此包中的类还包含获取并无条件设置值的方法,以及以下描述的较弱条件的原子更新操作 weakCompareAndSet。

这些方法的规范使实现能够使用当代处理器上提供的高效机器级别原子指令。但是在某些平台上,该支持可能需要某种形式的内部锁。因而,该方法不能严格保证不被阻塞 - 执行操作之前可能暂时阻塞线程。
......
fainfy 2011-09-21
  • 打赏
  • 举报
回复
为什么需要锁我也很想知道,呵呵。
不过官方的API也给了比较模糊的答案。

软件包 java.util.concurrent.atomic 的描述
类的小工具包,支持在单个变量上解除锁的线程安全编程。事实上,此包中的类可将 volatile 值、字段和数组元素的概念扩展到那些也提供原子条件更新操作的类,其形式如下:

boolean compareAndSet(expectedValue, updateValue);
如果此方法(在不同的类间参数类型也不同)当前保持 expectedValue,则以原子方式将变量设置为 updateValue,并在成功时报告 true。此包中的类还包含获取并无条件设置值的方法,以及以下描述的较弱条件的原子更新操作 weakCompareAndSet。

这些方法的规范使实现能够使用当代处理器上提供的高效机器级别原子指令。但是在某些平台上,该支持可能需要某种形式的内部锁。因而,该方法不能严格保证不被阻塞 - 执行操作之前可能暂时阻塞线程。
...
fainfy 2011-09-21
  • 打赏
  • 举报
回复

private static AtomicLong along = new AtomicLong(10000); //为什么这里要加static

楼主仔细看:这里加上static,无论是t1,t2...线程都是同一个along 对象,修改也是在同一个对象的基础之上的。

加static的话输出的答案正确:
阿三操作金额:-9000账户余额:1000 - (基数为10000 - 9000)
阿大操作金额:8000账户余额:9000 - (基数为1000 + 8000)
赵六操作金额:-6000账户余额:3000 (基数为9000 - 6000)
张三操作金额:1000账户余额:4000
王五操作金额:5000账户余额:9000
李四操作金额:-1000账户余额:8000

不加static所有的线程对象都是各自的along 对象,所以基数都为10000,所以结果没有问题。
不加static的话就得不到正确答案:
阿三操作金额:-9000账户余额:1000
阿大操作金额:8000账户余额:18000
赵六操作金额:-6000账户余额:4000
李四操作金额:-1000账户余额:9000
王五操作金额:5000账户余额:15000
healer_kx 2011-09-21
  • 打赏
  • 举报
回复
因为Java的long不是C++的long,现在大多数C++的编译器,long是4个字节。
而Java的long是8个字节。那么32位的OS,一个指令最多处理4个字节。

也就说你 long a = 1;其实操作了两次,那么这两次就有可能被多线程cut。
所以一定要把 long当做一个原子量来操作。

幸好Java的lib提供了AtomicLong 。

就这么回事。
lyhmy 2011-09-21
  • 打赏
  • 举报
回复
你们讨论我学习
brightyq 2011-09-21
  • 打赏
  • 举报
回复
这里是要加锁的,不加锁可能有两个线程同时执行,第二个线程执行along.addAndGet(),就把第一个线程的值改掉了。
至于为什么要加private static AtomicLong along
如果不加的话,t1,t2...都是不同的MyCountA对象,那么操作的就是不同的along 变量了,加了static就是操作的同一个。
gukuitian 2011-09-21
  • 打赏
  • 举报
回复
测试了下下代码,发现去掉lock扣,这个数值一直是对的,只是打印的顺序不一样,
scrack 2011-09-21
  • 打赏
  • 举报
回复
static 变量属于 类级的
昆卡卡 2011-09-21
  • 打赏
  • 举报
回复
[Quote=引用 5 楼 gukuitian 的回复:]

你这里需要用lock么?
[/Quote]
需要的啊,不用的话,还是无法同步的,你可以测试一下;
我现在就是纠结这个,既然还需要锁的话为什么要用原子量
gukuitian 2011-09-21
  • 打赏
  • 举报
回复
你这里需要用lock么?
gukuitian 2011-09-21
  • 打赏
  • 举报
回复
AtomicLong 相当于用 synchronized声明的对像,同步 ;
当一个线程访问当前数据时,会拒绝别的线程结它进行操作,
等当前线程操作完成,才释放锁
昆卡卡 2011-09-21
  • 打赏
  • 举报
回复
[Quote=引用 1 楼 gukuitian 的回复:]

你这问题跟多线程无关,
就是static变量的事,

static变量是属于类的,不是某一个对像,也就是所有对像共用的变量,
非static就是一个对像一个
[/Quote]
麻烦看下我最后的一个问题,既然原子量和普通的变量一样要加锁,为什么要用原子量呢
softroad 2011-09-21
  • 打赏
  • 举报
回复
加载更多回复(1)

62,614

社区成员

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

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