第四章_Java 并发编程基础

线程简介

线程优先级

在 Java 线程中,通过一个整形成员变量 priority 来控制优先级,优先级的范围从 1~10,在线程构建的时候可以通过 setPriority(int)方法来修改优先级,默认优先级是 5,优先级高的线程比优先级低的线程要分配更多的时间片。设置线程优先级时,针对频繁阻塞(休眠或者 I/O 操作)的线程需要设置较高的优先级,而偏重计算(需要较多 CPU 时间或者偏运算)的线程则设置较低的优先级,确保处理器不会被独占。

线程优先级不能作为程序正确性的依赖,因为操作系统可以完全不同理会 Java 线程对于优先级的设定。

线程的状态
状态 说明
NEW 初始状态,线程被构建,但是还没有调用 start 方法
RUNNABLE 运行状态,Java 线程将操作系统中的就绪和运行两种状态笼统的称为运行中
BLOCKED 阻塞状态,表示线程阻塞于锁
WAITING 等待状态,表示线程进入等待状态,进入该状态表示当前线程需要
TIME_WAITING 超时等待状态,该状态不同于 WAITING,它可以在指定的时间自行返回的
TEAMINATED 终止状态,表示当前线程已经执行完毕
第四章_Java 并发编程基础_第1张图片
image

线程在创建之后,调用 start 方法开始运行。当线程执行 wait 方法之后,线程进入等待状态。进入等待状态的线程需要依靠其他线程的通知才能返回运行状态,而超时等待状态相当于等待状态的基础上增加了超时限制,也就是超时时间到达时将会返回到运行状态。当线程调用同步方法时,在没有获取锁的情况下,线程将会进入阻塞状态。线程在执行 Runnable 的 run 方法之后将会进入到终止状态。

注意:Java 将操作系统中的运行和就绪两种状态合并为运行状态。阻塞状态是线程阻塞在进入synchronized 关键字修饰的方法或代码块时的状态,但是阻塞在 java.concurrent 包中 Lock 接口的线程状态却是等待状态,因为 java.concurrent 包中 Lock 接口对于阻塞的实现均使用了 LockSupport 类中的相关方法。

Daemon 线程

Daemon 线程是一种支持型线程,因为它主要被用作程序中后台调度以及支持性工作。这意味着,当一个 Java 虚拟机不存在非 Daemon 线程的时候,Java 虚拟机将会退出。可以通过调用 Thread.setDaemon(true) 将线程设置为 Daemon 线程。

Daemon 线程被用作完成支持型工作,所以在 Java 虚拟机退出时 Daemon 线程中的 finally 块不一定会执行到。

public class DaemonDemo {
    public static void main(String[] args) {
        Thread thread=new Thread(() -> {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }finally {
                System.out.println("Daemon");   //这一句实际上并没有执行到
            }
        });
        thread.setDaemon(true);
        thread.start();
    }
}

因此,在构建 Daemon 线程时,不能依靠 finally 块中的内容来确保执行关闭或者清理资源的逻辑。

线程的中断

理解中断

中断可以理解为线程的一个标识位属性,它表示一个运行中的线程是否被其他线程进行了中断操作。中断好比其他线程对该线程打了招呼,其他线程通过调用该线程的 interrupt() 方法对其进行中断操作。

线程通过检查自身是否被中断来进行响应,线程通过方法 isInterrupted() 来进行判断是否被中断,也可以调用静态方法 Thread.interrupted() 对当前线程的中断标识位进行复位。如果该线程已经处于终结状态,即使该线程被中断过,在调用对象的 isInterrupted() 时依旧会返回 false。

从 Java 的 API 中可以看到,许多声明抛出 InterruptedException 的方法( 例如 Thread.sleep(long millis)方法 ),在这些方法抛出该异常之前,Java 虚拟机会将该线程的中断标识位清楚,然后再抛出异常,此时调用 isInterrupted() 将会返回 false。

public class InterruptedDemo {
    public static void main(String[] args) {
        Thread threadA = new Thread(() -> {
            while (true) {
                try {
                    Thread.sleep(10);
                    int a = 0;  //让线程具有实际的意义
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        Thread threadB = new Thread(() -> {
            while (true) {

            }
        });
        threadA.start();
        threadB.start();
        try {
            Thread.sleep(2000); //让线程 A 和 B 充分运行
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        threadA.interrupt();
        threadB.interrupt();
        System.out.println(threadA.isInterrupted());
        System.out.println(threadB.isInterrupted());
    }
}
/**
 * 输出:
 * false
 * true
 */

可以看出抛出 InterruptedException 的线程标志位被清除了。

安全的中断线程

可以使用标识位来解决,其实和 interrupt 标识位是一样的原理,不过需要注意 interrupt 可以会因为抛异常而清除了标识位。

public class SafelyStopThread {
    public static void main(String[] args) throws InterruptedException {
        Runnable runnable = new WorkRunnable();
        Thread thread = new Thread(runnable);
        thread.start();
        Thread.sleep(1000);
        ((WorkRunnable) runnable).setCancel(true);

        Runnable runnable1 = new WorkRunnable();
        Thread thread1 = new Thread(runnable1);
        thread1.start();
        Thread.sleep(1000);
        thread1.interrupt();
    }

    static class WorkRunnable implements Runnable {
        private long i;
        private volatile boolean isCancel = false;

        @Override
        public void run() {
            while (!isCancel && !Thread.currentThread().isInterrupted()) {
                i++;
            }
            System.out.println(i);
        }

        public void setCancel(boolean cancel) {
            isCancel = cancel;
        }
    }
}

线程间通信

下篇讲(逃

你可能感兴趣的:(第四章_Java 并发编程基础)