当前位置: 首页 > Thinking in Java > 正文

第21章 – 并发 – 线程阻塞,线程中断,何时以及如何响应中断

第21章 – 并发 – 线程阻塞,线程中断,何时以及如何响应中断

 

1.在线程阻塞时中断

 

  线程的4中状态:

  (1) 新建(new):线程被创建时,它只会短暂的处于这种状态.

      此时线程已经分配了必须的系统资源,并执行了初始化.

      此刻线程已经有资格获取CPU时间,之后调度器将把这个线程转变为可运行状态

      或阻塞状态

  (2) 就绪(Runnable):在这种状态下,只要调度器把时间片分配给线程,线程就可以运行.

      也就是说,在任意时刻线程可以运行也可以不运行.只要调度器能分配时间片给线程,

      它就可以运行,这不同于阻塞和死亡状态.

  (3) 阻塞(Blocked): 线程能够运行,但有某个条件阻止它运行.当线程处于阻塞状态时,

      调度器将忽略该线程,不会分配给线程任何CPU时间.直到线程重新进入了就绪状态,

      它才有可能执行操作.

  (4) 处于死亡或终止状态的线程将不再是可调度的,并且也不会得到CPU时间,它的任务

      已结束,或不再是可运行的.任务死亡的通常方式是从run()方法返回,但任务的线程

      还可以被中断.

 

  任务进入阻塞状态的4个原因:

  (1) 通过调用sleep(milliseconds)使任务进入休眠状态,此时,任务在指定的时间内不会运行

  (2) 通过调用wait()是线程挂起,直到线程得到了notify()或notifyAll()消息(或signal(),

  signalAll()消息),线程才会进入就绪状态

  (3)任务在等待某个输入/输出完成

  (4) 任务视图在某个对象上调用其同步控制方法,但是对象的锁不可用,因为另一个任务已经

      获取了锁.

 

  另,在当前现场上调用其他线程的join()方法时,也会引起当前线程阻塞(Java并发编程实践(童云兰等译)P115).

 

2. sleep()造成的线程阻塞可以中断,而IO和synchronized造成的阻塞不可中断(即不能响应中断请求)

 

 

   中断 不可中断阻塞 的方法: 关闭在其上发生阻塞的底层资源.比如 IO阻塞时不可中断,

   在关闭这个IO时便可以中断.

 

   nio类库中的IO阻塞时会自动响应中断.

 

   在ReentrantLock上阻塞的任务具备可被中断的能力.

 

3. 线程的interrupted( )方法检查线程是否被中断,但是该调用同时会清除中断状态.

   因此重复调用该方法不能保证得到相同的值.

 

4. 何时以及如何响应中断?

 

   要中断某个线程,就调用这个线程的interrupt()方法.

   调用interrupt()方法方法并不意味着立即停止目标线程正在进行的工作,而只是传递了请求中断的消息.

   调用该方法后,中断只能在任务将要进入到阻塞操作中,或者任务已经在阻塞操作内部.(P701)

 

   如果调用interrupt()方法时,线程没有阻塞则无能为力(不能使线程中断).

   如下面的示例代码:

public class TestInterrupt
		{
		  public static void main(String[] args)
		  {
		    
		    Thread nonInterruptedThread = new Thread(new Runnable()
		    {
		      //这个run无法正常退出了,因为是无限循环
		      public void run()
		      {
		        while(true)
		        {
		          System.out.println("哈哈哈,你不能中断我.");
		        }
		      }
		    });
		    
		    nonInterruptedThread.start();
		    //这里调用interrupt无能为力,因为Runnable的run方法中没有任何阻塞发生的可能.
		    //所以线程需要用别的方法退出(就是书中所说的第二种方式退出P701).
		    nonInterruptedThread.interrupt(); 
		  }
		}

 上面代码的改善:在run()方法中检查当前线程的中断状态

public class TestInterrupt
		{
		  public static void main(String[] args)
		  {
		    
		    Thread nonInterruptedThread = new Thread(new Runnable()
		    {
		      public void run()
		      {
		        //这里检查了当前线程的中断状态,只有在没有中断时执行
		        while(!Thread.currentThread().isInterrupted())
		        {
		          System.out.println("好吧,你可以中断我.");
		        }
		      }
		    });
		    
		    nonInterruptedThread.start();
		    try
		    {
		      //让线程执行1秒之后中断.
		      TimeUnit.SECONDS.sleep(1);.
		      nonInterruptedThread.interrupt(); 
		    }
		    catch (InterruptedException e)
		    {
		      System.out.println("sleep被中断");
		    }
		  }
		}

 

可中断的阻塞,对中断的响应方式:提前返回,清除中断状态,抛出InterruptedException异常.

比如,Thread.sleep()或BlockingQueue.put().

注意,这里的清除中断状态,当代码中检测InterruptedException时,无法向上层抛出InterruptedException时,

应当在catch到这个异常中重新调用Thread.currentThread.interrupt();

 

所以,run()方法中通常的结构为:

public void run()
		{
			try()
			{
				while(!Thread.currentThread.isInterrupted())
				{
					...
					//假如这里有个操作在响应中断时,先清除中断状态,并InterruptedException抛出异常,
					//则Thread.currentThread.isInterrupted() 变成false了(因为中断状态在抛出异常中被清除了)
				}
			}
			catch(InterruptedException e)
			{
				//允许线程退出
				Thread.currentThread.interrupted();//重新设置中断状态.
			}	
		}

 

更合理的方式:

上面while(!Thread.currentThread.isInterrupted())循环中代码假如有个操作在响应中断时,

先清除中断状态,并抛出InterruptedException异常,则Thread.currentThread.isInterrupted() 

变成false了(因为中断状态在抛出异常中被清除了),那么while循环中检测的条件就不再可靠了

(此时因为抛出异常而将程序执行点已转到catch(InterruptedException e)中,所以还是要使用检测

InterruptedException异常的方式更为可靠).

 

下面是一种合理方式,中断抛出的InterruptedException异常不能被向上传递时,恢复中断状态,

没有使用while(!Thread.currentThread.isInterrupted())

并使用一个变量保存中断信息,使得finally中总是可以恢复中断状态.(Java并发编程实战P118)

public Task getNextTask(BlockingQueue<gaskgt> queue)
		{
			boolean interrupted = false;
			try
			{
				return queue.take();//可能响应中断的某个操作,
				//在中断时将清除中断状态,并抛出InterruptedException异常
			}
			catch(InterruptedException e)
			{
				interrupted = true;//为finally中处理保存中断状态
				//
				
			}
			finally
			{
				if(interrupted) {Thread.currentThread.interrupt();}//恢复中断状态
			}
		}

 

参考 Java 并发之线程中断 http://my.oschina.net/lifany/blog/163153 

 

5. 处理不可中断阻塞

 

   不可中断阻塞,要实现中断需要关闭当前正在阻塞的资源.

public class ReaderThread extends Thread 
   {
    private final Socket socket;
    private final InputStream in;

    public ReaderThread(Socket socket) throws IOException 
    {
        this.socket = socket;
        this.in = socket.getInputStream();
    }

    public void  interrupt()  
    //覆盖interrupt方法,在中断时先关闭 不可中断的阻塞的资源
    {
        try 
        {
            socket.close();
        }
        catch (IOException ignored) { }
        finally 
        {
            super.interrupt();
        }
    }

    public void run() 
    {
        try 
        {
            byte[] buf = new byte[BUFSZ];
            while (true)
            {
                int count = in.read(buf);
                if (count < 0)
                    break;
                else if (count > 0)
                    processBuffer(buf, count);
            }
        } 
        catch (IOException e) 
        { /*  Allow thread to exit  */  }
    }
}

 

赞 赏

   微信赞赏  支付宝赞赏


本文固定链接: https://www.jack-yin.com/coding/thinking-in-java/2142.html | 边城网事

该日志由 边城网事 于2015年03月18日发表在 Thinking in Java 分类下, 你可以发表评论,并在保留原文地址及作者的情况下引用到你的网站或博客。
原创文章转载请注明: 第21章 – 并发 – 线程阻塞,线程中断,何时以及如何响应中断 | 边城网事
关键字: ,

第21章 – 并发 – 线程阻塞,线程中断,何时以及如何响应中断 暂无评论

发表评论

快捷键:Ctrl+Enter