写点什么

线程简介

作者:周杰伦本人
  • 2022 年 5 月 10 日
  • 本文字数:3900 字

    阅读完需:约 13 分钟

线程简介

六种线程状态

NEW:初始状态,线程被构建,但是还没有调用 start 方法


RUNNABLE:运行状态,Jaⅵa 线程将操作系统中的就绪和运行两种状态笼统地称作“运行中


BLOCKED:阻塞状态,表示线程阻塞于锁


WAITING:等待状态,表示线程进入等待状态,进入该状态表示当前线程需要等待其他线程做出一些特定动作(通知或中断)


TIME WAITING:超时等待状态,该状态不同于 WAITING,它是可以在指定的时间自行返回的


TERMINATED:终止状态,表示当前线程已经执行完毕



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

Daemon 线程

public class Daemon {    public static void main(String[] args) {        Thread thread = new Thread(new DaemonRunner(), "DaemonRunner");        thread.setDaemon(true);        thread.start();    }    static class DaemonRunner implements Runnable {        @Override        public void run() {            try {                SleepUtils.second(10);            } finally {                System.out.println("DaemonThread finally run.");            }        }    }}
复制代码


运行 Daemon 程序,可以看到在终端或者命令提示符上没有任何输出。main 线程(非 Daemon 线程)在启动了线程 DaemonRunner 之后随着 main 方法执行完毕而终止,而此时 Java 虚拟机中已经没有非 Daemon 线程,虚拟机需要退出。Java 虚拟机中的所有 Daemon 线程都需要立即终止,因此 DaemonRunner 立即终止,但是 DaemonRunner 中的 finally 块并没有执行。

线程构造

private void init(ThreadGroup g, Runnable target, String name,                  long stackSize, AccessControlContext acc,                  boolean inheritThreadLocals) {    if (name == null) {        throw new NullPointerException("name cannot be null");    }
this.name = name;
Thread parent = currentThread(); SecurityManager security = System.getSecurityManager(); if (g == null) { /* Determine if it's an applet or not */
/* If there is a security manager, ask the security manager what to do. */ if (security != null) { g = security.getThreadGroup(); }
/* If the security doesn't have a strong opinion of the matter use the parent thread group. */ if (g == null) { g = parent.getThreadGroup(); } }
/* checkAccess regardless of whether or not threadgroup is explicitly passed in. */ g.checkAccess();
/* * Do we have the required permissions? */ if (security != null) { if (isCCLOverridden(getClass())) { security.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION); } }
g.addUnstarted();
this.group = g; this.daemon = parent.isDaemon(); this.priority = parent.getPriority(); if (security == null || isCCLOverridden(parent.getClass())) this.contextClassLoader = parent.getContextClassLoader(); else this.contextClassLoader = parent.contextClassLoader; this.inheritedAccessControlContext = acc != null ? acc : AccessController.getContext(); this.target = target; setPriority(priority); if (inheritThreadLocals && parent.inheritableThreadLocals != null) this.inheritableThreadLocals = ThreadLocal.createInheritedMap(parent.inheritableThreadLocals); /* Stash the specified stack size in case the VM cares */ this.stackSize = stackSize;
/* Set thread ID */ tid = nextThreadID();}
复制代码


一个新构造的线程对象是由其 parent 线程来进行空间分配的,而 child 线程继承了 parent 是否为 Daemon、优先级和加载资源的 contextClassLoader 以及可继承的 ThreadLocal,同时还会分配一个唯一的 ID 来标识这个 child 线程。至此,一个能够运行的线程对象就初始化好了,在堆内存中等待着运行。

线程启动

线程对象在初始化完成之后,调用 start()方法就可以启动这个线程。线程 start()方法的含义是:当前线程(即 parent 线程)同步告知 Java 虚拟机,只要线程规划器空闲,应立即启动调用 start()方法的线程。


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


许多声明抛出 InterruptedException 的方法(例如 Thread.sleep(longmillis)方法)这些方法在抛出 InterruptedException 之前,Java 虚拟机会先将该线程的中断标识位清除,然后抛出 InterruptedException,此时调用 isInterrupted()方法将会返回 false。


首先创建了两个线程,SleepThread 和 BusyThread,前者不停地睡眠,后者一直运行,然后对这两个线程分别进行中断操作,观察二者的中断标识位。


package com.example.xppdemo.chapter4;
import java.util.concurrent.TimeUnit;
public class Interrupted { public static void main(String[] args) throws Exception {// sleepThread不停的尝试睡眠 Thread sleepThread = new Thread(new SleepRunner(), "SleepThread"); sleepThread.setDaemon(true);// busyThread不停的运行 Thread busyThread = new Thread(new BusyRunner(), "BusyThread"); busyThread.setDaemon(true); sleepThread.start(); busyThread.start();// 休眠5秒,让sleepThread和busyThread充分运行 TimeUnit.SECONDS.sleep(5); sleepThread.interrupt(); busyThread.interrupt(); System.out.println("SleepThread interrupted is " + sleepThread.isInterrupted()); System.out.println("BusyThread interrupted is " + busyThread.isInterrupted());// 防止sleepThread和busyThread立刻退出SleepUtils.second(2); }
static class SleepRunner implements Runnable { @Override public void run() { while (true) { SleepUtils.second(10); } } }
static class BusyRunner implements Runnable { @Override public void run() { while (true) { } } }}
复制代码


public class SleepUtils {    public static final void second(long seconds) {        try {            TimeUnit.SECONDS.sleep(seconds);        } catch (InterruptedException e) {        }    }}
复制代码


输出:


SleepThread interrupted is falseBusyThread interrupted is true
复制代码


从结果可以看出,抛出 InterruptedException 的线程 SleepThread,其中断标识位被清除了,而一直忙碌运作的线程 BusyThread,中断标识位没有被清除。

安全终止线程

suspend()、resume()和 stop()方法完成了线程的暂停、恢复和终止工作,而且非常“人性化”。但是这些 API 是过期的,也就是不建议使用的。


以 suspend()方法为例,在调用后,线程不会释放已经占有的资源(比如锁),而是占有着资源进入睡眠状态,这样容易引发死锁问题。同样,stop()方法在终结


一个线程时不会保证线程的资源正常释放,通常是没有给予线程完成资源释放工作的机会,因此会导致程序可能工作在不确定状态下。


public class Shutdown {    public static void main(String[] args) throws Exception {        Runner one = new Runner();        Thread countThread = new Thread(one, "CountThread");        countThread.start();    // 睡眠1秒,main线程对CountThread进行中断,使CountThread能够感知中断而结束        TimeUnit.SECONDS.sleep(1);        countThread.interrupt();        Runner two = new Runner();        countThread = new Thread(two, "CountThread");        countThread.start();    // 睡眠1秒,main线程对Runner two进行取消,使CountThread能够感知on为false而结束        TimeUnit.SECONDS.sleep(1);        two.cancel();    }    private static class Runner implements Runnable {        private long i;        private volatile boolean on = true;        @Override        public void run() {            while (on && !Thread.currentThread().isInterrupted()){                i++;            }            System.out.println("Count i = " + i);        }        public void cancel() {            on = false;        }    }}
复制代码


main 线程通过中断操作和 cancel()方法均可使 CountThread 得以终止。这种通过标识位或者中断操作的方式能够使线程在终止时有机会去清理资源,而不是武断地将线程停止,因此这种终止线程的做法显得更加安全和优雅

发布于: 刚刚阅读数: 3
用户头像

还未添加个人签名 2020.02.29 加入

还未添加个人简介

评论

发布
暂无评论
线程简介_5月月更_周杰伦本人_InfoQ写作社区