使用原子变量代替同步

Java 9并发编程指南 目录

使用原子变量代替同步

当在多个线程之间共享数据时,必须使用同步机制保护对数据块的访问。可以在修改数据的方法声明中使用synchronized关键字,这样每次只能有一个线程修改数据。另一种方式是使用Lock类创建具有修改数据指令的临界区。

自从版本5开始,Java引入原子变量。当线程使用原子变量执行操作时,类的实现检查操作是否在一个步骤中完成。通常情况下,此操作得到变量值,在局部变量中修改值,然后试图将新值更改旧值。如果旧值仍然相同,那么它将执行更改。如果没有,该方法将再次开始操作。Java提供了以下类型的原子变量:

  • AtomicBoolean
  • AtomicInteger
  • AtomicLong
  • AtomicReference

在某些情况下,Java的原子变量性能比基于同步机制的解决方案更好(特别是关注每个独立变量的原子性时)。java.util.concurrent类包的一些类使用原子变量代替同步机制。本节将开发范例展示原子属性如何比同步机制提供更好的性能。

准备工作

本范例通过Eclipse开发工具实现。如果使用诸如NetBeans的开发工具,打开并创建一个新的Java项目。

实现过程

通过如下步骤实现不可变类:

  1. 创建名为TaskAtomic的类,指定其实现Runnable接口:

    public class TaskAtomic implements Runnable{
    
  2. 声明名为number的私有AtomicInteger属性:

    	private final AtomicInteger number;
    
  3. 实现类构造函数,初始化属性:

    	public TaskAtomic () {
    		this.number=new AtomicInteger();
    	}
    
  4. 实现run()方法,在重复1000000次的循环中,使用set()方法,将步骤的数量作为值分配给原子属性:

    	@Override
    	public void run() {
    		for (int i=0; i<1000000; i++) {
    			number.set(i);
    		}	
    	}
    }
    
  5. 创建名为TaskLock的类,指定其实现Runnable接口:

    public class TaskLock implements Runnable {
    
  6. 声明名为number的私有int属性和名为lock的私有Lock属性:

    	private Lock lock;
    	private int number;
    
  7. 实现类构造函数,初始化属性:

    	public TaskLock() {
    		this.lock=new ReentrantLock();
    	}
    
  8. 实现run()方法,在重复1000000次的循环中,将步骤的数量分发给整型属性。在分配之前需要得到锁,之后再释放锁:

    	@Override
    	public void run() {
    		for (int i=0; i<1000000; i++) {
    			lock.lock();
    			number=i;
    			lock.unlock();
    		}
    	}
    }
    
  9. 实现本范例主类,创建名为Main的类,包含main()方法:

    public class Main {
    	public static void main(String[] args) {
    
  10. 创建名为atomicTask的TaskAtomic对象:

    		TaskAtomic atomicTask=new TaskAtomic();
    
  11. 创建名为lockTask的TaskLock对象:

    		TaskLock lockTask=new TaskLock();
    
  12. 声明线程数量,创建Thread对象数组存储线程:

    		int numberThreads=50;
    		Thread threads[]=new Thread[numberThreads];
    		Date begin, end;
    
  13. 加载指定数量的线程执行TaskLock对象,计算和输出线程执行时间到控制台:

    		begin=new Date();
    		for (int i=0; i<numberThreads; i++) {
    			threads[i]=new Thread(lockTask);
    			threads[i].start();
    		}
    		
    		for (int i=0; i<numberThreads; i++) {
    			try {
    			threads[i].join();
    			} catch (InterruptedException e) {
    			e.printStackTrace();
    			}
    		}
    		end=new Date();
    		System.out.printf("Main: Lock results: %d\n", (end.getTime()-begin.getTime()));
    
  14. 加载指定数量的线程执行TaskAtomic对象,计算和输出线程执行时间到控制台:

    		begin=new Date();
    		for (int i=0; i<numberThreads; i++) {
    			threads[i]=new Thread(atomicTask);
    			threads[i].start();
    		}
    		for (int i=0; i<numberThreads; i++) {
    			try {
    				threads[i].join();
    			} catch (InterruptedException e) {
    				e.printStackTrace();
    			}
    		}
    		end=new Date();
    		System.out.printf("Main: Atomic results: %d\n", (end.getTime()-begin.getTime()));
    	}
    }
    

扩展学习

当运行本范例时,将看到使用原子变量的TaskAtomic任务的执行时间是如何始终比使用锁的TaskLock任务更少。如果使用synchronized关键字代替锁,将得到相似的结果。

本节结论是使用原子变量将比其它同步方法提供更好的性能。如果没有适合需要的原子类型,那么可以尝试自定义原子类型。

更多关注

  • 第八章“定制并发类”中的“实现自定义原子对象”小节
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值