使用变量句柄

Java 9并发编程指南 目录

变量句柄是Java 9的一个新特性,获取对变量(属性、静态字段或数组元素)的类型化引用,以便以不同的模式进行访问。例如,通过允许对此变量的原子访问来保护在并发应用程序中对其访问。到目前为止,只能使用原子变量获得此行为,但现在能够使用变量句柄获得相同的功能,且无需使用任何同步机制。变量句柄还能够得到访问变量的附加模式。

本节将学习如获得和使用变量句柄,以及使用变量句柄的好处。

准备工作

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

实现过程

通过如下步骤实现范例:

  1. 创建名为Account的类,包含两个名为amount和unsafeAmount的公有双精度属性,实现类构造函数,初始化属性值:

    public class Account {
    	public double amount;
    	public double unsafeAmount;
    	
    	public Account() {
    		this.amount=0;
    		this.unsafeAmount=0;
    	}
    }
    
  2. 创建名为Decrementer的类,指定其实现Runnable接口,包含在类构造函数中初始化的私有Account属性:

    public class Decrementer implements Runnable{
    
    	private Account account;
    	
    	public Decrementer(Account account) {
    		this.account = account;
    	}
    
  3. 实现run()方法,此方法将在amount和unsafeAmount属性中进行10000个递减操作。使用VarHandle来修改amount属性值,使用MethodHandles类的lookup()方法得到属性,然后使用getAndAdd()方法修改属性值。为了修改unsafeAmount属性,使用=操作符:

    	@Override
    	public void run() {
    		VarHandle handler;
    		try {
    			handler = MethodHandles.lookup().in(Account.class).findVarHandle(Account.class, "amount",double.class);
    			for (int i = 0; i < 10000; i++) {
    				handler.getAndAdd(account, -100);
    				account.unsafeAmount -= 100;
    			}
    		} catch (NoSuchFieldException | IllegalAccessException e) {
    			e.printStackTrace();
    		}
    	}
    }
    
  4. 实现名为Incrementer的类,此类与Decrementer类相似,但是递增账户值。此类的源代码不在此列出。

  5. 最后,实现包含main()方法的Main类。首先,创建account对象:

    public class Main {
    
    	public static void main(String[] args) {
    		Account account = new Account();
    
  6. 然后,创建执行Incrementer和Decremente任务的线程。启动这两个线程,使用join()方法等待线程执行结束:

    		Thread threadIncrementer = new Thread(new Incrementer(account));
    		Thread threadDecrementer = new Thread(new Decrementer(account));
    		threadIncrementer.start();
    		threadDecrementer.start();
    		
    		try {
    			threadIncrementer.join();
    			threadDecrementer.join();
    		} catch (InterruptedException e) {
    			e.printStackTrace();
    		}
    
  7. 最后,输出amount和unsafeAmount属性值到控制台:

    		System.out.printf("Safe amount: %f\n", account.amount);
    		System.out.printf("Unsafe amount: %f\n", account.unsafeAmount);
    	}
    }
    

工作原理

下图显示本范例的输出结果:

pics/07_06.jpg

因为设置的递增和递减操作次数相同,所以两个属性的期望结果应是0。amount属性得到这个结果,因为当使用VarHandle访问它时,确保对其修改进行原子访问。另一方面,unsafeAmount没有得到预期值,因为访问值时不受保护,具备数据竞争条件。

为了使用变量句柄,首先要使用MethodHandles类的lookup()方法得到此变量,接下来是in()方法和findVarHandle()方法。lookup()方法返回Lookup对象,in()方法返回指定类的Lookup对象,在本范例中是Account类,然后findVarHandle()为要访问的属性生成VarHandle。

一旦具有VarHandle对象,就能够使用不同的方法来应用不同的访问模式。本范例中用到了getAndAdd()方法。此方法保证原子访问来增加属性值,将要访问的对象和增量的值传递给它们。

接下来提供关于不同访问模式和在每种情况下可以使用的方法的更多信息。

扩展学习

对于具有变量句柄的变量,有四种不同的访问类型:

  • **读模式:**用于获取对变量的读模式访问,可以使用如下方法:
    • get():如果变量被声明为非易失性,读取变量值
    • getVolatile():如果变量被声明为易失性,读取变量值
    • getAcquire():读取变量值,确保修改或访问该此变量的指令不会在用于优化目的的指令之前被重新排序
    • getOpaque():读取变量值,确保当前线程指令不会被重新排序,但无法保证其它线程
  • **写模式:**用于获取对变量的写模式访问。可以使用set()、setVolatile()、setRelease()和setOpaque()方法,这些方法与读模式方法相似,不过具有写入访问。
  • **原子访问模式:**用于获取具有操作的原子变量提供的相似功能 ,例如,比较和获取变量值。可以使用如下方法:
    • compareAndSet():如果作为参数传递的期望值等于变量的当前值,将变量的值更改为volatile变量
    • weakCompareAndSet()和weakCompareAndSetVolatile():如果作为参数传递的期望值等于变量的当前值,因为它被分别声明为非易失性变量或易失性变量,则可能以原子方式改变变量值
  • **数值更新访问模式:**用原子方式修改数值。

更多关注

  • 本章“使用原子变量”和“使用原子数组”小节
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值