[java] Thread类详解

版权声明:未经博主同意,谢绝转载!(请尊重原创,感谢) https://blog.csdn.net/topdeveloperr/article/details/82180463

 

目录

Thread类概述

成员变量

构造方法

start方法

run方法

Thread直接调用run()和start()方法的区别

sleep方法

yield方法

interrupt方法

join方法

getState方法

总结


本篇博客基于jdk1.8

Thread类概述

Thread的一个实例,即是一个程序中执行的一个线程,java虚拟机允许一个应用程序同时有多个并发的线程运行。

每一个线程都有一个优先级。优先级高的线程比优先级低的线程优先执行。每一个线程也都可能被标记为一个守护线程。当代码执行到某一个线程去创建另一个线程时,新创建的线程的优先级会初始化成和创建它的线程一样。并且当且仅当创建线程为守护线程时,被创建线程才会被设为守护线程。

当Java虚拟机启动时,通常会有一个单独的非守护进程线程(通常这个非守护线程会去调用一个指定的类的main方法)。Java虚拟机器继续执行线程,直到以下任一一种情况发生:

Runtime类的exit方法被调用并且security manager(安全管理器)允许退出操作操作发生。

所有的非守护线程全部都已经终结,要么通过从调用返回到运行方法或由抛出超出运行的异常方法。

创建一个新的执行线程有两种方法:

声明一个Thread类的子类。这个子类应当去重写Thread类里的run方法。然后子类的实例就可以分配和启动。例如,计算大于指定值的素数的线程可以写成如下:

class PrimeThread extends Thread {
          long minPrime;
          PrimeThread(long minPrime) {
              this.minPrime = minPrime;
          }
 
          public void run() {
              // compute primes larger than minPrime
              ...
          }
}

下面的代码就可以创建一个线程并启动它:

PrimeThread p = new PrimeThread(143);
p.start();

创建线程的另一种方法是声明一个类实现Runable接口,这个类需要实现run方法,然后这个类的实例就可以在创建线程时被当做参数传递进去,并且启动,同一个例子的另一种样式如下:

class PrimeRun implements Runnable {
          long minPrime;
          PrimeRun(long minPrime) {
              this.minPrime = minPrime;
          }
 
          public void run() {
              // compute primes larger than minPrime
              ...
          }
}

然后,下面的代码就可以创建一个线程并启动它:

PrimeRun p = new PrimeRun(143);
new Thread(p).start();

每个线程都有用于标识目的的名称。多个线程可能具有相同的名称。如果创建一个线程时未指定名称,则会为其生成一个新名称。

除非另有说明,否则,向Thread类中的构造函数或方法传递null参数将导致抛出NullPointerException。

另外一点特别有意思的是,Thread类本身就是实现了Runnable接口的一个类。

成员变量

    private volatile String name;   //线程的名字
    private int priority; //线程优先级
    /* 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; //是否是守护线程

    /* What will be run. */
    private Runnable target; //将会被执行的Runnable.

    /* 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;  //默认线程的自动编号
   
    /* ThreadLocal values pertaining to this thread. This map is maintained
     * by the ThreadLocal class. */
    ThreadLocal.ThreadLocalMap threadLocals = null; //当前线程附属的ThreadLocal,而ThreadLocalMap会被ThreadLocal维护)

    /*
     * InheritableThreadLocal values pertaining to this thread. This map is
     * maintained by the InheritableThreadLocal class.
     */
    ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
  // 主要作用:为子线程提供从父线程那里继承的值
  //在创建子线程时,子线程会接收所有可继承的线程局部变量的初始值,以获得父线程所具有的值
  // 创建一个线程时如果保存了所有 InheritableThreadLocal 对象的值,那么这些值也将自动传递给子线程
  //如果一个子线程调用 InheritableThreadLocal 的 get() ,那么它将与它的父线程看到同一个对象

    /*
     * 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; //该线程请求的堆栈大小 默认一般都是忽略

    /*
     * Thread ID
     */
    private long tid;  // 每个线程都有专属ID,但名字可能重复

    /* For generating thread ID */
    private static long threadSeqNumber;   //用来生成thread ID

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

    private volatile int threadStatus = 0;  //标识线程状态,默认是线程未启动

    /* 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;  //阻塞器锁,主要用于处理阻塞情况 

    /**
     * The minimum priority that a thread can have.
     */
    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; //最高的优先级

构造方法

据完全统计,Thread类里一共提供了九个构造方法...其中有一个是非public的方法。

但是这九个构造方法事实上最后都是调用了一个叫做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) {
        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();
    }

该方法一共六个参数:

1.ThreadGroup 线程组

2.Runnable  target 一个Runnable对象,即调用run方法的对象

3.String name 新线程的名字

4.long stackSize 新线程分配所需堆栈的大小

5.AccessControlContext acc 权限控制

6.boolean inheritThreadLocals 是否继承ThreadLocals, 这个参数总是true。

方法的实现大致总结起来就是,给变量赋初值,大部分的初值都是通过该线程的父线程赋值,获取父线程对象的代码为:

Thread parent = currentThread();

而currentThread方法的实现是一个本地方法。

start方法

start方法的作用就是让该线程开始执行,java虚拟机会调用这个线程的run方法。

执行start方法的结果是两个线程会并发的运行,当前线程即执行start方法的线程,以及另一个调用run方法的线程。

启动一个线程两次永远是非法的,会抛出IllegalThreadStateException。代码如下:

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

可以看到上来会验证线程的状态,如果线程已启动则抛异常。核心实现代码为方法start0,是一个本地方法,在这个本地方法的实现当中,虚拟机会去调用该线程的run方法,在java代码中并看不出这一点。

这就是我们可以通过start方法启动线程,并且run方法会执行的原因所在。

run方法

Thread类实现了Runable接口,自然也就实现了run方法,代码如下

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

target定义如下:

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

target就是Runnable的一个实例,是Thread类中执行run的对象。注意:这里的代码同时说明了我们new一个Thread的时候问什么要传入一个Runable的对象,因为事实上后来这个Thread启动调用run方法的时候,就是调用的这个Runable的run方法。

问题:Thread直接调用run()和start()方法的区别?

 run()方法: 在本线程内调用该Runnable对象的run()方法,可以重复多次调用;

 start()方法:启动一个线程,然后Java虚拟机会调用该Runnable对象,即new一个Thread的时候传入的Runable对象,的run()方法,不能多次启动一个线程。

你调用run()方法的时候,只会是在原来的线程中调用,没有新的线程启动,start()方法才会启动新线程。

sleep方法

有两个重载的sleep方法。其中一个代码如下:

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

在调用另一个sleep方法之前,就是做了一些range check,即范围检查,然后就直接调用了另一个native的sleep方法。

yield方法

代码如下

 public static native void yield();

作用是使当前线程从执行状态(运行状态)变为可执行态(就绪状态)。也是一个native方法。

interrupt方法

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

实现也是调用了一个interrupt0的本地方法,有一句关键的注释:只是去set了interrupt flag,即不会真正的去停止线程,关于这一部分的内容,可以参考我的另一篇博客:停止线程

join方法

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

join的作用就是使得当前线程阻塞,一直到调用join方法的线程对象执行完run方法中的任务且销毁之后才继续执行后面的代码。

从上面的代码实现来看,join方法是通过和wait方法配合实现的。

getState方法

该方法是返回线程的状态,它的实现很有意思,如下:

 public State getState() {
        // get current thread state
        return sun.misc.VM.toThreadState(threadStatus);
    }
public static State toThreadState(int var0) {
        if ((var0 & 4) != 0) {
            return State.RUNNABLE;
        } else if ((var0 & 1024) != 0) {
            return State.BLOCKED;
        } else if ((var0 & 16) != 0) {
            return State.WAITING;
        } else if ((var0 & 32) != 0) {
            return State.TIMED_WAITING;
        } else if ((var0 & 2) != 0) {
            return State.TERMINATED;
        } else {
            return (var0 & 1) == 0 ? State.NEW : State.RUNNABLE;
        }
    }

toThreadState方法的实现就是根据线程状态的数字,返回对应的状态而已,它们只是有一个mapping的关系。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;
    }

可以看到java的线程一共是有六种状态的。

holdsLock方法判断线程是否持有锁对象

   /**
     * Returns <tt>true</tt> if and only if the current thread holds the
     * monitor lock on the specified object.
     *
     * <p>This method is designed to allow a program to assert that
     * the current thread already holds a specified lock:
     * <pre>
     *     assert Thread.holdsLock(obj);
     * </pre>
     *
     * @param  obj the object on which to test lock ownership
     * @throws NullPointerException if obj is <tt>null</tt>
     * @return <tt>true</tt> if the current thread holds the monitor lock on
     *         the specified object.
     * @since 1.4
     */
    public static native boolean holdsLock(Object obj);

为什么wait和notify方法不在Thread类里定义

需要说明为什么把这些方法放在Object类里是有意义的,还有不把它放在Thread类里的原因。一个很明显的原因是JAVA提供的锁是对象级的而不是线程级的,每个对象都有锁,通过线程获得。如果线程需要等待某些锁那么调用对象中的wait()方法就有意义了。如果wait()方法定义在Thread类中,线程正在等待的是哪个锁就不明显了。简单的说,由于wait,notify和notifyAll都是锁级别的操作,所以把他们定义在Object类中因为锁属于对象

总结

经过上面的分析,可以发现事实上Thread类因为设计到线程的操作,因此大量的还是依靠了native方法实现。java核心逻辑并不多。

猜你喜欢

转载自blog.csdn.net/topdeveloperr/article/details/82180463