IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> Thread类全局分析(创建,线程状态,中断) -> 正文阅读

[Java知识库]Thread类全局分析(创建,线程状态,中断)

Thread源码解析

创建和启动线程

创建线程的方法很多,但是启动的方法只有一个就是strat。

Runnale接口

我们看Thread类的定义知道,它实现了Runable接口

   public class Thread implements Runnable {
        ...
    }
    ```

Runnable接口定义如下

    @FunctionalInterface
    public interface Runnable {
        /**
         * When an object implementing interface <code>Runnable</code> is used
         * to create a thread, starting the thread causes the object's
         * <code>run</code> method to be called in that separately executing
         * thread.
         * <p>
         * The general contract of the method <code>run</code> is that it may
         * take any action whatsoever.
         *
         * @see     java.lang.Thread#run()
         */
        public abstract void run();
    }
    

我们可以通过Lambda表达式来创建Runnable接口的实例

线程创建

在java中,创建一个线程,有且仅有一种方式:

创建一个Thread类实例,并调用它的start方法。

这四个参数分别是:

  • ThreadGroup g(线程组)
  • Runnable target (Runnable 对象)
  • String name (线程的名字)
  • long stackSize (为线程分配的栈的大小,若为0则表示忽略这个参数)
init
/**
 * Initializes a Thread with the current AccessControlContext.
 * @see #init(ThreadGroup,Runnable,String,long,AccessControlContext,boolean)
 */
private void init(ThreadGroup g, Runnable target, String name, long stackSize) {
    init(g, target, name, stackSize, null, true);
}

//上面那个init方法最终调用了下面这个方法:

/**
 * Initializes a Thread.
 *
 * @param g the Thread group
 * @param target the object whose run() method gets called
 * @param name the name of the new Thread
 * @param stackSize the desired stack size for the new thread, or
 *        zero to indicate that this parameter is to be ignored.
 * @param acc the AccessControlContext to inherit, or
 *            AccessController.getContext() if null
 * @param inheritThreadLocals if {@code true}, inherit initial values for
 *            inheritable thread-locals from the constructing thread
 */
private void init(ThreadGroup g, Runnable target, String name, long stackSize, AccessControlContext acc, boolean inheritThreadLocals) {
    ...
}

所以综上来看,我们最常用的也就两个参数:

  • Runnable target (Runnable 对象)
  • String name (线程的名字)

而对于线程的名字,其默认值为"Thread-" + nextThreadNum(), nextThreadNum方法又是什么呢:

/* For autonumbering anonymous threads. */
    private static int threadInitNumber;
    private static synchronized int nextThreadNum() {
        return threadInitNumber++;
    }

它就是一个简单的递增计数器,所以如果创建线程时没有指定线程名,那线程名就会是: Thread-0, Thread-1, Thread-2, Thread-3, … 这玩意还是上锁的。。

至此,我们看到,虽然Thread类的构造函数有这么多,但对我们来说真正重要的参数只有一个:

Runnable target (Runnable 对象)

如果我们在线程构造时没有传入target(例如调用了无参构造函数),那么这个run方法就什么也不干。

启动线程

线程对象创建完了之后,接下来就是启动一个线程,在java中,启动一个线程必须调用线程的start方法

public synchronized void start() {
        /**
         * This method is not invoked for the main method thread or "system"
         * group threads created/set up by the VM. Any new functionality added
         * to this method in the future may have to also be added to the VM.
         *
         * A zero status value corresponds to state "NEW".
         */
        if (threadStatus != 0)
            throw new IllegalThreadStateException();

        /* Notify the group that this thread is about to be started
         * so that it can be added to the group's list of threads
         * and the group's unstarted count can be decremented. */
        group.add(this);

        boolean started = false;
        try {
            start0();
            started = true;
        } finally {
            try {
                if (!started) {
                    group.threadStartFailed(this);
                }
            } catch (Throwable ignore) {
                /* do nothing. If start0 threw a Throwable then
                  it will be passed up the call stack */
            }
        }
    }

  // 调了一个native方法
    private native void start0();

实际上调用strat的时候是创建了一个线程,这个线程帮我们玩run方法里面的东西,我们strat()的调用者,不用废话,是主线程。这个毋庸置疑。其实start是帮我们开了一个新的线程去跑run方法,仅此而已。

线程状态及常用方法

线程状态

在Thread类中, 线程状态是通过threadStatus属性以及State枚举类实现的:

在Thread内部有个东西叫做State类

 public enum State {
        /**
         * Thread state for a thread which has not yet started.
         */
        NEW,

        /**
         * Thread state for a runnable thread.  A thread in the runnable
         * state is executing in the Java virtual machine but it may
         * be waiting for other resources from the operating system
         * such as processor.
         */
        RUNNABLE,

        /**
         * Thread state for a thread blocked waiting for a monitor lock.
         * A thread in the blocked state is waiting for a monitor lock
         * to enter a synchronized block/method or
         * reenter a synchronized block/method after calling
         * {@link Object#wait() Object.wait}.
         */
        BLOCKED,

        /**
         * Thread state for a waiting thread.
         * A thread is in the waiting state due to calling one of the
         * following methods:
         * <ul>
         *   <li>{@link Object#wait() Object.wait} with no timeout</li>
         *   <li>{@link #join() Thread.join} with no timeout</li>
         *   <li>{@link LockSupport#park() LockSupport.park}</li>
         * </ul>
         *
         * <p>A thread in the waiting state is waiting for another thread to
         * perform a particular action.
         *
         * For example, a thread that has called <tt>Object.wait()</tt>
         * on an object is waiting for another thread to call
         * <tt>Object.notify()</tt> or <tt>Object.notifyAll()</tt> on
         * that object. A thread that has called <tt>Thread.join()</tt>
         * is waiting for a specified thread to terminate.
         */
        WAITING,

        /**
         * Thread state for a waiting thread with a specified waiting time.
         * A thread is in the timed waiting state due to calling one of
         * the following methods with a specified positive waiting time:
         * <ul>
         *   <li>{@link #sleep Thread.sleep}</li>
         *   <li>{@link Object#wait(long) Object.wait} with timeout</li>
         *   <li>{@link #join(long) Thread.join} with timeout</li>
         *   <li>{@link LockSupport#parkNanos LockSupport.parkNanos}</li>
         *   <li>{@link LockSupport#parkUntil LockSupport.parkUntil}</li>
         * </ul>
         */
        TIMED_WAITING,

        /**
         * Thread state for a terminated thread.
         * The thread has completed execution.
         */
        TERMINATED;
    }

public State getState() {
    // get current thread state
    return sun.misc.VM.toThreadState(threadStatus);
}

很清晰的可以看出来,有6个状态。
他们之间的关系,我找了个官方的图。
在这里插入图片描述
RUNNABLE状态包含了我们通常所说的running和ready两种状态。 ’

常用方法
currentThread

源码中currentThread定义如下:

/**
 * Returns a reference to the currently executing thread object.
 *
 * @return  the currently executing thread.
 */
public static native Thread currentThread();

这个东西事实上很简单,谁在运行,返回的就是谁,例如:A线程在执行。返回的就是A,B就是B,Main就是Main。

线程是CPU最小的调度单位,只要代码再跑,那么就是有线程在执行,返回的就是当前执行的线程。

sleep
public static native void sleep(long millis) throws InterruptedException

这个很简单,就是线程让出了当前的时间片,可能给其他线程去执行了,但是, 当前线程仍然持有它所获得的监视器锁

 public static void sleep(long millis, int nanos)
    throws InterruptedException {
        if (millis < 0) {
            throw new IllegalArgumentException("timeout value is negative");
        }

        if (nanos < 0 || nanos > 999999) {
            throw new IllegalArgumentException(
                                "nanosecond timeout value out of range");
        }

        if (nanos >= 500000 || (nanos != 0 && millis == 0)) {
            millis++;
        }

        sleep(millis);
    }

这个比较鸡肋,没什么用。因为native方法只有一个参数。

wait
public final void wait(long timeout, int nanos) throws InterruptedException {
    if (timeout < 0) {
        throw new IllegalArgumentException("timeout value is negative");
    }

    if (nanos < 0 || nanos > 999999) {
        throw new IllegalArgumentException("nanosecond timeout value out of range");
    }

    if (nanos > 0) {
        timeout++;
    }

    wait(timeout);
}

wait有无参的wait()方法,它调用的是wait(0),表示无限期等待,而sleep并没有无参数的方法,因为sleep(0)代表的是不睡,那么可能就是简简单单的给其他线程一个空子钻,这个没什么必要。注意,
wait会释放掉监视器锁!!!
wait会释放掉监视器锁!!!
wait会释放掉监视器锁!!!
重要的话说三次,这也是它与sleep的区别。

yield
/**
 * A hint to the scheduler that the current thread is willing to yield
 * its current use of a processor. The scheduler is free to ignore this
 * hint.
 *
 * <p> Yield is a heuristic attempt to improve relative progression
 * between threads that would otherwise over-utilise a CPU. Its use
 * should be combined with detailed profiling and benchmarking to
 * ensure that it actually has the desired effect.
 *
 * <p> It is rarely appropriate to use this method. It may be useful
 * for debugging or testing purposes, where it may help to reproduce
 * bugs due to race conditions. It may also be useful when designing
 * concurrency control constructs such as the ones in the
 * {@link java.util.concurrent.locks} package.
 */
public static native void yield();

yield只是让步,至于到底会不会让,就不知道了,如果你使用sleep或者wait。那么线程状态可能会变成 TIMED_WAITING,使用yield,最多会使线程从running 变成 ready。

join
/**
 * Waits at most {@code millis} milliseconds for this thread to
 * die. A timeout of {@code 0} means to wait forever.
 *
 * <p> This implementation uses a loop of {@code this.wait} calls
 * conditioned on {@code this.isAlive}. As a thread terminates the
 * {@code this.notifyAll} method is invoked. It is recommended that
 * applications not use {@code wait}, {@code notify}, or
 * {@code notifyAll} on {@code Thread} instances.
 *
 * @param  millis
 *         the time to wait in milliseconds
 *
 * @throws  IllegalArgumentException
 *          if the value of {@code millis} is negative
 *
 * @throws  InterruptedException
 *          if any thread has interrupted the current thread. The
 *          <i>interrupted status</i> of the current thread is
 *          cleared when this exception is thrown.
 */
public final synchronized void join(long millis) throws InterruptedException {
    long base = System.currentTimeMillis();
    long now = 0;

    if (millis < 0) {
        throw new IllegalArgumentException("timeout value is negative");
    }

    if (millis == 0) {
        while (isAlive()) {
            wait(0);
        }
    } else {
        while (isAlive()) {
            long delay = millis - now;
            if (delay <= 0) {
                break;
            }
            wait(delay);
            now = System.currentTimeMillis() - base;
        }
    }
}

意思是等待当前线程终止。也可以指定时间,不知道默认上送0,一直等,等到地老天荒也要等。只要线程还活着,就一直等,等的是哪个线程,被等的又是哪个。
等人的是当前线程,被等的是调用者。

现在有两个线程,小A和MAIN, a.join(),那么等人的就是MAIN,等A终止之后再执行。

线程中断interrupt

isInterrupted 和 interrupted

java中对于中断的大部分操作无外乎以下两点:

  1. 设置或者清除中断标志位
  2. 抛出InterruptedException

在Thread线程类里面提供了获取中断标志位的接口:

/**
 * Tests if some Thread has been interrupted.  The interrupted state
 * is reset or not based on the value of ClearInterrupted that is
 * passed.
 */
private native boolean isInterrupted(boolean ClearInterrupted);

这是一个native方法,同时也是一个private方法,该方法除了能够返回当前线程的中断状态,还能根据ClearInterrupted参数来决定要不要重置中断标志位

Thread类提供了两个public方法来使用该native方法:

public boolean isInterrupted() {
    return isInterrupted(false);
}

public static boolean interrupted() {
    return currentThread().isInterrupted(true);
}

啥意思呢?意思就是当你调用isInterrupted方法的时候,我只能给你返回 调用者是状态是不是中断。其他什么也不做。

而interrupted是一个静态方法,所以它可以由Thread类直接调用,自然就是作用于当前正在执行的线程,所以函数内部使用了currentThread()方法,与isInterrupted()方法不同的是,它的ClearInterrupted参数为true,在返回线程中断状态的同时,重置了中断标识位。

interrupt

这个方法并不能直接打断执行线程,只是说把当前线程的中断标志设为true,抛出InterruptedException异常(当线程处于阻塞情况,你去打断他),当然,如果这个线程已经终止,那就没用了,因为人家都执行完毕了,你还有打断他的必要吗?
上源码。

/**
 * Interrupts this thread.
 *
 * <p> Unless the current thread is interrupting itself, which is
 * always permitted, the {@link #checkAccess() checkAccess} method
 * of this thread is invoked, which may cause a {@link
 * SecurityException} to be thrown.
 *
 * <p> If this thread is blocked in an invocation of the {@link
 * Object#wait() wait()}, {@link Object#wait(long) wait(long)}, or {@link
 * Object#wait(long, int) wait(long, int)} methods of the {@link Object}
 * class, or of the {@link #join()}, {@link #join(long)}, {@link
 * #join(long, int)}, {@link #sleep(long)}, or {@link #sleep(long, int)},
 * methods of this class, then its interrupt status will be cleared and it
 * will receive an {@link InterruptedException}.
 *
 * <p> If this thread is blocked in an I/O operation upon an {@link
 * java.nio.channels.InterruptibleChannel InterruptibleChannel}
 * then the channel will be closed, the thread's interrupt
 * status will be set, and the thread will receive a {@link
 * java.nio.channels.ClosedByInterruptException}.
 *
 * <p> If this thread is blocked in a {@link java.nio.channels.Selector}
 * then the thread's interrupt status will be set and it will return
 * immediately from the selection operation, possibly with a non-zero
 * value, just as if the selector's {@link
 * java.nio.channels.Selector#wakeup wakeup} method were invoked.
 *
 * <p> If none of the previous conditions hold then this thread's interrupt
 * status will be set. </p>
 *
 * <p> Interrupting a thread that is not alive need not have any effect.
 *
 * @throws  SecurityException
 *          if the current thread cannot modify this thread
 *
 * @revised 6.0
 * @spec JSR-51
 */
public void interrupt() {
    if (this != Thread.currentThread())
        checkAccess();

    synchronized (blockerLock) {
        Interruptible b = blocker;
        if (b != null) {
            interrupt0();           // Just to set the interrupt flag
            b.interrupt(this);
            return;
        }
    }
    interrupt0();
}
打断一个线程

我们现在要打断一个线程,去调用interrupt方法不能直接打断它,有人告诉我,stop()方法,但是一个线程运行的好好的,直接二话不说打断,不预留任何空间,这好吗?这不好?是不是有点不讲武德了?至少,你要让我知道吧,让我有点准备吧?偷袭是不好的,你没有防御措施,更加不好。所以这个方法被废弃了,不太安全。

那我们要怎么有武德的去打断线程呢?

当然了,线程要以和为贵,要讲武德。当A线程调用了B.interrupt的时候,如果此时B不在阻塞,在正常上班,那么B的中断状态被设成了true,我们可以这样,让B中断。

while (Thread.interrupted() && Thread.currentThread().getName().contains("main")){
				System.err.println("线程中断状态为true");
				System.err.println("那就别跑了呗");
				return;
			}

但是一定要做点什么,不然下一次,B就会被复位,因为我用的是interrupted()。
如果用isInterrupted ,那就不会被复位了,但是一定要做事,不然他会永远卡在这个while循环出不来,如果用interrupted,如果不做事,下次interrupted就会被设为false,线程没有任何影响。

好了,那么我们应该如何打断一个阻塞的线程呢,等的时间太长,黄花菜都凉了,回家了,不等了。怎么办?

@Override
public void run() {
    while(true) {
        try {
            // do some task
            // blocked by calling wait/sleep/join
        } catch (InterruptedException ie) {  
            // 如果该线程被中断,则会抛出InterruptedException异常
            // 我们通过捕获这个异常,使得线程从block状态退出
            break; // 这里使用break, 可以使我们在线程中断后退出死循环,从而终止线程。
        }
    }

通过捕获异常即可。

结束语

结束

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-06-08 18:52:54  更:2022-06-08 18:56:28 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 19:12:55-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码