Lock接口是Java并发API提供的基本机制之一,用来同步代码块。此接口定义了临界区,即访问共享资源的代码块,不能同时执行多个线程。这种机制通过Lock接口和ReentrantLock类实现。
本节讲学习关于Lock对象的信息以及如何获取这些信息。
准备工作
本范例通过Eclipse开发工具实现。如果使用诸如NetBeans的开发工具,打开并创建一个新的Java项目。
实现过程
通过如下步骤实现范例:
-
创建名为MyLock的类,继承ReentrantLock类:
public class MyLock extends ReentrantLock {
-
实现getOwnerName()方法,此方法返回具有锁(如果有)控制的线程名称,使用名为getOwner()的Lock类保护方法:
public String getOwnerName() { if (this.getOwner()==null) { return "None"; } return this.getOwner().getName(); }
-
实现getThreads()方法,此方法返回锁中排队的线程列表,使用名为getQueuedThreads()的Lock类保护方法:
public Collection<Thread> getThreads() { return this.getQueuedThreads(); } }
-
创建名为Task的类,实现Runnable接口:
public class Task implements Runnable{
-
声明名为lock的私有Lock属性:
private final Lock lock;
-
实现类构造函数,初始化属性:
public Task (Lock lock) { this.lock=lock; }
-
实现run()方法,创建重复五次的循环:
@Override public void run() { for (int i=0; i<5; i++) {
-
使用lock()方法得到锁,输出信息到控制台:
lock.lock(); System.out.printf("%s: Get the Lock.\n", Thread.currentThread().getName());
-
设置线程休眠500毫秒,使用unlock()方法释放锁,输出信息到控制台:
try { TimeUnit.MILLISECONDS.sleep(500); System.out.printf("%s: Free the Lock.\n", Thread.currentThread().getName()); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } } } }
-
实现本范例主类,创建名为Main的类,包含main()方法:
public class Main { public static void main(String[] args) throws Exception {
-
创建名为lock的MyLock对象:
MyLock lock=new MyLock();
-
创建包含五个thread对象的数组:
Thread threads[]=new Thread[5];
-
创建并启动五个线程,执行五个Task对象:
for (int i=0; i<5; i++) { Task task=new Task(lock); threads[i]=new Thread(task); threads[i].start(); }
-
创建重复15次的循环:
for (int i=0; i<15; i++) {
-
输出锁拥有者名字到控制台:
System.out.printf("Main: Logging the Lock\n"); System.out.printf("************************\n"); System.out.printf("Lock: Owner : %s\n",lock.getOwnerName());
-
显示排队等待锁定的线程的数量和名称:
System.out.printf("Lock: Queued Threads: %s\n", lock.hasQueuedThreads()); if (lock.hasQueuedThreads()){ System.out.printf("Lock: Queue Length: %d\n", lock.getQueueLength()); System.out.printf("Lock: Queued Threads: "); Collection<Thread> lockedThreads=lock.getThreads(); for (Thread lockedThread : lockedThreads) { System.out.printf("%s ",lockedThread.getName()); } System.out.printf("\n"); }
-
输出Lock对象的公平和状态信息到控制台:
System.out.printf("Lock: Fairness: %s\n",lock.isFair()); System.out.printf("Lock: Locked: %s\n",lock.isLocked()); System.out.printf("************************\n");
-
设置线程休眠1秒钟,关闭类循环:
TimeUnit.SECONDS.sleep(1); } } }
工作原理
本节实现了继承ReentrantLock类的MyLock类,返回不可用的信息,否则是ReentrantLock类的受保护数据。通过MyLock类实现了如下方法:
- getOwnerName():只有一个线程能够执行受Lock对象保护的临界区。这个锁存储正在执行临界区的线程,此线程通过ReentrantLock类的getOwner()方法返回。
- getThreads():当一个线程执行临界区时,其它线程在继续执行临界区之前试图设置此线程休眠。ReentrantLock类的getQueuedThreads()保护方法返回等待执行临界区的线程列表。
我们还使用了ReentrantLock类实现的其它方法:
- hasQueuedThreads():此方法返回Boolean值,指明是否有线程等待获取调用ReentrantLock
- getQueueLength():此方法返回等待获取调用ReentrantLock的线程数量
- isLocked():此方法返回Boolean值,指明调用ReentrantLock是否属于一个线程
- isFair():此方法返回Boolean值,指明调用ReentrantLock是否激活公平模式
扩展学习
ReentrantLock类中还有其它方法用来获取Lock对象的信息:
- getHoldCount():返回当前线程获得锁的次数
- isHeldByCurrentThread():返回Boolean值,指明当前线程是否拥有锁
更多关注
- 第二章“基础线程同步”中的“锁同步代码块”小节
- 第八章“定制并发类”中的“实现自定义Lock类”小节