阻塞操作是在事件发生之前,阻止当前线程执行。典型的阻塞操作是那些涉及控制台、文件或网络的输入或输出操作。
如果在锁临界区内部使用阻塞操作,则会使应用性能恶化。当一个线程等待将结束阻塞操作的事件时,应用的其它部分可能也在等待相同的事件。然而其余线程无法访问临界区且执行其代码(临界区的代码)。
本节将实现这种情况的范例,线程从临界区中的控制台读取行。这个指令将阻塞应用的其余线程,直到引入此行。
准备工作
本范例通过Eclipse开发工具实现。如果使用诸如NetBeans的开发工具,打开并创建一个新的Java项目。
实现过程
通过如下步骤实现范例:
-
创建名为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() { System.out.printf("%s: Starting\n", Thread.currentThread().getName());
-
使用lock()方法获取锁:
lock.lock();
-
调用criticalSection()方法:
try { criticalSection();
-
从控制台读取行:
System.out.printf("%s: Press a key to continue: \n", Thread.currentThread().getName()); InputStreamReader converter = new InputStreamReader(System.in); BufferedReader in = new BufferedReader(converter); String line=in.readLine(); } catch (IOException e) { e.printStackTrace();
-
在finally部分中使用unlock()方法释放锁:
} finally { lock.unlock(); } }
-
实现criticalSection()方法,等待随机时间周期:
private void criticalSection() { Random random=new Random(); int wait=random.nextInt(10); System.out.printf("%s: Wait for %d seconds\n", Thread.currentThread().getName(),wait); try { TimeUnit.SECONDS.sleep(wait); } catch (InterruptedException e) { e.printStackTrace(); } } }
-
实现本范例主类,创建名为Main的类,包含main()方法:
public class Main { public static void main(String[] args) {
-
创建名为lock的新的ReentrantLock对象,创建10个Task对象和10个执行它们的线程:
ReentrantLock lock=new ReentrantLock(); for (int i=0; i<10; i++) { Task task=new Task(lock); Thread thread=new Thread(task); thread.start(); } } }
工作原理
当执行本范例时,10个线程开始执行,但只有一个进入在run()方法中实现的临界区。当每个任务在释放锁之前从控制台读取行时,所有应用都将被阻塞,直到在控制台中引入文本。
更多关注
- 本章“保持锁的时间尽可能短”小节