JAVA线程之Thread类详解-程序员宅基地

技术标签: java  

Thread类用于操作线程,是所以涉及到线程操作(如并发)的基础。本文将通过源码对Thread类的功能作用进行分析。

一、属性

    /* Make sure registerNatives is the first thing <clinit> does. */
    private static native void registerNatives();
    static {
        registerNatives();
    }

    private volatile String name;
    private int            priority;
    private Thread         threadQ;
    private long           eetop;

    /* Whether or not to single_step this thread. */
    private boolean     single_step;

    /* Whether or not the thread is a daemon thread. */
    private boolean     daemon = false;

    /* JVM state */
    private boolean     stillborn = false;

    /* What will be run. */
    private Runnable target;

    /* The group of this thread */
    private ThreadGroup group;

    /* The context ClassLoader for this thread */
    private ClassLoader contextClassLoader;

    /* The inherited AccessControlContext of this thread */
    private AccessControlContext inheritedAccessControlContext;

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

    /* ThreadLocal values pertaining to this thread. This map is maintained
     * by the ThreadLocal class. */
    ThreadLocal.ThreadLocalMap threadLocals = null;
    /*
     * InheritableThreadLocal values pertaining to this thread. This map is
     * maintained by the InheritableThreadLocal class.
     */

    ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;

    /*
     * The requested stack size for this thread, or 0 if the creator did
     * not specify a stack size.  It is up to the VM to do whatever it
     * likes with this number; some VMs will ignore it.
     */
    private long stackSize;

    /*
     * JVM-private state that persists after native thread termination.
     */
    private long nativeParkEventPointer;

    /*
     * Thread ID
     */
    private long tid;

    /* For generating thread ID */
    private static long threadSeqNumber;

    /* Java thread status for tools,
     * initialized to indicate thread 'not yet started'
     */

    private volatile int threadStatus = 0;
  volatile Object parkBlocker;

    /* The object in which this thread is blocked in an interruptible I/O
     * operation, if any.  The blocker's interrupt method should be invoked
     * after setting this thread's interrupt status.
     */
    private volatile Interruptible blocker;
    private final Object blockerLock = new Object();

    public final static int MIN_PRIORITY = 1;

    /**
     * The default priority that is assigned to a thread.
     */
    public final static int NORM_PRIORITY = 5;

    /**
     * The maximum priority that a thread can have.
     */
    public final static int MAX_PRIORITY = 10;
    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;
    }

此处重点说一下线程的优先级和状态。

1.线程优先级


    private int  priority;

    public final static int MIN_PRIORITY = 1;
    public final static int NORM_PRIORITY = 5;
    public final static int MAX_PRIORITY = 10;

   public final void setPriority(int newPriority) {
        ThreadGroup g;
        checkAccess();
        if (newPriority > MAX_PRIORITY || newPriority < MIN_PRIORITY) {
            throw new IllegalArgumentException();
        }
        if((g = getThreadGroup()) != null) {
            if (newPriority > g.getMaxPriority()) {
                newPriority = g.getMaxPriority();
            }
            setPriority0(priority = newPriority);
        }
    }

    public final int getPriority() {
        return priority;
    }

线程执行有优先级,优先级越高先执行机会越大(并不是一定先执行!!)。优先级用int的priority参数表示。
线程优先级最高为10,最低为1。默认为5
2.线程的状态

 public enum State {
        NEW,
        RUNNABLE,
        BLOCKED,
        WAITING,
        TIMED_WAITING,
        TERMINATED;
    }
      public State getState() {
        return sun.misc.VM.toThreadState(threadStatus);
    }

Thread对象共有6种状态:NEW(新建),RUNNABLE(运行),BLOCKED(阻塞),WAITING(等待),TIMED_WAITING(有时间的等待),TERMINATED(终止);状态转换如下:
这里写图片描述

也有一种说法,我认为也可以:
线程只有”就绪”、”阻塞”、”运行”三种状态(新建[NEW]”和”终止[TERMINATED]”状态的线程并不是线程,只是代表一个线程对象还存在):
1. RUNNABLE,对应”就绪”和”运行”两种状态,也就是说处于就绪和运行状态的线程在java.lang.Thread中都表现为”RUNNABLE”
2. BLOCKED,对应”阻塞”状态,此线程需要获得某个锁才能继续执行,而这个锁目前被其他线程持有,所以进入了被动的等待状态,直到抢到了那个锁,才会再次进入”就绪”状态
3. WAITING,对应”阻塞”状态,代表此线程正处于无限期的主动等待中,直到有人唤醒它,它才会再次进入就绪状态
4. TIMED_WAITING,对应”阻塞”状态,代表此线程正处于有限期的主动等待中,要么有人唤醒它,要么等待够了一定时间之后,才会再次进入就绪状态

二、构造器

    public Thread() {
        init(null, null, "Thread-" + nextThreadNum(), 0);
    }

    public Thread(Runnable target) {
        init(null, target, "Thread-" + nextThreadNum(), 0);
    }

    public Thread(ThreadGroup group, Runnable target) {
        init(group, target, "Thread-" + nextThreadNum(), 0);
    }

   public Thread(String name) {
        init(null, null, name, 0);
    }

    public Thread(ThreadGroup group, String name) {
        init(group, null, name, 0);
    }

    public Thread(Runnable target, String name) {
        init(null, target, name, 0);
    }

    public Thread(ThreadGroup group, Runnable target, String name) {
        init(group, target, name, 0);
    }


    public Thread(ThreadGroup group, Runnable target, String name,
                  long stackSize) {
        init(group, target, name, stackSize);
    }
     private void init(ThreadGroup g, Runnable target, String name,long stackSize) {
        init(g, target, name, stackSize, null, true);
    }
    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();
            }
        }

        g.checkAccess();

        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();
    }

通过以上代码可以看出,Thread()对外提供了8个构造器,但是都是接收不同参数,然后调用init(ThreadGroup g, Runnable target, String name, long stackSize)方法。故我们只需分析次init()方法即可。
init()方法共有4个参数,分别代表:

  1. ThreadGroup g 指定当前线程的线程组,未指定时线程组为创建该线程所属的线程组。线程组可以用来管理一组线程,通过activeCount() 来查看活动线程的数量。其他没有什么大的用处。 
  2. Runnable target 指定运行其中的Runnable,一般都需要指定,不指定的线程没有意义,或者可以通过创建Thread的子类并重新run方法。
  3. String name 线程的名称,不指定自动生成。
  4. long stackSize 预期堆栈大小,不指定默认为0,0代表忽略这个属性。与平台相关,不建议使用该属性。

三、public方法

Thread Thread.currentThread() :获得当前线程的引用。获得当前线程后对其进行操作。
Thread.UncaughtExceptionHandler getDefaultUncaughtExceptionHandler() :返回线程由于未捕获到异常而突然终止时调用的默认处理程序。
int Thread.activeCount():当前线程所在线程组中活动线程的数目。
void dumpStack() :将当前线程的堆栈跟踪打印至标准错误流。
int enumerate(Thread[] tarray) :将当前线程的线程组及其子组中的每一个活动线程复制到指定的数组中。
Map<Thread,StackTraceElement[]> getAllStackTraces() :返回所有活动线程的堆栈跟踪的一个映射。
boolean holdsLock(Object obj) :当且仅当当前线程在指定的对象上保持监视器锁时,才返回 true。
boolean interrupted() :测试当前线程是否已经中断。
void setDefaultUncaughtExceptionHandler(Thread.UncaughtExceptionHandler eh) :设置当线程由于未捕获到异常而突然终止,并且没有为该线程定义其他处理程序时所调用的默认处理程序。
void sleep(long millis) :休眠指定时间
void sleep(long millis, int nanos) :休眠指定时间
void yield() :暂停当前正在执行的线程对象,并执行其他线程。意义不太大

void checkAccess() :判定当前运行的线程是否有权修改该线程。
ClassLoader getContextClassLoader() :返回该线程的上下文 ClassLoader。
long getId() :返回该线程的标识符。
String getName() :返回该线程的名称。
int getPriority() :返回线程的优先级。
StackTraceElement[] getStackTrace() :返回一个表示该线程堆栈转储的堆栈跟踪元素数组。
Thread.State getState() :返回该线程的状态。
ThreadGroup getThreadGroup() :返回该线程所属的线程组。
Thread.UncaughtExceptionHandler getUncaughtExceptionHandler() :返回该线程由于未捕获到异常而突然终止时调用的处理程序。
void interrupt() :中断线程。
boolean isAlive() :测试线程是否处于活动状态。
boolean isDaemon() :测试该线程是否为守护线程。
boolean isInterrupted():测试线程是否已经中断。
void join() :等待该线程终止。
void join(long millis) :等待该线程终止的时间最长为 millis 毫秒。
void join(long millis, int nanos) :等待该线程终止的时间最长为 millis 毫秒 + nanos 纳秒。
void run() :线程启动后执行的方法。
void setContextClassLoader(ClassLoader cl) :设置该线程的上下文 ClassLoader。
void setDaemon(boolean on) :将该线程标记为守护线程或用户线程。
void start():使该线程开始执行;Java 虚拟机调用该线程的 run 方法。
String toString():返回该线程的字符串表示形式,包括线程名称、优先级和线程组。

在此重点是Thread currentThread(),void run() ,void start(),boolean interrupted() ,void interrupt(),boolean isInterrupted(),void sleep(long millis) ,void sleep(long millis, int nanos) ,void join() ,
void join(long millis) ,void join(long millis, int nanos) 这几个方法
1.Thread currentThread()
该方法是本地静态方法,用于获取当前线程,返回线程对象。

   public static native Thread currentThread();

2.void run()
是线程执行任务的主要代码。

   public void run() {
        if (target != null) {
            target.run();
        }
    }

当target不为空时,执行target的run方法。若target为空,则需要重写此方法,方法内是业务逻辑
3.void start()
使得该线程开始执行;Java 虚拟机调用该线程的 run 方法。

  public synchronized void start() {
        if (threadStatus != 0)
            throw new IllegalThreadStateException();

        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 */
            }
        }
    }

    private native void start0();

4.boolean interrupted() & void interrupt() & boolean isInterrupted()
这三个方法是针对线程中断标记的方法,

    public static boolean interrupted() {
        return currentThread().isInterrupted(true);
    }
     public boolean isInterrupted() {
        return isInterrupted(false);
    }
    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();
    }

    private native boolean isInterrupted(boolean ClearInterrupted);

interrupt():中断本线程(将中断状态标记为true)
isInterrupted():检测本线程是否已经中断 。如果已经中断,则返回true,否则false。中断状态不受该方法的影响。 如果中断调用时线程已经不处于活动状态,则返回false。
interrupted():检测当前线程是否已经中断 。如果当前线程存在中断,返回true,并且修改标记为false。再调用isIterruoted()会返回false。如果当前线程没有中断标记,返回false,不会修改中断标记。
测试代码:

public class TestThread {
    public static void main(String[] args) throws Exception{
        test();
    }

    public static void test() throws Exception{
        Thread t1 = new Thread(new MyThread());
        Thread t2 = new Thread(new MyThread());
        t1.start();
        t1.interrupt();
        System.out.println("1"+t1.isInterrupted());
        System.out.println("2"+t1.isInterrupted());
        System.out.println("3"+t1.interrupted());
        t2.start();

    }

}

class MyThread extends Thread {

    @Override
    public void run() {

        for (int i = 0; i < 10000; i++) {

        }
        System.out.println("4"+currentThread().getName()+interrupted());
        System.out.println("5"+currentThread().getName()+interrupted());

    }
}

执行结果:

1true
4Thread-1true
2false
5Thread-1false
3false
4Thread-3false
5Thread-3false

5.void sleep(long millis) & void sleep(long millis, int nanos)
这两个方法是让线程休眠

 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);
    }
   public static native void sleep(long millis) throws InterruptedException;

如上,两个参数的sleep只是将nanos转为millis(四舍五入转换),调用sleep(millis)方法。
sleep(millis)是本地方法,让当前线程休眠指定时间。
sleep不释放锁。
6.void join() & void join(long millis) & void join(long millis, int nanos)
这三个方法是让等待该线程终止。

    public final void join() throws InterruptedException {
        join(0);
    }
    public final synchronized void join(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++;
        }

        join(millis);
    }
   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时,代表一直等待到结束。

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/pengqiaowolf/article/details/80442071

智能推荐

Entering emergency mode. Exit the shell to continue._linux系统提示entering emergency mode. exit the shell t-程序员宅基地

文章浏览阅读545次。Linux虚拟机复制到另外一台AMD的机器上,启动后直接进入emergency模式。解决方式如下:解决问题:journalctl可以查看错误原因和解决办法。输入命令:#umount /dev/dm-0#xfs_repair -v -L /dev/dm-0【-L 选项指定强制日志清零,强制xfs_repair将日志归零,即使它包含脏数据(元数据更改)。】#reboot..._linux系统提示entering emergency mode. exit the shell to continue. type

AmazeUI基本样式-程序员宅基地

文章浏览阅读811次。2019独角兽企业重金招聘Python工程师标准>>> ..._amazeui宽度

win10环境下vc++6.0安装插件报unable to register........-程序员宅基地

文章浏览阅读790次。在安装显示行号插件时出现unable to register this add-in because its dllregisterserver returns an error 的错误,大体意思是该Add-in DLL注册服务返回一个错误而导致无法注册。在网上有很对修改办法,有的确实没有问题,但是有的居然需要另外下载一个插件,实在是接受不了。我的解决办法如下。(1)先关..._vc++6.0报错插件

python操作mysql数据库-程序员宅基地

文章浏览阅读3k次,点赞4次,收藏45次。Python DB-API操作数据库Python 标准数据库接口为 Python DB-API,Python DB-API为开发人员提供了数据库应用编程接口。Python 数据库接口支持非常多的数据库:mysql,oracle。不同的数据库你需要下载不同的DB API模块,例如你需要访问Oracle数据库和Mysql数据,你需要下载Oracle和MySQL数据库模块。Python的DB-API,为大多数的数据库实现了接口,使用它连接各数据库后,就可以用相同的方式操作各数据库。使用python DB_python操作mysql数据库

python金融量化——股票数据分割+计算单支股票利益率曲线(代码)_python 股票循环计算-程序员宅基地

文章浏览阅读719次。【代码】python金融量化——股票数据分割+计算单支股票利益率曲线(代码)_python 股票循环计算

Elasticsearch Java API 的使用(22)—实现桶聚合_es中聚合添加过滤条件 javaapi-程序员宅基地

文章浏览阅读2.3k次,点赞2次,收藏3次。分组聚合 使用terms时间分组集合public class EsTermsAgg throws UnknownHostException{ public void TermsAgg(TransportClient client){ AggregationBuilder agg = AggregationBuilders.terms(&quot;terms&quot;).field(&quot;ag..._es中聚合添加过滤条件 javaapi

随便推点

必须是可修改的左值_关于C++左值和右值你真的了解吗?-程序员宅基地

文章浏览阅读7.8k次,点赞5次,收藏18次。一、认识左值和右值关于左值右值有几条规则和特点,先列举在这里,后面可以跟随例子慢慢体会:1、左值和右值都是指的表达式,比如 int a = 1 中的 a 是左值,++a 是左值, func() 也可能是左值,而 a+1 是右值, 110 也是一个右值。2、左值可以放在 = 的左边,右值只能放在 = 的右边,这其中隐含的意思就是左值也能放在 = 的右边,但是右值不能放在 = 的左边。3、左..._a必须是可修改的左值

Oracle分区表详解(Oracle Partitioned Tables)-程序员宅基地

文章浏览阅读1.1w次,点赞31次,收藏139次。当单表数据量随着时间变的越来越大时,会给数据的管理和查询带来不便。我们可以考虑对表进行分区,利用分区表特性将数据分成小块存储,可以大幅提升查询性能,管理便捷性及数据的可用性。目录一、分区表概述1.1 分区表概念1.2 何时使用分区表1.3 分区表的优点1.3.1 提升SQL查询性能1.3.2 提升表可管理性1.3.3 提升数据可用性二、基础分区策略2.1 范围分区(Range Partition)2.2 哈希分区(Hash Partition)2.3 列表分区(List partition)三、扩展分区策略_oracle分区表

踩坑 视觉SLAM 十四讲第二版 ch5 /rgbd 编译及运行问题_slam14讲ch5 rgbd报错-程序员宅基地

文章浏览阅读321次,点赞3次,收藏5次。在ch5/rgbd路径下。_slam14讲ch5 rgbd报错

整合升级BTM和MoE,大模型专业领域能力高效训练法BTX诞生-程序员宅基地

文章浏览阅读6k次。整理丨王轶群出品丨AI 科技大本营(ID:rgznai100)整合多个专家大模型到一个混合专家大模型(Mixing Expert LLMs into a Mixture-of-Experts LLM),Meta在人工智能机器学习领域取得新的高效方法突破。最近,Meta基础人工智能研究(FAIR)团队发布了名为Branch-Train-MiX (BTX)的方法,可从种子模型开始,该模型经过分支,以高...

You could try using --skip-broken to work around the problem-程序员宅基地

文章浏览阅读1.2w次,点赞7次,收藏7次。解决方法:移除这个冲突包yum -y remove centos-release-ceph-luminous-1.1-2.el7.centos.noarch成功了:

bug: nerdtree 显示图标乱码-程序员宅基地

文章浏览阅读2.3k次。bug 复现。选择:nerd font解决原因是字体。droidsansmono nerd font book_nerdtree 显示图标