漫谈并发编程(四):终结任务

使用状态变量来终结任务

有时我们可以使用一个状态变量(如布尔值)来终结任务的执行,这种方式非常平和,且提供给你机会在任务终止前做一些操作。如:

public class StateStopTask implements Runnable{
     private static volatile boolean isCancled = false;
     public void run() {
          while(true) {
               if(isCancled == true) {
                    System.out.println("StateStopTask is stop");
                    return;
               }
          }
     }

     public static void main(String []args) {
          new Thread(new StateStopThread()).start();
          Thread.sleep(3000);
          isCancled = true;
     }
}

或者直接使用Thread.interrupt()来设置线程的中断标识,并且在程序中检测该标识,可以避免状态变量的定义。

public class StateStopTask implements Runnable{
     public void run() {
          while(!Thread.currentThread().isInterrupted()) {
                System.out.println("StateStopTask is running");
          }
          System.out.println("StateStopTask is stopping");
     }
     public static void main(String []args) {
          Thread t = new Thread(new StateStopThread()).start();
          Thread.sleep(3000);
          t.interrupt();
     }
}

在阻塞时终结

前面使用状态变量只能在线程运行到该检测时才能终止,有时我们需要线程无论在何时何地都能够被终止,尤其是在阻塞的时候。一个任务进入阻塞状态,可能有如下原因:

  1. 通过调用sleep(milliseconds)使任务进入休眠状态,在这种情况下,任务在指定的时间内不会运行。
  2. 你可以通过wait()使线程挂起。直到线程得到了notify()或notifyAll()消息(或者在JavaSE5的java.util.concurrent类库中等价的signal()或signalAll()消息),线程才会进入就绪状态。
  3. 任务在等待某个输入输出完成。
  4. 任务试图在某个对象上调用其同步控制方法,但是对象锁不可用,因为另一个任务已经获取了这个锁。

中断

程序在由于sleep或者wait()导致的阻塞情况下,如果被调用interrupt()方法将抛出interruptedExcetion异常,并且中断状态将清除。程序在由于IO和锁的阻塞时,调用interrupt()方法不会抛出interruptedException,但会设置中断状态。使用interrupted()可以检测中断状态并且如果正处于中断则清除中断状态,所以在程序中一般使用下面的方式检测中断,并做善后处理:

public class InterruptedTask implements Runnable{
     public void run() {
          while(!Thread.currentThread().interrupted()) {
               try {
                    System.out.println("InterruptedTask running");
                    Thread.sleep(100);
               } catch (InterruptedException e) {
                    System.out.println("interrupted by InterruptedException ")
               }
          }
          System.out.println("InterruptedTask Interrupted")

     }
}

现代java代码不推荐直接操作Thread对象,转而尽量通过Executor来执行所有操作。如果你在Executor上调用shutdownNow(),那么它将发送一个interrupt()调用给它启动的所有线程。这样做是常见的,因为一个Executor通常执行同一类任务,所以经常会希望关闭Executor的所有任务。然后,你有时会希望只中断单一的任务。如果使用Executor,那么通过调用submit()而不是executor()来启动任务,就可以持有该任务的上下文,submit()将返回一个泛型Future<?>,其中有一个未修饰的参数,因为你不能调用get(),使用这个Futrue对象可以调用cancle(),如果传递true给cancle(),那么在这个线程上就会调用interrupt()方法。

一般由于锁而导致的阻塞无法中断,但原子锁(ReentrantLock)支持可中断的阻塞操作lockInterruptibly()。

时间: 2024-12-25 03:12:38

漫谈并发编程(四):终结任务的相关文章

漫谈并发编程(一) - 并发简介

并发编程是每个程序员进阶的必修之课,想写一个安全稳定,性能强劲的并发程序可没那么容易.我将在未来的日子里,与大家分享一个并发小白成长路上的所思所想.并发编程的思想是通的,但是例子得要是具现的,在该系列中将使用java语言用以演示. 此文作为为漫谈并发编程系列的第一篇,由于本人喜欢先论理再论事,而非先论事再论理,所以就以一篇对并发的文字描述开头了. 并发编程由来 早年的计算机中没有操作系统,在某个时间段内只支持运行一个程序,并且这个程序能访问计算机的所有资源.在这个程序完全执行完后,再执行下一个程

漫谈并发编程(一) - 并发简单介绍

并发编程是每一个程序猿进阶的必修之课,想写一个安全稳定,性能强劲的并发程序可没那么easy.我将在未来的日子里,与大家分享一个并发小白成长路上的所思所想.并发编程的思想是通的,可是样例得要是具现的,在该系列中将使用java语言用以演示. 此文作为为漫谈并发编程系列的第一篇,探本溯源,以一篇对并发的文字描写叙述开头. 并发编程由来 早年的计算机中没有操作系统,在某个时间段内仅仅支持运行一个程序,而且这个程序能訪问计算机的全部资源.在这个程序全然运行完后,再运行下一个程序. 在此时,引入并发编程的优

Java 并发编程(四):如何保证对象的线程安全性

本篇来谈谈 Java 并发编程:如何保证对象的线程安全性. 01.前言 先让我吐一句肺腑之言吧,不说出来会憋出内伤的.<Java 并发编程实战>这本书太特么枯燥了,尽管它被奉为并发编程当中的经典之作,但我还是忍不住.因为第四章"对象的组合"我整整啃了两周的时间,才啃出来点肉丝. 读者朋友们见谅啊.要怪只能怪我自己的学习能力有限,真读不了这种生硬无趣的技术书.但是为了学习,为了进步,为了将来(口号喊得有点大了),只能硬着头皮上. 请随我来,我尽量写得有趣点. 02.线程安全类

漫谈并发编程(三):共享受限资源

解决共享资源竞争 一个不正确的访问资源示例 考虑下面的例子,其中一个任务产生偶数,而其他任务消费这些数字.这里,消费者任务的唯一工作就是检查偶数的有效性. 我们先定义一个偶数生成器的抽象父类. public abstract class IntGenerator { private volatile boolean canceled = false; public abstract int next( ); public void cancle( ) { canceled = true; } p

Go并发编程(四)

并发基础 多进程 多线程 基于回调的非阻塞/异步IO 协程 协程 与传统的系统级线程和进程相比,协程的最大优势在于其"轻量级",可以轻松创建上百万个而不会导致系统资源衰竭, 而线程和进程通常最多也不能超过1万个.这也是协程也叫轻量级线程的原因.多数语言在语法层面并不直接支持协程,而是通过库的方式支持,但用库的方式支持的功能也并不完整,比如仅仅提供轻量级线程的创建.销毁与切换等能力.如果在这样的轻量级线程中调用一个同步 IO 操作,比如网络通信.本地文件读写,都会阻塞其他的并发执行轻量级

漫谈并发编程(五):线程之间的协作

编写多线程程序需要进行线程协作,前面介绍的利用互斥来防止线程竞速是来解决线程协作的衍生危害的.编写线程协作程序的关键是解决线程之间的协调问题,在这些任务中,某些可以并行执行,但是某些步骤需要所有的任务都结束之后才能开动. wait()与notifyAll() wait()使你可以等待某个条件发生变化,wait()会在等待外部世界产生变化的时候将任务挂起,并且只有在notify()或notifyAll()发生时,即表示发生了某些感兴趣的事物,这个任务才会被唤醒并去检查所产生的变化. 调用sleep

漫谈并发编程(二):java线程的创建与基本控制

java线程的创建 定义任务 在java中使用任务这个名词来表示一个线程控制流的代码段,用Runnable接口来标记一个任务,该接口的run方法为线程执行的代码段. public class LiftOff implements Runnable { protected int countDown = 10; private static int taskCount = 0; private final int id = taskCount++; public void run() { whil

Java并发编程(四):并发容器(转)

解决并发情况下的容器线程安全问题的.给多线程环境准备一个线程安全的容器对象. 线程安全的容器对象: Vector, Hashtable.线程安全容器对象,都是使用 synchronized 方法实现的. concurrent 包中的同步容器,大多数是使用系统底层技术实现的线程安全.类似 native. Java8 中使用 CAS. 1.Map/Set 1.1 ConcurrentHashMap/ConcurrentHashSet 底层哈希实现的同步 Map(Set).效率高,线程安全.使用系统底

漫谈并发编程(六):java中一些常用的并发构件的介绍

CountDownLatch 它被用来同步一个或多个任务,强制它们等待其它任务执行的一组操作完成. 你可以向CountDownLatch对象设置一个初始计数值,任何在这个对象上调用await()的方法都将阻塞,直至这个计数值到达0.其它任务在结束其工作时,可以在该对象上调用countDown()来减小这个计数值.CountDownLatch被设计为只触发一次,计数值不能被重置.如果你需要能够重置值的版本,则可以使用CylicBarrier. 调用countDown()的任务在产生这个调用时并没有