JavaSE学习之AtomicInteger

十个线程去分别为final i = 0 加一万次,最后结束输出i的值

public class AtomicInteger1 {
	static int i = 0;
	public static void main(String[] args) throws Exception {
		Thread[] ts = new Thread[10];//十个线程放到一个数组里

		for (int k = 0; k < ts.length; k++) {
			ts[k] = new Thread(new Runnable() {

				@Override
				public void run() {
					for (int k = 1; k <= 10000; k++) {
						i++;
					}
				}
			});
			ts[k].start();
		}
		for (int k = 0
		; k < ts.length; k++) {
			ts[k].join();//释放cpu,让下个线程去运算
		}
		System.out.println(i);//十个线程结束后输出i的值
	}
}

计算结果:很明显和十万差的有点多,而且再次运行 i 的值会不同。
计算结果
这里的原因主要是i++操作不是一个原子操作。
详细解释可看i++的底层解释(数据原子性)
下边是上面链接的详细内容

原因

ts1线程拿到时间片进入运行状态,它可能刚把 i =10读入到寄存器 ,它的时间片就到了。另外一个线程拿到时间片了,它进入运行状态,它的操作也是i++;但此时它读入的i的值还是10,在寄存器运算之后将i=11写入i,此时i=11;ts1再次得到时间片时,它对寄存器中的 i 进行操作(别忘了读入的 i 是等于10的),因此ts1的结果还是把i = 11写入 i。丢失了一次i++操作。同理程序丢失了很多的i++操作。所以结果不是100000.

处理方法

1.给对象 加锁 (把int转换成对象类型integer)

public class AtomicInteger1 {
	static int i = 0;
	static Integer a = Integer.valueOf(0);
	public static void main(String[] args) throws Exception{
		Thread[] ts = new Thread[10];
		
		for (int k = 0; k < ts.length; k++) {
			ts[k] = new Thread(new Runnable() {

				@Override
				public void run() {
					for (int k = 1; k <= 10000; k++) {
						i++;
						synchronized(a) {//那个线程得到a对象的锁,它来操作
						a=Integer.valueOf(a.intValue()+1);//锁不住intValue()+1
						}
					}
				}
			});
			ts[k].start();
		}
		for (int k = 0; k < ts.length; k++) {
			ts[k].join();
		}
		System.out.println(i);
		System.out.println("a的结果是"+a);
	}
}

运行代码:结果还是没有解决

输出结果
==原因:==对象没锁上,integer对象的值是不变的,内存中一开始有个对象a表示0,a=Integer.valueOf(a.intValue()+1);//让a+1之后,用一个新对象赋值给a,其实只是a的指向了新对象。不是在原有基础上把0改成1。这样,a线程对数字0这个integer对象加锁,b线程对数字1这个ingeter这个对象加锁…所以这个代码里没有临界资源,锁不住的。

改良版:创建一个类,类里面有个属性,创建一个这个类的对象,保证锁的是这个对象,里面的属性让它去变

public class AtomicInteger1 {
	static int i = 0 ; //error!
	static Integer b = Integer.valueOf(0);//error!
	static MyObject1 obj = new MyObject1();//对这个对象加锁
	
	public static void main(String[] args) throws Exception{
		Thread[] ts = new Thread[10];
		for(int k = 0 ; k<ts.length ; k++){
			ts[k] = new Thread(new Runnable(){
				public void run(){
					for(int k = 1 ; k <= 10000; k++){
						i++;
						synchronized(b){
							b = Integer.valueOf(b.intValue()+1);
						}
						
						synchronized(obj){
							obj.x++;
						}
					}
				}
			});
			ts[k].start();
		}
		
		for(int k=0; k <ts.length ; k++){
			ts[k].join();
		}

		System.out.println("i的结果是:"+i);
		System.out.println("b的结果是:"+b);
		System.out.println("obj.x的结果是:"+obj.x);
	}

}
class MyObject1{
	public int x=0;//属性x等于0,x可变
}

结果正确
结果
缺点并发效率低,只有一个线程可以拿到目的对象的锁。其他九个线程总是在等待状态。

2.Atomic包下的AtomicInteger类,是个包装类,完成整数的原子更新,没有用到锁,CAS算法
类中的cas方法
cas:比较交换算法,可以理解成:刚才的ts1准备把i=11写入i时,它自身有一个期待值(期待值为10,因为之前i=10),会让期待值与当前 i 的值去比较,若值相等,可以进行写入操作,否则写入失败,线程重新去读入i=11到寄存器,完成运算,直到成功写入。完成此次的i++操作。

import java.util.concurrent.atomic.AtomicInteger;
public class AtomicInteger1 {
	static int i = 0;
	static AtomicInteger s = new AtomicInteger(0);//这里换成了AtomicInteger对象
	public static void main(String[] args) throws Exception {
		Thread[] ts = new Thread[10];

		for (int k = 0; k < ts.length; k++) {
			ts[k] = new Thread(new Runnable() {

				@Override
				public void run() {
					for (int k = 1; k <= 10000; k++) {
						i++;
						s.incrementAndGet();//获取加1后的值 相当于++s
					}
				}
			});
			ts[k].start();
		}
		for (int k = 0; k < ts.length; k++) {
			ts[k].join();
		}
		System.out.println(i);//原来的结果
		System.out.println(s);//正确结果
	}
}

这次的结果:正确
s的结果正确
总结 一下 :这个方法是效率高的,线程安全的

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

见贤思齐焉,,

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值