【Java 并发】结束任务、中断线程 :blocked、interrupted


thinking in java 课后答案

Terminating task

Terminating when blocked

  1. New: A thread remains in this state only momentarily, as it is being created. It allocates any necessary system resources and performs initialization. At this point it becomes eligible to receive CPU time. The scheduler will then transition this thread to the runnable or blocked state.
  2. Runnable: This means that a thread can be run when the time-slicing mechanism has CPU cycles available for the thread. Thus, the thread might or might not be running at any moment, but there’s nothing to prevent it from being run if the scheduler can arrange it. That is, it’s not dead or blocked.
  3. Blocked: The thread can be run, but something prevents it. While a thread is in the blocked state, the scheduler will simply skip it and not give it any CPU time. Until a thread reenters the runnable state, it won’t perform any operations.
  4. Dead: A thread in the dead or terminated state is no longer schedulable and will not receive any CPU time. Its task is completed, and it is no longer runnable. One way for a task to die is by returning from its run( ) method, but a task’s thread can also be interrupted, as you’ll see shortly.

线程共有四种状态,New(刚初始化好,可执行)/Runnable(执行中)/Blocked(被某种原因阻塞,等待信号返回执行态)/Dead(任务执行完毕。从run()中返回和被打断都会使线程死亡)

Becoming blocked

  • You’ve put the task to sleep by calling sleep(milliseconds), in which case it will not be run for the specified time.
  • You’ve suspended the execution of the thread with wait( ). It will not become runnable again until the thread gets the notify( ) or notifyAll( ) message (or the equivalent signal( ) or signalAll( ) for the Java SE5 java.util.concurrent library tools). We’ll examine these in a later section.
  • The task is waiting for some I/O to complete.
  • The task is trying to call a synchronized method on another object, and that object’s lock is not available because it has already been acquired by another task

有四种线程陷入blocked的情况:
1、使用sleep();
2、wait(),此方法需要调用notify()、signal()函数才能唤醒;
3、等待I/O操作;
4、在调用其他对象的synchronized函数时被阻塞


The problem we need to look at now is this:Sometimes you want to terminate a task that is in a blocked state. If you can’t wait for it to get to a point in the code where it can check a state value and decide to terminate on its own, you have to force the task out of its blocked state.

我们聚焦:当不想等待任务自动从blocked退出时,强制退出blocked状态的方法

Interruption

抛出异常可以使线程跳出run(),但是在catch中需要注意清理所以相关资源

So that you can terminate a blocked task, the Thread class contains the interrupt( ) method. This sets the interrupted status for that thread. A thread with its interrupted status set will throw an InterruptedException if it is already blocked or if it attempts a blocking operation. The interrupted status will be reset when the exception is thrown or if the task calls Thread.interrupted( ). As you’ll see, Thread.interrupted( ) provides a second way to leave your run( ) loop, without throwing an exception.

调用interrupt()必须要有一个Thread对象,但是concurrency library 推荐使用Executors 而不是 Thread。在executor中调用shutdownNow()将会对每个启动的线程发送interrupt()指令,所有任务即将终止。
然而我们有时候只需要打断其中一个任务,此时我们使用submit(THREAD)函数替代execute(),此函数返回一个Future类,此类包含一个cancel(TRUE/FALSE)方法,可以打断此任务。

However, there are times when you may want to only interrupt a single task. If you’re using Executors, you can hold on to the context of a task when you start it by calling submit( ) instead of execute( ). submit( ) returns a generic Future<?>, with an unspecified parameter because you won’t ever call get( ) on it the point of holding this kind of Future is that you can call cancel( ) on it and thus use it to interrupt a particular task. If you pass true to cancel( ), it has permission to call interrupt( ) on that thread in order to stop it; thus cancel( ) is a way to interrupt individual threads started with an Executor.

import java.util.concurrent.*;
import java.io.*;
import static net.mindview.util.Print.*;

class SleepBlocked implements Runnable {
    public void run() {
        try {
            TimeUnit.SECONDS.sleep(100);
        } catch(InterruptedException e) {
            print("InterruptedException in SleepBlocked");
        }
        print("Exiting SleepBlocked.run()");
    }
}

class IOBlocked implements Runnable {
    private InputStream in;
    public IOBlocked(InputStream is) { in = is; }
    public void run() {
        try {
            print("Waiting for read():");
            in.read();
        } catch(IOException e) {
            if(Thread.currentThread().isInterrupted()) {
                print("Interrupted from blocked I/O");
            } else {
                throw new RuntimeException(e);
            }
        }
        print("Exiting IOBlocked.run()");
    }
}
class SynchronizedBlocked implements Runnable {
    public synchronized void f() {
        while(true) // Never releases lock
            Thread.yield();
    }
    public SynchronizedBlocked() {
        new Thread(() -> {
            f(); // Lock acquired by this thread
        }).start();
    }
    public void run() {
        print("Trying to call f()");
        f();
        print("Exiting SynchronizedBlocked.run()");
    }
}

public class Interrupting {
    private static ExecutorService exec =
            Executors.newCachedThreadPool();
    static void test(Runnable r) throws InterruptedException{
        Future<?> f = exec.submit(r);
        TimeUnit.MILLISECONDS.sleep(1000);
        print("Interrupting " + r.getClass().getName());
        f.cancel(true); // Interrupts if running
        print("Interrupt sent to " + r.getClass().getName());
    }
    public static void main(String[] args) throws Exception {
        test(new SleepBlocked());
        test(new IOBlocked(System.in));
        test(new SynchronizedBlocked());
        TimeUnit.SECONDS.sleep(3);
        print("Aborting with System.exit(0)");
        System.exit(0); // ... since last 2 interrupts failed
    }
}

在这里插入图片描述
从输出中看到 f.cancel(true); 可以中断对sleep()的调用(或任何需要捕获InterruptedException的调用)。但是不能中断正在尝试获取同步锁的任务或正在尝试执行I/O的任务(第二三个类中代码段未完全执行)。

另一种中断blocked的方法是关闭任务所持资源

public class CloseResource {
    public static void main(String[] args) throws Exception{
        ExecutorService exec=Executors.newCachedThreadPool();
        ServerSocket server = new ServerSocket(8080);
        InputStream socketinput= new Socket("localhost",8080).getInputStream();
        exec.execute(new IOBlocked(socketinput));
        exec.execute(new IOBlocked(System.in));
        TimeUnit.MILLISECONDS.sleep(100);
        print("Shutting down all threads");
        exec.shutdownNow();

        TimeUnit.SECONDS.sleep(1);
        print("Closing "+socketinput.getClass().getName());
        socketinput.close(); //releases blocked thread
        TimeUnit.SECONDS.sleep(1);
        print("Closing "+ System.in.getClass().getName());
        System.in.close();//Release blocked thread
    }
}

调用shutdownNow()之后,在两个输入流上调用close之前的延迟强调了一旦底层资源关闭,任务便会解除阻塞。有趣的是,当关闭Socket时会出现interrupt(),而在关闭System.in时却没有。
在这里插入图片描述
在I/O一章中介绍的nio类提供了更文明的I/O中断。 阻塞的nio通道会自动响应中断。这里略过。

Note that using execute( ) to start both tasks and calling e.shutdownNow( ) will easily terminate everything; capturing the Future in the example above was only necessary to send the interrupt to one thread and not the other

总结三种 interrupt任务的方法

    //use Thread + interrupt()
        Thread t1= new Thread(new CallSleep());
        t1.start();
        TimeUnit.SECONDS.sleep(3);
        t1.interrupt();
   //use Future.cancel()
        ExecutorService exec= Executors.newCachedThreadPool();
        Future<?> f= exec.submit(new CallSleep());
        TimeUnit.SECONDS.sleep(2);
        f.cancel(true);
        if(f.isCancelled()){
            exec.shutdown();
        }
   // use Executors.shutdown()
        exec.execute(new CallSleep());
        exec.shutdownNow();
    }

Blocked by a mutex

在同一个类中的两个函数轮换持有锁是可行的,因为隶属同一个任务。
不同于上文中I/O操作无法被打断,使用ReentrantLocks 的blocked机制是可被打断的。

One of the features added in the Java SE5 concurrency libraries is the ability for tasks blocked on ReentrantLocks to be interrupted, unlike tasks blocked on synchronized methods or critical sections:

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import static net.mindview.util.Print.*;

class BlockedMutex {
    private Lock lock = new ReentrantLock();
    public BlockedMutex() {
// Acquire it right away, to demonstrate interruption of a task blocked on a ReentrantLock:
        lock.lock();
    }
    public void f() {
        try {
// This will never be available to a second task
            lock.lockInterruptibly(); // Special call
            print("lock acquired in f()");
        } catch(InterruptedException e) {
            print("Interrupted from lock acquisition in f()");
        }
    }
}
class Blocked2 implements Runnable {
    BlockedMutex blocked = new BlockedMutex();
    BlockedMutex blocked2 = new BlockedMutex();
    public void run() {
        print("Waiting for f() in BlockedMutex");
        blocked.f();
      //  blocked2.f(); blocked2 never get the lock
        print("Broken out of blocked call");
    }
}
public class Interrupting2 {
    public static void main(String[] args) throws Exception {
        Thread t = new Thread(new Blocked2());
        t.start();
        TimeUnit.SECONDS.sleep(1);
        System.out.println("Issuing t.interrupt()");
        t.interrupt();
    }
}

Checking for an interrupt

If you can only exit by throwing an exception on a blocking call, you won’t always be able to leave the run( ) loop. Thus, if you call interrupt( ) to stop a task, your task needs a second way to exit in the event that your run( ) loop doesn’t happen to be making any blocking calls.
This opportunity is presented by the interrupted status, which is set by the call to interrupt( ). You check for the interrupted status by calling interrupted( ). This not only tells you whether interrupt( ) has been called, it also clears the interrupted status. Clearing the interrupted status ensures that the framework will not notify you twice about a task being interrupted. You will be notified via either a single InterruptedException or a single successful Thread.interrupted( ) test. If you want to check again to see whether you were interrupted, you can store the result when you call Thread.interrupted( ).

import java.util.concurrent.*;

class NeedsCleanup {
    private final int id;
    public NeedsCleanup(int ident) {
        id = ident;
        System.out.println("NeedsCleanup " + id);
    }
    public void cleanup() {
        System.out.println("Cleaning up " + id);
    }
}
class Blocked3 implements Runnable {
    private volatile double d = 0.0;
    public void run() {
        try {
            while(!Thread.interrupted()) {
// point1
                NeedsCleanup n1 = new NeedsCleanup(1);
// Start try-finally immediately after definition of n1, to guarantee proper cleanup of n1:
                try {
                    System.out.println("Sleeping");
                    TimeUnit.SECONDS.sleep(1);
// point2
                    NeedsCleanup n2 = new NeedsCleanup(2);
// Guarantee proper cleanup of n2:
                    try {
                        System.out.println("Calculating");
						// A time-consuming, non-blocking operation:
                        for(int i = 1; i < 2500000; i++)
                            d = d + (Math.PI + Math.E) / d;
                        System.out.println("Finished time-consuming operation");
                    } finally {
                        n2.cleanup();
                    }
                } finally {
                    n1.cleanup();
                }
            }
            System.out.println("Exiting via while() test");
        } catch(InterruptedException e) {
            System.out.println("Exiting via InterruptedException");
        }
    }
}
public class InterruptingIdion {
    public static void main(String[] args) throws Exception{
        if(args.length!=1){
            System.out.println("usage:java InterruptingIdion delay-in-ms");
            System.exit(1);
        }
        Thread t= new Thread(new Blocked3());
        t.start();
        TimeUnit.MILLISECONDS.sleep(new Integer(args[0]));
        t.interrupt();
    }
}

try-finally保证两个cleanup()总能被调用。输入不同的值可在不同point位置中断。

By using different delays, you can exit Blocked3.run( ) at different points in the loop: in the blocking sleep( ) call, and in the non-blocking mathematical calculation. You’ll see that if interrupt( ) is called after the comment “point2” (during the non-blocking operation), first the loop is completed, then all the local objects are destroyed, and finally the loop is exited at the top via the while statement. However, if interrupt( ) is called between “pointi” and “point2” (after the while statement but before or during the blocking operation sleep( )), the task exits via the InterruptedException, the first time a blocking operation is attempted. In that case, only the NeedsCleanup objects that have been created up to the point where the exception is thrown are cleaned up, and you have the opportunity to perform any other cleanup in the catch clause.

发布了27 篇原创文章 · 获赞 1 · 访问量 694

猜你喜欢

转载自blog.csdn.net/SUKI547/article/details/101350176