《Java 线程编程》学习笔记5

《Java 线程编程》学习笔记5

第5章 完美终止线程

5.1 中断线程:interrupt()

  1. 当一个线程运行时,另一个线程可以调用对应的 Thread 对象的 interrupt() 方法来中断它:public void interrupt()
  2. 这个方法只是在目标线程中设置一个标志位,表示它已经被中断,并立即返回。该方法可能抛出 SecurityException,表示发出中断请求的线程没有权限中断其他线程
    • 在 Thread 上调用 checkAccess() 方法可以进行安全性检查,这个方法又会调用 SecurityManager 的 checkAccess(Thread) 方法。

警告:
SecurityException 属于 RuntimeException 的一个子类,因此,对于可能抛出此异常的 Thread 或 ThreadGroup 的任何方法,并不需要 try/catch 块。默认时,应用程序没有定义 SecurityManager。所以,在代码中需要进行一般性检查,使用方法 System.getSecurityManager()。如果返回 null,则表示没有安装 SecurityManager。如果没有返回 null,则调用 SecurityManager 时必须小心。常见覆写 SecurityManager 方法:

import java.io.*;  
class PasswordSecurityManager extends SecurityManager {  
    private String password;  
    PasswordSecurityManager(String password) {  
        super();  
        this.password = password;  
    }  
    private boolean accessOK() {  
        int c;  
        //DataInputStream dis = new DataInputStream(System.in);  
        BufferedReader dis = new BufferedReader(new InputStreamReader(System.in));  
        String response;  
        System.out.println("What's the secret password?");  
        try {  
            response = dis.readLine();  
            if (response.equals(password))  
                return true;  
            else  
                return false;  
        } catch (IOException e) {  
            return false;  
        }  
    }  
    public void checkRead(FileDescriptor filedescriptor) {  
        if (!accessOK())  
            throw new SecurityException("Not a Chance!");  
    }  
    public void checkRead(String filename) {  
        if (!accessOK())  
            throw new SecurityException("No Way!");  
    }  
    public void checkRead(String filename, Object executionContext) {  
        if (!accessOK())  
            throw new SecurityException("Forget It!");  
    }  
    public void checkWrite(FileDescriptor filedescriptor) {  
        if (!accessOK())  
            throw new SecurityException("Not!");  
    }  
    public void checkWrite(String filename) {  
        if (!accessOK())  
            throw new SecurityException("Not Even!");  
    }  
}   
5.1.1 中断休眠线程
  1. 代码示例:
public class SleepInterrupt extends Object implements Runnable {
    public void run() {
        try {
            System.out.println("in run - about to sleep for 20 seconds");
            Thread.sleep(20000);
            System.out.println("in run() - woke up");
        } catch(InterruptionException x) {
            System.out.println("in run() - interrupted while sleeping");
            return;
        }
        System.out.println("in run() - doing stuff after nap");
        System.out.println("in run() - leaving normally");
    }

    public static void main(String[] args) {
        SleepInterrupt si = new SleepInterrupt();
        Thread t = new Thread(si);
        t.start();
        // 确保新线程有机会运行一段时间
        try {
            Thread.sleep(2000);
        } 
        catch(InterruptedExcepted x) {
        }
        System.out.println("in main() - interrupting other thread");
        t.interrupt();
        System.out.println("in main() - leaving");
    }
}

/*
运行结果:
    in run()  - about to sleep for 20 seconds
    in main() - interrupting other thread
    in main() - leaving
    in run()  - interrupting while sleeping
*/
5.1.2 待决中断
  1. 上一个示例说明在线程 sleep() 时,会被 interrupt()。另外,如果在调用 sleep() 之前,中断已经被调用,那它会立即抛出 InterruptedException。
public class PendingInterrupt extends Object {
    public static void main(String[] args) {
        if(args.length > 0) {
            Thread.currentThread().interrupt();
        }
    }
    long startTime = System.currentTimeMillis();
    try {
        Thread.sleep(2000);
        System.out.println("was Not interrupted.");
    }
    catch (InterruptedException x) {
        System.out.println("was interrupted");
    }

    System.out.println("elapsedTime = " + (System.currentTimeMillis() - startTime));
}
/*
如果不带参数执行:
    was Not interrupted
    elapsedTime = 2080
带参数执行:
    was interrupted
    elapsedTime = 110
*/
5.1.3 使用 isInterrupted()
  1. 可以在 Thread 对象上调用 isInterrupted() 方法来检查任何线程的中断状态。
  2. public boolean isInterrupted()
5.1.4 使用 Thread.interrupted()
  1. 如果线程被中断,调用该函数,将返回 true,同时清除中断标志位。
  2. public static boolean isInterrupted()
5.1.5 InterruptedException

5.2 挂起和恢复线程运行

  1. 加入某个程序使用一个线程按顺序翻转图片达到动画显示的目的,当动画不可见时,就没有必要继续动画显示,直到窗口可见后,再次恢复动画。
5.2.1 使用淘汰的方法 suspend() 和 resume()

技巧:
这些方法和类已经被 Sun 公司淘汰,说明开发人员应该尽量避免使用它们。淘汰的方法仍然可以使用,但是编译代码时,就会发出警告。淘汰这些方法或类,表示他们过时了,或者使用时比较危险,有可能在将来的 JDK 版本中删除。

  1. suspend() 方法是在 JDK 1.2 中淘汰的方法,因为如果在不合适的时候挂起线程(如锁定共享资源时),此时可能会发生死锁条件(deadlock condition)。
5.2.2 在不恰当的时候挂起
  1. 下面的代码示例通过休眠来减缓运行,使线程更可能在不适当的时候被挂起。
5.2.3 不使用淘汰方法实现挂起和恢复
  1. 示例代码:
public class AlternateSuspendResume extends Object implements Runnable {

    private volatile int firstVal;
    private volatile int secondVal;
    private volatile boolean suspended; // 等待状态变量,用于跟踪让内部线程临时终止运行的请求

    public boolean areValesEqual() {
        return (firstVal == secondVal);
    }
    public void run() {
        try {
            suspend = false;
            firstVal = 0;
            secondVal = 0;
            workMethod();
        } catch(InterruptedException x) {
            System.out.println("interrupted while in workMethod()");
        }
    }
    private void workMethod() throws InterruptedException {
        int val = 1;
        while(true) {
            // 仅当挂起时才运行的代码
            waitWhileSuspended();

            stepOne(Val);
            stepTwo(Val);
            val++;

            // 仅当挂起时才运行的代码
            waitWhileSuspended();

            Thread.sleep(200);
        }
    }
    public void suspendRequest() {
        suspended = true;
    }
    public void resumeRequest() {
        suspended = false;
    }
    private void waitWhileSuspended() throws InterruptedException {
        // 这是一个『繁忙等待』技术的示例
        // 它是非等待条件改变的最佳途径
        // 因为它会不断请求处理器周期来检查执行
        // 更佳的技术是:使用 Java 内置的『等待-通知』机制
        while(suspended) {
            Thread.sleep(200);
        }
    }
}

技巧:
如果在代码中有多个安全的地方可以挂起线程,对所有这些安全地方,应该添加更多的 waitWhileSuspended() 方法调用。只要确保没有在持有锁时允许挂起就行了!应该频繁使用 waitWhileSuspended(),有助于线程对挂起请求的快速反应。同时,要记住,调用 waitWhileSuspended() 会耗费一些处理器资源,因此,也不要用得太频繁。

5.3 终止线程

5.3.1 使用淘汰的方法 stop()
  1. JDK 1.2 淘汰了 stop() 方法,因为它可能导致对象中数据的崩溃。
  2. 一个问题是,当线程突然终止时,很少有机会会执行清理工作。
  3. 另一个问题,当在某个线程上调用 stop() 方法时,线程释放它当前持有的所有锁。持有这些锁必定有某种合适的理由 —— 也许是组织其他线程访问尚未处于一致性状态的数据。突然释放锁可能使某些对象中的数据处于不一致的状态,而且不出现数据可能崩溃的任何警告。
5.3.2 取代 stop()
  1. 作为一种直接终止线程的替代技术,可使用 boolean 指示变量来判断线程是否应该继续执行。
public class AlternateStop extends Object implements Runnable {
    private volatile boolean stopRequested;
    private Thread runThread;

    public void run() {
        runThread = Thread.currentThread();
        stopRequested = false;
        int count = 0;
        while(! stopRequested) {
            System.out.println("Running ... count = " + count);
            count++;
            try {
                Thread.sleep(300);
            } catch(InterruptedException x) {
                Thread.currentThread().interrupt(); // 重新申明
            }
        }
    }

    /*
        函数说明: 将 stopRequested 设置成 true,并中断线程的执行
    */
    public void stopRequest() {
        stopRequest = true;
        if(runThread != null) {
            runThread.interrupt();
        }
    }
}   

5.5 守护线程

  1. 标记为守护(daemon)的线程将以全新的方式终止。守护线程用于后台支持任务,而且仅在普通,非守护线程仍然运行时才需要。当 VM 检测到只剩下一个守护线程时,就会退出。如果非守护线程仍然存活,VM 就不会退出。守护线程提供了一种管理某些后台处理的理想方法,它们只是在支持其他非守护线程时才需要。
  2. 示例代码:
public class DaemonThread extends Object implements Runnable {
    public void run() {
        System.out.println("entering run()");
        try {
            System.out.println("in run() - currentThread() = " + Thread.currentThread());
            while(true) {
                try {Thread.sleep(500);}
                catch(InterruptedException x) {}
                System.out.println("in run() - woke up again")
            }
        } finally {
            System.out.println("in run() - leaving run()")
        }
    }
}

public class DaemonThreadMain extends Object {
    public static void main(String[] args) {
        System.out.println("in main() - entering main()");

        Thread t = new Thread(new DaemonThread());
        t.setDaemon(true); // 这里设置为守护线程
        t.start();
        try {Thread.sleep(30000);}
        catch(InterruptedException x) {}

        System.out.println("in main() - leaving main()")
    }
}

警告:虽然守护线程非常有用,但是必须确保其他所有非守护线程消亡时,不会由于它的终止而产生危害。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值