java 实现一个自定义的Lock类

实现一个自定义的Lock类

锁是Java并发API提供的基本同步机制之一。它允许程序员保护代码的临界区,所以,在某个时刻只有一个线程能执行这个代码块。它提供以下两种操作:

  • lock():当你想要访问一个临界区时,调用这个方法。如果有其他线程正在运行这个临界区,其他线程将阻塞,直到它们被这个锁唤醒,从而获取这个临界区的访问。
  • unlock():你在临界区的尾部调用这个方法,允许其他线程访问这个临界区。

在Java并发API中,锁是在Lock接口及其一些实现类中声明的,比如ReentrantLock类。

在这个指南中,你将学习如何实现你自己的Lock对象,它将实现一个实现了Lock接口并可用来保护临界区的类。

准备工作…

这个指南的例子使用Eclipse IDE实现。如果你使用Eclipse或其他IDE,如NetBeans,打开它并创建一个新的Java项目。

如何做…

按以下步骤来实现的这个例子:

1.创建一个继承AbstractQueuedSynchronizer类的MyQueuedSynchronizer类。

public class MyAbstractQueuedSynchronizer extends
AbstractQueuedSynchronizer {

2.声明一个私有的、AtomicInteger类型的属性state。

private AtomicInteger state;

3.实现这个类的构造器,并初始化它的属性。

public MyAbstractQueuedSynchronizer() {
    state=new AtomicInteger(0);
}

4.实现tryAcquire()方法。这个方法试图将变量state的值从0变成1。如果成功,它将返回true,否则,返回false。

@Override
protected boolean tryAcquire(int arg) {
    return state.compareAndSet(0, 1);
}

5.实现tryRelease()方法。这个方法试图将变量sate的值从1变成0.如果成功,它将返回true,否则,返回false。

@Override
protected boolean tryRelease(int arg) {
    return state.compareAndSet(1, 0);
}

6.创建一个MyLock类,并指定它实现Lock接口。

public class MyLock implements Lock{

7.声明一个私有的、AbstractQueuedSynchronizer类型的属性sync。

private AbstractQueuedSynchronizer 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();
        return false;
    }
}

12.实现其他版本的带有两个参数的tryLock()方法。一个long类型参数,名为time,一个TimeUnit类型参数,名为unit。调用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.声明一个私有的、MyLock类型的属性lock。

private MyLock lock;

17.声明一个私有的、String类型的属性name。

private String name;

18.实现这个类的构造器,并初始化它的属性。

public Task(String name, MyLock lock){
    this.lock=lock;
    this.name=name;
}

19.实现这个类的run()方法。获取锁,令线程睡眠2秒,然后,释放这个lock对象。

@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.创建一个MyLock对象,名为lock。

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.

在这些方法中,你已实现可用来控制临界区访问的机制。在你的例子中,你已实现继承AbstractQueuedSyncrhonizer类的MyQueuedSynchonizer类,并使用AtomicInteger变量实现抽象方法来控制临界区的访问。如果锁是自由的,这个变量的值为0,表明线程可以访问这个临界区。如果锁是阻塞的,这个变量的值为1,表明线程不能访问这个临界区。

你已使用AtomicInteger类提供的compareAndSet()方法,尝试将你指定的值作为第一个参数改变成你指定的值作为第二个参数。实现tryAcquire()方法,你尝试将原子变量的值从0变成1。同样地,你实现tryRelease()方法,尝试将原子变量的值从1变成0。

你必须实现这个类,因为AbstractQueuedSynchronizer类的其他实现(比如,所使用的ReentrantLock类)是作为私有的内部类使用来实现的,所以你不能访问它。

然后,你已实现MyLock类。这个类实现Lock接口,有一个MyQueuedSynchronizer对象属性。你已使用MyQueuedSynchronizer对象的方法,来实现Lock接口的所有方法。

最后,你实现了Task类,它实现了Runnable接口,并使用一个MyLock对象来控制临界区的访问。这个临界区令线程睡眠2秒。主类创建一个MyLock对象,并运行10个Task对象来共享这把锁。主类也使用tryLock()方法来尝试获取锁的访问。

当你执行这个例子,你可以看到只有一个线程可以访问这个临界区,并且当这个线程结束,其他线程可以继续访问这个临界区。

你可以使用你自己的锁来写入关于它的使用的日志信息,控制锁定时间,或实现先进的同步机制来控制。比如,只能在特定的时间内,才能对资源访问。

不止这些…

AbstractQueuedSynchronizer类提供两个方法可以用来控制锁的状态,它们就是getState()和setState()方法。这两个方法,接收和返回一个整数值作为锁的状态。你可以使用这两个方法而不是AtomicInteger属性来存储锁的状态。

Java并发API提供其他类来实现同步机制。它就是AbstractQueuedLongSynchronizer类,它与AbstractQueuedSynchronizer一样,除了使用一个long类型属性来存储线程的状态。

参见
在第2章,基本线程同步中的使用locks同步代码块指南

转载自:http://ifeve.com/customizing-concurrency-classes-9/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java中的机制是通过`java.util.concurrent.locks`包中的`Lock`接口和`ReentrantLock`实现的。`ReentrantLock`是一个可重入的互斥,它提供了与`synchronized`关键字似的功能,但具有更高的灵活性。 `ReentrantLock`使用的主要原理是`AQS`(AbstractQueuedSynchronizer),它是一个用于构建和同步器的框架。`AQS`使用一个双向链表来维护等待线程的队列,通过`CAS`(Compare and Swap)操作来实现线程的获取和释放。 在使用`ReentrantLock`时,可以使用`lock()`方法获取,并使用`unlock()`方法释放。如果某个线程已经持有了,那么再次调用`lock()`方法时,该线程可以继续持有,也就是可重入的特性。 动态代理是一种在运行时创建代理和实例的技术,它允许我们在不修改原始的情况下,对其方法进行增强或拦截。Java中的动态代理是通过`java.lang.reflect.Proxy`和`InvocationHandler`接口来实现的。 通过动态代理,我们可以在调用目标对象的方法前后添加额外的逻辑。首先,我们需要实现一个`InvocationHandler`接口,然后使用`Proxy.newProxyInstance()`方法创建代理对象。在`InvocationHandler`的`invoke()`方法中,我们可以在调用目标方法前后执行自定义的逻辑。 反射是Java中一种强大的机制,它允许我们在运行时获取、检查和修改的属性、方法和构造函数。通过反射,我们可以在不知道的具体信息的情况下,动态地创建对象、调用方法和访问属性。 Java中的反射是通过`java.lang.reflect`包中的`Class`、`Method`和`Field`等实现的。我们可以使用`Class.forName()`方法获取的`Class`对象,然后通过`Class`对象获取的方法和属性。 需要注意的是,虽然反射提供了很大的灵活性,但由于它会在一定程度上降低性能,所以在性能敏感的场景下应谨慎使用。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值