Java源码分析之Thread

Java源码分析之Thread

1、java虚拟机允许应用程序多线程并发执行,线程拥有优先级,
jvm只有两种线程:
- daemon 线程(后台线程):jvm自己用,如一个线程自动执行垃圾收集。A daemon thread is ordinarily a thread used by the virtual machine itself, such as a thread that performs garbage collection.
- non-daemon 线程(前台线程):负责和用户打交道,如一个应用首先运行的是main()线程,然后main线程启动我们的写的方法。
当虚拟机启动后通常情况下只有一个non-daemon线程(main)
2、java中创建线程的方法
Java中创建线程主要有三种方式
- 继承Thread类创建线程类
(1)定义Thread类的子类,并重写该类的run方法,该run方法的方法体就代表了线程要完成的任务。因此把run()方法称为执行体。
(2)创建Thread子类的实例,即创建了线程对象。
(3)调用线程对象的start()方法来启动该线程
- 通过Runnable接口创建线程类
(1)定义runnable接口的实现类,并重写该接口的run()方法,该run()方法的方法体同样是该线程的线程执行体。
(2)创建 Runnable实现类的实例,并依此实例作为Thread的target来创建Thread对象,该Thread对象才是真正的线程对象。
(3)调用线程对象的start()方法来启动该线程。
- 通过Callable和Future创建线程
(1)创建Callable接口的实现类,并实现call()方法,该call()方法将作为线程执行体,并且有返回值。
(2)创建Callable实现类的实例,使用FutureTask类来包装Callable对象,该FutureTask对象封装了该Callable对象的call()方法的返回值。
(3)使用FutureTask对象作为Thread对象的target创建并启动新线程。
(4)调用FutureTask对象的get()方法来获得子线程执行结束后的返回值
3、Thread也是实现的Runnable接口
4、Thread相关属性

    //注册java方法到本地方法
    private static native void registerNatives();
    static {
        registerNatives();
    }
    private volatile String name;
    private int            priority;
    private Thread         threadQ;
    private long           eetop;
    //是否为single_step线程
    private boolean     single_step;
    //是否为daemon线程
    private boolean     daemon = false;
    //JVM状态
    private boolean     stillborn = false;
    //要运行的目标
    private Runnable target;
    //线程所属的线程组
    private ThreadGroup group;
    //线程的Classloader
    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;
    private static synchronized long nextThreadID() {
        return ++threadSeqNumber;
    }
    /**
     * The argument supplied to the current call to
     * java.util.concurrent.locks.LockSupport.park.
     * Set by (private) java.util.concurrent.locks.LockSupport.setBlocker
     * Accessed using java.util.concurrent.locks.LockSupport.getBlocker
     */
    volatile Object parkBlocker;
<span class="hljs-comment">/* 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.
 */</span>
<span class="hljs-keyword">private</span> <span class="hljs-keyword">volatile</span> Interruptible blocker;
<span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> Object blockerLock = <span class="hljs-keyword">new</span> Object();
<span class="hljs-comment">/* Set the blocker field; invoked via sun.misc.SharedSecrets from java.nio code
 */</span>
<span class="hljs-keyword">void</span> blockedOn(Interruptible b) {
    <span class="hljs-keyword">synchronized</span> (blockerLock) {
        blocker = b;
    }
}
 <span class="hljs-comment">//线程优先级最小值</span>
<span class="hljs-keyword">public</span> <span class="hljs-keyword">final</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">int</span> MIN_PRIORITY = <span class="hljs-number">1</span>;

<span class="hljs-comment">//线程优先级默认值</span>
<span class="hljs-keyword">public</span> <span class="hljs-keyword">final</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">int</span> NORM_PRIORITY = <span class="hljs-number">5</span>;
<span class="hljs-comment">//线程优先级最大值</span>
<span class="hljs-keyword">public</span> <span class="hljs-keyword">final</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">int</span> MAX_PRIORITY = <span class="hljs-number">10</span>;<div class="hljs-button {2}" data-title="复制" data-report-click="{&quot;spm&quot;:&quot;1001.2101.3001.4259&quot;}"></div></code><ul class="pre-numbering" style=""><li style="color: rgb(153, 153, 153);">1</li><li style="color: rgb(153, 153, 153);">2</li><li style="color: rgb(153, 153, 153);">3</li><li style="color: rgb(153, 153, 153);">4</li><li style="color: rgb(153, 153, 153);">5</li><li style="color: rgb(153, 153, 153);">6</li><li style="color: rgb(153, 153, 153);">7</li><li style="color: rgb(153, 153, 153);">8</li><li style="color: rgb(153, 153, 153);">9</li><li style="color: rgb(153, 153, 153);">10</li><li style="color: rgb(153, 153, 153);">11</li><li style="color: rgb(153, 153, 153);">12</li><li style="color: rgb(153, 153, 153);">13</li><li style="color: rgb(153, 153, 153);">14</li><li style="color: rgb(153, 153, 153);">15</li><li style="color: rgb(153, 153, 153);">16</li><li style="color: rgb(153, 153, 153);">17</li><li style="color: rgb(153, 153, 153);">18</li><li style="color: rgb(153, 153, 153);">19</li><li style="color: rgb(153, 153, 153);">20</li><li style="color: rgb(153, 153, 153);">21</li><li style="color: rgb(153, 153, 153);">22</li><li style="color: rgb(153, 153, 153);">23</li><li style="color: rgb(153, 153, 153);">24</li><li style="color: rgb(153, 153, 153);">25</li><li style="color: rgb(153, 153, 153);">26</li><li style="color: rgb(153, 153, 153);">27</li><li style="color: rgb(153, 153, 153);">28</li><li style="color: rgb(153, 153, 153);">29</li><li style="color: rgb(153, 153, 153);">30</li><li style="color: rgb(153, 153, 153);">31</li><li style="color: rgb(153, 153, 153);">32</li><li style="color: rgb(153, 153, 153);">33</li><li style="color: rgb(153, 153, 153);">34</li><li style="color: rgb(153, 153, 153);">35</li><li style="color: rgb(153, 153, 153);">36</li><li style="color: rgb(153, 153, 153);">37</li><li style="color: rgb(153, 153, 153);">38</li><li style="color: rgb(153, 153, 153);">39</li><li style="color: rgb(153, 153, 153);">40</li><li style="color: rgb(153, 153, 153);">41</li><li style="color: rgb(153, 153, 153);">42</li><li style="color: rgb(153, 153, 153);">43</li><li style="color: rgb(153, 153, 153);">44</li><li style="color: rgb(153, 153, 153);">45</li><li style="color: rgb(153, 153, 153);">46</li><li style="color: rgb(153, 153, 153);">47</li><li style="color: rgb(153, 153, 153);">48</li><li style="color: rgb(153, 153, 153);">49</li><li style="color: rgb(153, 153, 153);">50</li><li style="color: rgb(153, 153, 153);">51</li><li style="color: rgb(153, 153, 153);">52</li><li style="color: rgb(153, 153, 153);">53</li><li style="color: rgb(153, 153, 153);">54</li><li style="color: rgb(153, 153, 153);">55</li><li style="color: rgb(153, 153, 153);">56</li><li style="color: rgb(153, 153, 153);">57</li><li style="color: rgb(153, 153, 153);">58</li><li style="color: rgb(153, 153, 153);">59</li><li style="color: rgb(153, 153, 153);">60</li><li style="color: rgb(153, 153, 153);">61</li><li style="color: rgb(153, 153, 153);">62</li><li style="color: rgb(153, 153, 153);">63</li><li style="color: rgb(153, 153, 153);">64</li><li style="color: rgb(153, 153, 153);">65</li><li style="color: rgb(153, 153, 153);">66</li><li style="color: rgb(153, 153, 153);">67</li><li style="color: rgb(153, 153, 153);">68</li><li style="color: rgb(153, 153, 153);">69</li><li style="color: rgb(153, 153, 153);">70</li><li style="color: rgb(153, 153, 153);">71</li><li style="color: rgb(153, 153, 153);">72</li><li style="color: rgb(153, 153, 153);">73</li><li style="color: rgb(153, 153, 153);">74</li><li style="color: rgb(153, 153, 153);">75</li><li style="color: rgb(153, 153, 153);">76</li><li style="color: rgb(153, 153, 153);">77</li><li style="color: rgb(153, 153, 153);">78</li><li style="color: rgb(153, 153, 153);">79</li><li style="color: rgb(153, 153, 153);">80</li><li style="color: rgb(153, 153, 153);">81</li><li style="color: rgb(153, 153, 153);">82</li><li style="color: rgb(153, 153, 153);">83</li><li style="color: rgb(153, 153, 153);">84</li><li style="color: rgb(153, 153, 153);">85</li></ul></pre> 

5、Thread相关方法

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

从源码中可以看到,nanos参数是没有用到的,也就是说,sleep只能精确到毫秒级。次方法最终调用的是一个本地方法sleep(millis),而sleep(millis)的方法如下:

public static native void sleep(long millis) throws InterruptedException;
  
  
  • 1
  • 5.2 void init(ThreadGroup g, Runnable target, String name,
    long stackSize, AccessControlContext acc,boolean inheritThreadLocals)
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();
    }

此方法是线程初始化最终使用的方法
- 5.3 synchronized void 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 */
            }
        }
    }

run方法是如何被调用的
写过线程的我们都知道,我们经常看到这样的代码

new SubThread().start()`;//subThread 是继承Thread的子类
    
    
  • 1

或者是

`new Thread(new SubRunnable()).start()`;//SubRunnable是实现Runnable的类。`
    
    
  • 1

从上面的代码可以看出,start()方法主要是调用了一个start0()这个方法。而start0() 是一个原生态方法,如下:

private native void start0();
    
    
  • 1

原生态的方法是由其它的语言实现,我们看不到里面的具体实现。下面我们来看Thread类中的run()方法。由于Thread类是实现Runnable接口。Runable接口中只有一个抽象的run方法,如下:

public abstract void run();
    
    
  • 1

因此,Thread就重写了此run方法,源码如下:
@Override
public void run() {
if (target != null) {
target.run();//target是一个Runnable的引用
}
}`
从源码中可以看出,如果target存在,则执行target的run方法,否则什么也不做。也就是说Thread的run()方法总是先被调用,然后调用target(构造函数中的Runnable对象)的run()方法。
- 5.4 synchronized void join(long millis)

public final synchronized void join(long millis)
    throws InterruptedException {
        long base = System.currentTimeMillis();
        long now = 0;
    <span class="hljs-keyword">if</span> (millis &lt; <span class="hljs-number">0</span>) {
        <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> IllegalArgumentException(<span class="hljs-string">"timeout value is negative"</span>);
    }

    <span class="hljs-keyword">if</span> (millis == <span class="hljs-number">0</span>) {
        <span class="hljs-keyword">while</span> (isAlive()) {
            wait(<span class="hljs-number">0</span>);
        }
    } <span class="hljs-keyword">else</span> {
        <span class="hljs-keyword">while</span> (isAlive()) {
            <span class="hljs-keyword">long</span> delay = millis - now;
            <span class="hljs-keyword">if</span> (delay &lt;= <span class="hljs-number">0</span>) {
                <span class="hljs-keyword">break</span>;
            }
            wait(delay);
            now = System.currentTimeMillis() - base;
        }
    }
}<div class="hljs-button {2}" data-title="复制" data-report-click="{&quot;spm&quot;:&quot;1001.2101.3001.4259&quot;}"></div></code><ul class="pre-numbering" style=""><li style="color: rgb(153, 153, 153);">1</li><li style="color: rgb(153, 153, 153);">2</li><li style="color: rgb(153, 153, 153);">3</li><li style="color: rgb(153, 153, 153);">4</li><li style="color: rgb(153, 153, 153);">5</li><li style="color: rgb(153, 153, 153);">6</li><li style="color: rgb(153, 153, 153);">7</li><li style="color: rgb(153, 153, 153);">8</li><li style="color: rgb(153, 153, 153);">9</li><li style="color: rgb(153, 153, 153);">10</li><li style="color: rgb(153, 153, 153);">11</li><li style="color: rgb(153, 153, 153);">12</li><li style="color: rgb(153, 153, 153);">13</li><li style="color: rgb(153, 153, 153);">14</li><li style="color: rgb(153, 153, 153);">15</li><li style="color: rgb(153, 153, 153);">16</li><li style="color: rgb(153, 153, 153);">17</li><li style="color: rgb(153, 153, 153);">18</li><li style="color: rgb(153, 153, 153);">19</li><li style="color: rgb(153, 153, 153);">20</li><li style="color: rgb(153, 153, 153);">21</li><li style="color: rgb(153, 153, 153);">22</li><li style="color: rgb(153, 153, 153);">23</li><li style="color: rgb(153, 153, 153);">24</li></ul></pre> 

join方法有几种重载形式,但是,最终都是调用是上面的函数join(long millis),因此,下面就对其进行介绍。
上面源码前面对这个函数的介绍如下:
Waits at most {@code millis} milliseconds for this thread to die. A timeout of {@code 0} means to wait forever.
字面意思就是:等待millis毫秒直到这个线程死亡。但是这个线程到底是指的是子线程还是主线程呢???
下面举几个例子就知道答案了。

public class TestThread extends Thread {
   
   
    public static void main(String[] args) {
        Thread t=new TestThread();
        t.start();
        try {
            t.join(1000);//main线程只等待1000ms
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("主线程结束");
    }
<span class="hljs-annotation">@Override</span>
<span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">run</span>() {
    <span class="hljs-keyword">super</span>.run();
    System.out.println(<span class="hljs-string">"子线程开始"</span>);
    <span class="hljs-keyword">try</span> {
        System.out.println(<span class="hljs-string">"子线程开始休眠"</span>);
        Thread.sleep(<span class="hljs-number">1900</span>);<span class="hljs-comment">//休眠1900毫秒</span>
        System.out.println(<span class="hljs-string">"子线程休眠结束"</span>);
    } <span class="hljs-keyword">catch</span> (InterruptedException e) {
        e.printStackTrace();
    }
    System.out.println(<span class="hljs-string">"子线程结束"</span>);
}
}<div class="hljs-button {2}" data-title="复制" data-report-click="{&quot;spm&quot;:&quot;1001.2101.3001.4259&quot;}"></div></code><ul class="pre-numbering" style=""><li style="color: rgb(153, 153, 153);">1</li><li style="color: rgb(153, 153, 153);">2</li><li style="color: rgb(153, 153, 153);">3</li><li style="color: rgb(153, 153, 153);">4</li><li style="color: rgb(153, 153, 153);">5</li><li style="color: rgb(153, 153, 153);">6</li><li style="color: rgb(153, 153, 153);">7</li><li style="color: rgb(153, 153, 153);">8</li><li style="color: rgb(153, 153, 153);">9</li><li style="color: rgb(153, 153, 153);">10</li><li style="color: rgb(153, 153, 153);">11</li><li style="color: rgb(153, 153, 153);">12</li><li style="color: rgb(153, 153, 153);">13</li><li style="color: rgb(153, 153, 153);">14</li><li style="color: rgb(153, 153, 153);">15</li><li style="color: rgb(153, 153, 153);">16</li><li style="color: rgb(153, 153, 153);">17</li><li style="color: rgb(153, 153, 153);">18</li><li style="color: rgb(153, 153, 153);">19</li><li style="color: rgb(153, 153, 153);">20</li><li style="color: rgb(153, 153, 153);">21</li><li style="color: rgb(153, 153, 153);">22</li><li style="color: rgb(153, 153, 153);">23</li><li style="color: rgb(153, 153, 153);">24</li><li style="color: rgb(153, 153, 153);">25</li><li style="color: rgb(153, 153, 153);">26</li></ul></pre> 

修改里面的Thread.sleep(1900)中的毫秒数,分别为:900,1900,两次运行结果如下:

*900毫秒 1900毫秒
子线程开始 子线程开始
子线程开始休眠 子线程开始休眠
子线程休眠结束 主线程结束
子线程结束 子线程休眠结束
主线程结束 子线程结束*
即当main线程中调用t.join(mills)时,main线程只等待mills毫秒,当达到时间时,无论子线程是否结束,均结束子线程。
现在修改主线程调用t.sleep()方法,两次测试结果如下:
*900毫秒 1900毫秒
子线程开始 子线程开始
子线程开始休眠 子线程开始休眠
子线程休眠结束 子线程休眠结束
子线程结束 子线程结束
主线程结束 主线程结束*
t.join()方法最终调用的是join(0)方法,join(0)方法在jdk中的描述如下:
A timeout of {@code 0} means to wait forever,永远等待。
回到这一节开始的源码处,从源码中我们看到join()方法实现是通过wait(Object 中的一个native方法)来实现的。当main线程调用t.join的时候,main线程会获得线程对象t的锁(wait意味着拿到了该对象的锁),调用该对象的wait(等待时间),直到该对象唤醒main线程,比如子线程退出等。
这就意味着main线程调用t.join()时,必须要拿到线程t对象的锁,如果拿不到的话它是无法wait的,刚开始的例子中t.join(1000)不是说明了main线程等待1s,如果在它等待之前,其它线程获得了t对象的锁,它等待的时间就可不就是1s了。
测试代码如下:

public class TestThread extends Thread {
   
   
<span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span>(String[] args) {
    Thread t=<span class="hljs-keyword">new</span> TestThread();
    Thread t1=<span class="hljs-keyword">new</span> SubThread(t);
    t1.start();<span class="hljs-comment">//这个子线程会先持有线程t的锁</span>
    t.start();
    <span class="hljs-keyword">try</span> {
        <span class="hljs-comment">//main线程只等待1000ms,无论子线程是否结束,均不在等待。,但是其等待的前提条件时要持有线程t的锁</span>
        t.join(<span class="hljs-number">1000</span>);
    } <span class="hljs-keyword">catch</span> (InterruptedException e) {
        e.printStackTrace();
    }
    System.out.println(<span class="hljs-string">"主线程结束"</span>);
}
<span class="hljs-annotation">@Override</span>
<span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">run</span>() {
    <span class="hljs-keyword">super</span>.run();
    System.out.println(<span class="hljs-string">"子线程开始"</span>);
    <span class="hljs-keyword">try</span> {
        System.out.println(<span class="hljs-string">"子线程开始休眠"</span>);
        Thread.sleep(<span class="hljs-number">800</span>);<span class="hljs-comment">//休眠800毫秒</span>
        System.out.println(<span class="hljs-string">"子线程休眠结束"</span>);
    } <span class="hljs-keyword">catch</span> (InterruptedException e) {
        e.printStackTrace();
    }
    System.out.println(<span class="hljs-string">"子线程结束"</span>);
}   
}
class SubThread extends Thread{
Thread thread;
<span class="hljs-keyword">public</span> <span class="hljs-title">SubThread</span>(Thread t){
    <span class="hljs-keyword">this</span>.thread=t;
}
<span class="hljs-annotation">@Override</span>
<span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">run</span>() {
    <span class="hljs-keyword">super</span>.run();
    holdThreadLock(thread);
}
<span class="hljs-keyword">private</span> <span class="hljs-keyword">void</span> <span class="hljs-title">holdThreadLock</span>(Thread thread2) {
    <span class="hljs-keyword">synchronized</span> (thread2) {
        System.out.println(<span class="hljs-string">"持有线程锁"</span>);
        <span class="hljs-keyword">try</span> {
            Thread.sleep(<span class="hljs-number">3000</span>);<span class="hljs-comment">//休眠3000毫秒</span>
        } <span class="hljs-keyword">catch</span> (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(<span class="hljs-string">"释放线程锁"</span>);
    }
}

在main方法中,通过Thread t1=new SubThread(t);t1.start();开启另一个子线程,这个子线程它在holdThreadLock()方法中,通过synchronized(thread2)来获取线程对象t的锁,并在sleep(3000)后释放,这就意味着,即使在main方法中t.join(1000),等待1000毫秒,但是由于首先是子线程t1获得了子线程t的锁,main无法获取子线程t的锁,因此,他实际的等待时间是3000+1000ms

运行结果如下:
持有线程锁
子线程开始
子线程开始休眠
子线程休眠结束
子线程结束
释放线程锁
主线程结束

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值