Java复习--线程(2)

72 阅读5分钟

线程弹出执行

Thread 类定义了 yield 方法。当前线程执行到 Thread.yield() 方法,会停止运行进入就绪状态。但线程切换到就绪状态后,什么时候被 JVM 调度回运行状态开发者无法控制。

public class ThreadDemo {
    public static void main(String[] args) {
       MyThread mythread = new MyThread();
       Thread t1 = new Thread(mythread);              
       Thread t2 = new Thread(mythread);    
       t1.start();
       t2.start();
    }

    static class MyThread implements Runnable {
        @Override
        public void run() {
            int count = 0;
            for (int i = 0; i < 10000; i++) {
                Thread.yield();                    // 切换到就绪状态
                count++;
                System.out.println(count);
            }         
        }
    }
}Copy to clipboardErrorCopied

线程暂停执行

Thread 类定义了 sleep 方法。当前线程执行到 Thread.sleep(1000) 方法,会停止运行进入阻塞状态,但仍会保持对象锁,其他线程不可访问其资源。直到超时后进入就绪状态。调用 sleep 方法需要捕获或抛出 InterruptException 异常。

public class ThreadDemo {
    public static void main(String[] args) {
       MyThread mythread = new MyThread();
       Thread t1 = new Thread(mythread);              
       Thread t2 = new Thread(mythread);    
       t1.start();
       t2.start();
    }

    static class MyThread implements Runnable {
        @Override
        public void run() {
            int count = 0;
            for (int i = 0; i < 10000; i++) {
                try{
                    Thread.sleep(1000);             // 当前线程暂停 1s
                } catch(InterruptException e){
                    e.printStackTrace();
                }

                count++;
                System.out.println(count);
            }         
        }
    }

}Copy to clipboardErrorCopied

线程交互

Object 类定义了 wait 和 notify 方法,通常被用于线程间交互/通信。必须在同步环境下(synchronized)使用,否则会抛出 IllegalMonitorStateException 异常。假定 obj 为同步环境上锁的对象:

线程等待

当前线程执行 obj.wait() 方法,线程会停止运行并释放对象锁 obj,其他线程可以访问其资源。同时线程进入 obj 对象的等待池,直到被 notify 方法唤醒进入就绪状态。调用 wait 方法需要捕获或抛出 InterruptException 异常。

wait 方法允许计时等待。当前线程执行 obj.wait(1000) 方法,计时结束后线程会被自动唤醒进入就绪状态。

线程唤醒

当前线程执行 obj.notify() 方法,会随机从 obj 对象等待池中选择一个线程唤醒,使其进入就绪状态。但是 notify 方法不会释放当前进程的对象锁,如果该线程持有 obj 对象的锁,当前线程释放锁后被唤醒的其他线程才能被执行。如果想被唤醒线程先执行,notify 方法后添加 wait 方法释放锁。

当前线程执行 obj.notifyall() 方法,会将所有 obj 对象等待池中所有线程唤醒。

public class ThreadDemo {
    public static void main(String[] args) {
        MyThread t = new MyThread("t");
        synchronized(t) {                         // 对 t 设置对象锁
            try {
                t.start();
                System.out.println("1");
                t.wait();                         // 当前线程释放 t 锁,进入 t 对象等待池
                System.out.println("4");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    static class MyThread extends Thread {
        @Override
        public void run() {
            synchronized (this) {                 // 对 t 设置对象锁        
                Thread.sleep(1000);
                System.out.println("2");
                this.notify();                    // 随机唤醒一个 t 对象等待池中的线程
                System.out.println("3");
            }
        }
    }
}Copy to clipboardErrorCopied

其他线程优先

Thread 类定义了 join 方法,其底层通过调用 wait 方法实现。当前线程执行 t.join() 方法,线程会停止运行并释放对象锁,同时线程进入线程 t 对象的等待池,直到被唤醒进入就绪状态。通常用于主线程 main,等到线程 t 终止时自动被唤醒。调用 join 方法需要捕获或抛出 InterruptException 异常。

同样允许计时等待。当前线程执行 t.join(1000) 方法,计时结束后线程会被自动唤醒进入就绪状态,无须等待子线程结束时唤醒。

public class ThreadDemo {
    public static void main(String[] args) {
       MyThread mythread = new MyThread();
       Thread t = new Thread(mythread);              
       t.start();
       System.out.println("1");
       t.join();                              // 子线程结束后才被唤醒
       System.out.println("3");
    }

    static class MyThread implements Runnable {
        @Override
        public void run() {
            Thread.sleep(1000);    
            System.out.println("2");
        }         
    }

}Copy to clipboardErrorCopied
public class ThreadDemo {
    public static void main(String[] args) {
       MyThread mythread = new MyThread();
       Thread t = new Thread(mythread);              
       t.start();
       System.out.println("1");
       t.join(500);                           // 0.5s 后自动被唤醒
       System.out.println("2");
    }

    static class MyThread implements Runnable {
        @Override
        public void run() {
            Thread.sleep(1000);    
            System.out.println("3");
        }         
    }
}Copy to clipboardErrorCopied

如果我们手动给线程 t 加锁,即使计时结束后线程被唤醒进入就绪状态,但仍无法立刻拿到锁进入运行状态。

public class ThreadDemo {
    public static void main(String[] args) {
       MyThread mythread = new MyThread();
       Thread t = new Thread(mythread);              
       t.start();
       System.out.println("1");
       t.join(500);                           // 自动被唤醒后仍要等待线程 t 释放锁
       System.out.println("3");
    }

    static class MyThread implements Runnable {
        @Override
        public void run() {
            synchronized (currentThread()) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {}   
                System.out.println("2");
            }
        }         
    }
}Copy to clipboardErrorCopied

线程中断

调用 t.stop() 方法可以强制终止线程 t 运行,但强制中断线程可能会造成意想不到的问题,已不推荐使用。

目前主要采用设置线程中断标志的方式,向线程发送中止信号。由线程自行终止运行:

  • 执行 t.interrupt() 方法,将线程 t 中断标志设为 true 。
  • 执行 t.isInterrupted() 方法,查看线程 t 中断标志。
  • 执行 t.interrupted() 方法,查看线程 t 中断标志然后将其设为 false 。
public class ThreadDemo {
    public static void main(String[] args) {
       MyThread mythread = new MyThread();
       Thread t = new Thread(mythread);              
       t.start();
       try {
            Thread.sleep(500);
        } catch (InterruptedException e) {}
       t.interrupt();                            // 设置中断标志为 true
    }

    static class MyThread implements Runnable {
        @Override
        public void run() {
            while (true) {
                System.out.print("hello");
                if(this.isInterrupted()){        // 查看中断标志,若为 true 结束循环
                    break;
                }
            }
        }        
    }

}Copy to clipboardErrorCopied

捕获异常

调用 t.interrupt() 方法时如果线程 t 处在阻塞/等待状态,会立即退出阻塞/等待状态,并抛出 InterruptedException 异常。因此在调用 sleep/wait/join 方法时需要捕获 InterruptedException 异常。

开发者经常用 t.interrupt() 方法使线程退出阻塞/等待状态,同时也可以在捕获异常时使线程立即终止。

public class ThreadDemo {
    public static void main(String[] args) {
        MyThread mythread = new MyThread();
        Thread t = new Thread(mythread);
        t.start();
        System.out.println("1");
        t.interrupt();                               // 设置中断标志,使线程 t 退出等待状态
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {}
        System.out.println("3");
    }

    static class MyThread implements Runnable {
        @Override
        public void run() {
            try {
                Thread.sleep(100000);
            } catch (InterruptedException e) {      // 被设置中断标志后会自动抛出异常
                System.out.println("2");
                return;                             // 捕获异常后终止线程
            }
        }
    }
}