锁是 Java Concurrency API 提供的基本同步机制之一。它使得程序员可以包含一段代码临界区,同时只有一个线程能执行那段代码。 它提供了以下2个操作:
在 Java Concurrency API 中,锁在 Lock 接口中被声明并在其他类(如 ReentrantLock 类)中被实现。
在本节中,将通过实现一个实现了能被用于包含一个临界区的 Lock 接口的类,来学习如何实现您自己的 Lock 对象。
本节的示例代码在 com.elanzone.books.noteeg.chpt7.sect09 package中
MyQueuedSynchronizer 类 : extends AbstractQueuedSynchronizer
private AtomicInteger state; public MyQueuedSynchronizer() { state = new AtomicInteger(0); }
@Override protected boolean tryAcquire(int arg) { return state.compareAndSet(0, 1); }
@Override protected boolean tryRelease(int arg) { return state.compareAndSet(1, 0); }
MyLock 类 : implements Lock
private AbstractQueuedSynchronizer sync; public MyLock() { sync = new MyQueuedSynchronizer(); }
@Override public void lock() { sync.acquire(1); } @Override public void lockInterruptibly() throws InterruptedException { sync.acquireInterruptibly(1); } @Override public boolean tryLock() { try { return sync.tryAcquireNanos(1, 1000); } catch (InterruptedException e) { e.printStackTrace(); return false; } } @Override public boolean tryLock(long time, TimeUnit unit) throws InterruptedException { return sync.tryAcquireNanos(1, TimeUnit.NANOSECONDS.convert(time, unit)); } @Override public void unlock() { sync.release(1); } @Override public Condition newCondition() { return sync.new ConditionObject(); }
任务类 : Task : implements Runnable
private MyLock lock; private String name; public Task(String name, MyLock lock) { this.name = name; this.lock = 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(); } }
控制类 : Main
public static void main(String[] args) { MyLock lock = new MyLock(); for (int i = 0; i < 10; i++) { Task task = new Task("Task-" + i, lock); Thread thread = new Thread(task); thread.start(); } 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); System.out.printf("Main: Got the lock\n"); lock.unlock(); System.out.printf("Main: End of the program\n"); }
Java Concurrency API 提供的 AbstractQueuedSynchronizer 类能被用于实现有锁或信号灯特性的同步机制。 它是一个抽象类。它提供控制对临界区的访问和对被阻塞等待访问临界区的线程队列的管理的操作。 这些操作基于以下2个抽象方法:
您必须在这些方法中实现用以控制对临界区访问的机制。 在本例中,实现了扩展 AbstractQueuedSyncrhonizer 类的 MyQueuedSynchonizer 类, 并用一个 AtomicInteger 变量来控制对临界区的访问c从而实现了前述抽象方法。
您已使用 AtomicInteger 类的 compareAndSet() 方法。该方法尝试将第一个参数的值与自身的值比较,如果想等则改为第二个参数的值。
您必须实现 MyQueuedSynchonizer 类, 因为 AbstractQueuedSynchronizer 类的其他实现(如 ReentrantLock 使用的)都是作为使用它的类的内部私有类实现的, 所以您没有权限使用它们。
然后,实现了 MyLock 类。此类实现了 Lock 接口并有一个 MyQueuedSynchronizer 对象属性。 为了实现 Lock 接口的所有方法,使用了 MyQueuedSynchronizer 对象的方法。
最后,实现了 Task 类。它实现了 Runnable 接口并使用一个 MyLock 对象以访问临界区。此临界区将线程催眠2秒。
Main 类创建一个 MyLock 对象并运行 10 个 Task 对象共享此锁。Main 类也用 tryLock() 方法尝试获得对此锁的权限。
运行此样例,您能看到只有一个线程有对临界区有权限访问,当线程结束后,其他的获得权限。
您能使用您自己的 Lock 来输出关于它的使用的信息到日志、控制锁的时间、或实现更高级的同步机制(例如控制对一个资源的访问这样只在特定的时间才可用)。