实现自定义Lock类

Java 9并发编程指南 目录

锁是Java并发API提供的基本同步机制之一,每次只有一个线程可以执行代码块,因此用来保护代码的关键部分。锁机制提供如下两种操作:

  • lock():当访问临界区时调用此操作,如果线程正在运行此临界区,其它线程将被阻塞直到锁得到临界区访问权限时才被唤醒。
  • unlock():在临界区结尾调用此方法,允许其它线程访问临界区。

在Java并发API中,锁在Lock接口中声明,且在一些类中实现,例如ReentrantLock类。

本节将通过实现Lock接口的类学习如何实现自定义Lock对象,用来保护临界区。

准备工作

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

实现过程

通过如下步骤实现范例:

  1. 创建名为MyAbstractQueuedSynchronizer的类,继承AbstractQueuedSynchronizer类:

    public class MyAbstractQueuedSynchronizer extends AbstractQueuedSynchronizer{
    
  2. 声明名为state的私有AtomicInteger属性:

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

    	public MyAbstractQueuedSynchronizer() {
    		state=new AtomicInteger(0);
    	}
    
  4. 实现tryAcquire()方法,此方法试图将状态变量值从0变成1。如果改变则返回true值,否则返回false:

    	@Override
    	protected boolean tryAcquire(int arg) {
    		return state.compareAndSet(0, 1);
    	}
    
  5. 实现tryRelease()方法,此方法试图将状态变量值从1变成0。如果改变则返回true值,否则返回false:

    	@Override
    	protected boolean tryRelease(int arg) {
    		return state.compareAndSet(1, 0);
    	}
    }
    
  6. 创建名为MyLock的类,指定其实现Lock接口:

    public class MyLock implements Lock {
    
  7. 声明名为sync的私有AbstractQueuedSynchronizer属性:

    	private final MyAbstractQueuedSynchronizer sync;
    
  8. 实现类构造函数,用新的MyAbstractQueueSynchronizer对象初始化sync属性:

    	public MyLock() {
    		sync=new MyAbstractQueuedSynchronizer();
    	}
    
  9. 实现lock()方法,调用sync对象的acquire()方法:

    	@Override
    	public void lock() {
    		sync.acquire(1);
    	}
    
  10. 实现lockInterruptibly()方法,调用sync对象的acquireInterruptibly()方法:

    	@Override
    	public void lockInterruptibly() throws InterruptedException {
    		sync.acquireInterruptibly(1);
    	}
    
  11. 实现tryLock()方法,调用sync对象的tryAcquireNanos()方法:

    	@Override
    	public boolean tryLock() {
    		try {
    			return sync.tryAcquireNanos(1, 1000);
    		} catch (InterruptedException e) {
    			e.printStackTrace();
    			Thread.currentThread().interrupt();
    			return false;
    		}
    	}
    
  12. 实现tryLock()方法的另一个版本,包含两个参数:名为time的长整型参数和unit的TimeUnit参数。调用sync对象的tryAcquireNanos()方法:

    	@Override
    	public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
    		return sync.tryAcquireNanos(1, TimeUnit.NANOSECONDS.convert(time, unit));
    	}
    
  13. 实现unlock()方法, 调用sync对象的release()方法:

    	@Override
    	public void unlock() {
    		sync.release(1);
    	}
    
  14. 实现newCondition()方法,创建sync对象内部类的新对象,名为ConditionObject:

    	@Override
    	public Condition newCondition() {
    		return sync.new ConditionObject();
    	}
    }
    
  15. 创建名为Task的类,指定其实现Runnable接口:

    public class Task  implements Runnable{
    
  16. 声明名为lock的私有MyLock属性:

    	private final MyLock lock;
    
  17. 声明名为name的私有String属性:

    	private final String name;
    
  18. 实现类构造函数,初始化属性:

    	public Task(String name, MyLock lock){
    		this.lock=lock;
    		this.name=name;
    	}
    
  19. 实现类的run()方法,获取锁,设置线程休眠2秒钟,然后释放锁对象:

    	@Override
    	public void run() {
    		lock.lock();
    		System.out.printf("Task: %s: Take the lock\n",name);
    		try {
    			TimeUnit.SECONDS.sleep(2);
    			System.out.printf("Task: %s: Free the lock\n",name);
    		} catch (InterruptedException e) {
    			e.printStackTrace();
    		} finally {
    			lock.unlock();
    		}
    	}
    }
    
  20. 通过创建名为Main的类,添加main()方法,实现本范例主类:

    public class Main {
    	public static void main(String[] args) {
    
  21. 创建名为lock的MyLock对象:

    		MyLock lock=new MyLock();
    
  22. 创建和执行10个Task任务:

    		for (int i=0; i<10; i++){
    			Task task=new Task("Task-"+i,lock);
    			Thread thread=new Thread(task);
    			thread.start();
    		}
    
  23. 使用tryLock()方法试图得到锁。等待1秒钟,如果没有得到锁,输出信息到控制台,然后再次试图:

    		boolean value;
    		do {
    			try {
    				value=lock.tryLock(1,TimeUnit.SECONDS);
    				if (!value) {
    					System.out.printf("Main: Trying to get the Lock\n");
    				}
    			} catch (InterruptedException e) {
    				e.printStackTrace();
    				value=false;
    			}
    		} while (!value);
    
  24. 输出指明得到锁且释放锁的信息到控制台:

    		System.out.printf("Main: Got the lock\n");
    		lock.unlock();
    
  25. 输出指明程序结束的信息到控制台:

    		System.out.printf("Main: End of the program\n");
    	}
    }
    

工作原理

Java并发API提供用来实现具有锁或信号的同步机制的类,称之为AbstractQueuedSynchronizer,从类名可以看出,这是一个抽象类。它提供控制对临界区访问的操作,并管理阻塞的线程队列,等待对临界区的访问。这些操作基于两个抽象方法:

  • tryAcquire():在试图访问临界区时调用此方法,如果调用此方法的线程能够访问临界区,它将返回true值,否则返回false。
  • tryRelease():在试图解除访问临界区时调用此方法,如果调用此方法的线程能够解除访问,它将返回true值,否则返回false。

在这些方法中,需要实现用来控制访问临界区的机制。本范例中,实现了MyAbstractQueuedSynchonizer类,此类继承AbstractQueuedSyncrhonizer类,并且使用AtomicInteger变量实现抽象方法来控制访问临界区。如果锁是空闲的,这个变量存储值为0,,因此线程能够访问临界区。如果锁是阻塞的话,则值为1,线程无法访问临界区。

  • 还用到AtomicInteger类提供的compareAndSet()方法,此方法试图更改指定为第一个参数的值,并将值指定为第二个参数。为了实现tryAcquire()方法,试图将原子变量值从零变成一。同样地,为了实现tryRelease()方法,试图将原子变量值从一变成零。

因为AbstractQueuedSynchronizer类的其它实现(例如,通过ReentrantLock使用的实现)作为私有类在内部实现,所以必须实现AtomicInteger类。这是在使用它的类中执行的,所以无权访问它。

然后实现了MyLock类,此类实现了Lock接口且具有作为属性的MyQueuedSynchronizer对象。为了实现Lock接口的所有方法,使用了MyQueuedSynchronizer对象的方法。

最后,实现了Task类,此类实现了Runnable接口,且使用MyLock对象获得临界区访问权,此临界区设置线程休眠2秒钟。main类创建了MyLock对象,然后运行10个共享此锁的Task对象。main类还使用tryLock()方法试图获得锁访问权。

当执行本范例时,能过观察到如何只有一个线程能够访问临界区,并且当线程结束执行时,下一个线程接着访问临界区。

也能够使用自定义的Lock接口输出接口使用情况的日志信息,控制接口锁定的时间,或者实现高级的同步机制进行控制,例如访问资源,使其只能在特定时间可用。

扩展学习

AbstractQueuedSynchronizer类提供了两个方法,能够用来管理锁的状态,分别是getState()和setState()方法。这些方法接收和返回包含锁状态的整型值,可以使用它们代替AtomicInteger属性来存储锁状态。

AbstractQueuedLongSynchronizer类是Java并发API提供的另一个实现同步机制的类,与AbstractQueuedSynchronizer类相同,但它使用long属性存储线程状态。

更多关注

  • 第二章“基础线程同步”中的“锁同步代码块”小节
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值