原先多线程并发编程的学习笔记和代码整理一下贴上来。
---------------------------------
睡眠和中断
一、线程睡眠
1、sleep
Thread.sleep(time)方法应该比较常用,即在指定的毫秒数内让当前正在执行的线程休眠,如果有线程中断了当前线程,则抛出InterruptedException异常,且当前线程的中断状态被清除。
注意线程sleep时并不释放锁。
在javase5中java.util.concurrent.TimeUnit枚举类提供了另一种sleep方式:
TimeUnit.SECONDS.sleep(1);//1s
TimeUnit.MINUTES.sleep(1);//1min
...
这种方式不需要以毫秒作为时间单位,功能与sleep相同。
JDK文档:TimeUnit表示给定单元粒度的时间段,它提供在这些单元中进行跨单元转换和执行计时及延迟操作的实用工具方法。
二、线程中断
线程在sleep时,可以将其中断,但如果是因IO阻塞或synchronized同步导致的线程挂起,是不能被中断的。
1、sleep、io、synchronized中断
先定义3个任务:
sleep任务:睡眠5s
class SleepBlocked implements Runnable{
public void run() {
try {
System.out.println("Sleep Block.");
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
System.out.println("Sleep Interrupted!");
}
System.out.println("Exiting Sleep run...");
}
}
IO阻塞任务:读数据
class IOBlocked implements Runnable{
private InputStream in;
public IOBlocked(InputStream is){
this.in=is;
}
public void run() {
try {
System.out.println("I/O Blocked.");
in.read();//读数据
} catch (IOException e) {
if(Thread.currentThread().interrupted()){
System.out.println("Interrupted from I/O");
}
}
System.out.println("Exiting IOBlocked run...");
}
}
synchronized阻塞任务:block方法定义为synchronized,且方法内部无限循环会一直持有锁。
class SynchronizedBlocked implements Runnable{
public synchronized void block(){
System.out.println("Synchronized Blocked.");
while(true){
Thread.yield();//让步给其它线程
}
}
public void run() {
block();
System.out.println("Exit SynchronizedBlocked...");
}
}
测试时,先启动这3个任务,然后分别中断掉这三个任务。
ExecutorService exec=Executors.newCachedThreadPool();
Future f=exec.submit(new SleepBlocked());
Future f1=exec.submit(new IOBlocked(System.in));
Future f2=exec.submit(new SynchronizedBlocked());
Thread.yield();//让步一下
f.cancel(true);//true - 中断,false - 允许任务执行完
f1.cancel(true);
f2.cancel(true);
注意,取消任务时cancel的参数代表是否采用中断的方式结束。这里采用中断的方式,看看输出:
Sleep Block. I/O Blocked. Synchronized Blocked. Sleep Interrupted! Exiting Sleep run...
由此可见,3个任务启动后,分别进入阻塞状态,但只有sleep的中断起了作用,IO和synchronized的任务都没有被中断,而是处于一直阻塞等待的状态。
2、ReentrantLock中断
与synchronized不同的是,javase5的ReentrantLock是可以被中断的。这也是除了trylock外ReentrantLock与synchronized另一个不同点。
上代码:
首先,定义一个类
class ReentrantInterruptTask{
private Lock lock=new ReentrantLock();
public ReentrantInterruptTask(){
lock.lock();//创建对象时就获取锁.
}
public void block(){
try {
System.out.println("blocking...");
lock.lockInterruptibly();//线程阻塞,等待锁可用或者被中断。
System.out.println("this line not display.");
} catch (InterruptedException e) {
System.out.println("lock Interrupted!");
}
System.out.println("Exit block...");
}
}
该类在创建时的构造函数里就获取锁,然后在block方法中也去获取锁:
lock.lockInterruptibly():如果当前线程已经保持锁,则立即返回,如果锁被另一个线程保持,则该线程一直处于休眠状态,直到获取到锁或者当前线程被中断。
然后,定义一个任务,使该线程在block方法内阻塞:
class BlockTask implements Runnable{
private ReentrantInterruptTask rit = new ReentrantInterruptTask();
@Override
public void run(){
rit.block();
System.out.println("after block...");
}
}
最后启动并中断该线程:
Thread t=new Thread(new BlockTask());
t.start();
TimeUnit.SECONDS.sleep(2);
t.interrupt();//
控制台输出:
blocking... lock Interrupted! Exit block... after block...
由此可见,使用Lock接口的lockInterruptibly是可以实现因同步而阻塞的线程被中断的,这也是lock跟synchronized的不同之一。