精华内容
下载资源
问答
  • Thread源码分析

    2020-04-16 17:20:20
    Thread源码分析 1. Thread 介绍 Thread 是程序中执行的线程。Java虚拟机允许应用程序同时执行多个执行线程。 每个线程都有优先权。 具有较高优先级的线程优先于优先级较低的线程执行。 每个线程可能也可能不会被...

    Thread源码分析

    1. Thread 介绍

    • Thread 是程序中执行的线程。Java虚拟机允许应用程序同时执行多个执行线程。

      每个线程都有优先权。 具有较高优先级的线程优先于优先级较低的线程执行。 每个线程可能也可能不会被标记为守护程序。 当在某个线程中运行的代码创建一个新的Thread对象时,新线程的优先级最初设置为等于创建线程的优先级,并且当且仅当创建线程是守护进程时才是守护线程。

      当Java虚拟机启动时,通常有一个非守护进程线程(通常调用某些指定类的名为main的方法)。 Java虚拟机将继续执行线程,直到发生以下任一情况:

      • 已经调用了Runtime类的exit方法,并且安全管理器已经允许进行退出操作。
      • 所有不是守护进程线程的线程都已经死亡,无论是从调用返回到run方法还是抛出超出run方法的run

      创建一个新的执行线程有两种方法。 一个是将一个类声明为Thread的子类。 这个子类应该重写run类的方法Thread 。 然后可以分配并启动子类的实例。

    Thread类继承了Runnable接口

    ForkJoinWorkerThread是其子类

    守护线程

    一般来说,Java 中的线程可以分为两种:守护线程和普通线程。在 JVM 刚启动时,它创建的所有线程,除了主线程(main thread)外,其他的线程都是守护线程(比如:垃圾收集器、以及其他执行辅助操作的线程)。

    当创建一个新线程时,新线程将会继承它线程的守护状态,默认情况下,主线程创建的所有线程都是普通线程。

    什么情况下会需要守护线程呢?一般情况下是,当我们希望创建一个线程来执行一些辅助的工作,但是又不希望这个线程阻碍 JVM 的关闭,在这种情况下,我们就需要使用守护线程了。

    守护线程与普通线程唯一的区别是:当线程退出时,JVM 会检查其他正在运行的线程,如果这些线程都是守护线程,那么 JVM 会正常退出操作,但是如果有普通线程还在运行,JVM 是不会执行退出操作的。当 JVM 退出时,所有仍然存在的守护线程都将被抛弃,既不会执行 finally 部分的代码,也不会执行 stack unwound 操作,JVM 会直接退出。

    参考:https://matt33.com/2018/07/07/java-daemon-thread/

    2. registerNatives

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

    registerNatives目的是为了绑定本地方法,方式是通过JNI_onload函数实现动态绑定。

    3. 属性

    	//线程名字
    	private volatile String name;
    	//线程的优先级,数值越大,优先级越高,但是不一定有效
        private int            priority;
    	//
        private Thread         threadQ;
    
    	//指向线程本地存储的地址,Hotspot VM 在用
        private long           eetop;
    
        /* Whether or not to single_step this thread. */
        private boolean     single_step;
    
        //是否为守护线程,默认是非守护线程,也就是用户线程,JVM需要等待用户线程结束后才终止。
        private boolean     daemon = false;
    
        //部分虚拟机实现了,表示线程在start之前是否stop
        private boolean     stillborn = false;
    
        //线程执行体,
        private Runnable target;
    
        //线程组
        private ThreadGroup group;
    
        //线程上下文类加载器
        private ClassLoader contextClassLoader;
    
        //继承的访问权限上下文,用于系统资源的访问,可在构造时传入
        private AccessControlContext inheritedAccessControlContext;
    
        //自增的线程号,用于为匿名线程生成线程名
        private static int threadInitNumber;
        
        //当前线程的局部变量
        ThreadLocal.ThreadLocalMap threadLocals = null;
    
        //继承父线程的局部变量
        ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
    
        //栈空间大小
        private long stackSize;
    
        /*
         * JVM-private state that persists after native thread termination.
         */
        private long nativeParkEventPointer;
    
    	//线程ID,之前讲过线程管理是在用户空间控制的
        private long tid;
    
        /* For generating thread ID */
        private static long threadSeqNumber;
    
    	//Java线程状态
        private volatile int threadStatus = 0;
    
    	//阻塞线程时设置的同步对象,各线程共享此对象阻塞状态
    	volatile Object parkBlocker;
    
    	//在可中断IO上阻塞时设置该字段,当线程设置中断状态时,该对象的interrupt()方法被调用
    	private volatile Interruptible blocker;
    
    	//用于设置blocker的同步监视器对象
        private final Object blockerLock = new Object();
    

    3. 构造方法

    Thread的构造方法都是通过init方法来实现的

    
      	public Thread() {
            init(null, null, "Thread-" + nextThreadNum(), 0);
        }
    
        public Thread(Runnable target) {
            init(null, target, "Thread-" + nextThreadNum(), 0);
        }
    
        Thread(Runnable target, AccessControlContext acc) {
            init(null, target, "Thread-" + nextThreadNum(), 0, acc, false);
        }
    
        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);
        }
    
    

    5. init方法

    线程初始化方法

    	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 */
    			
                //security存在,则用security获得
                if (security != null) {
                    g = security.getThreadGroup();
                }
    
                //如果security获取不到,则用父线程的线程组
                if (g == null) {
                    g = parent.getThreadGroup();
                }
            }
    
            //检查线程组修改权限
            g.checkAccess();
    
            //检查是否有enableContextClassLoaderOverride权限
            if (security != null) {
                if (isCCLOverridden(getClass())) {
                    security.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
                }
            }
    		
            //检查是否有enableContextClassLoaderOverride权限
            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();
        }
    

    ThreadGroup

    ThreadGroup是位于java.lang包下的一个类,用于统一的线程管理.一旦一个线程加入到一个线程组后,就不能更换线程所在的线程组

    在这里插入图片描述

    6. run和start

    • run方法

    首先,虽然Thread实现了Runnable接口,但是却无法实现有意义的线程执行体,实际上它也无法提供,我们看一下Thread类中run()方法的实现

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

    一个线程的启动方法,一个线程只能start一次。主要是通过本地方法start0实现的。

    
        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();
    
           	//标记增加了启动线程,和init中的g.addUnstarted()类似
            //将线程添加到线程组中,并将线程组中未启动线程数减一,相当于移除
            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是一个本地方法
    private native void start0();
    

    start 和 run 方法的区别

    start() : 它的作用是启动一个新线程,新线程会执行相应的run()方法。start()不能被重复调用。
    run() : run()就和普通的成员方法一样,可以被重复调用。单独调用run()的话,会在当前线程中执行run(),而并不会启动新线程!

    阻塞 和 等待 的区别

    • 阻塞:当一个线程试图获取对象锁(非java.util.concurrent库中的锁,即synchronized),而该锁被其他线程持有,则该线程进入阻塞状态。它的特点是使用简单,由JVM调度器来决定唤醒自己,而不需要由另一个线程来显式唤醒自己,不响应中断

    • 等待:当一个线程等待另一个线程通知调度器一个条件时,该线程进入等待状态。它的特点是需要等待另一个线程显式地唤醒自己,实现灵活,语义更丰富,可响应中断。例如调用:Object.wait()、Thread.join()以及等待Lock或Condition。

    需要强调的是虽然synchronized和JUC里的Lock都实现锁的功能,但线程进入的状态是不一样的。synchronized会让线程进入阻塞态,而JUC里的Lock是用LockSupport.park()/unpark()来实现阻塞/唤醒的,会让线程进入等待态。但话又说回来,虽然等锁时进入的状态不一样,但被唤醒后又都进入runnable态,从行为效果来看又是一样的。

    8. join和sleep

    • join方法

    join方法用于等待线程执行完成,传入的时间单位为等待的最大时长,里面是一个while (isAlive())循环函数,当不传入时间参数,则为永久等待直到线程结束,传入时间参数,当时间到达时会结束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 {
                //IsAlive,顾名思义,它表示线程当前是否为可用状态,
                //如果线程已经启动,并且当前没有任何异常的话,则返回true,否则为false
                while (isAlive()) {
                    long delay = millis - now;
                    if (delay <= 0) {
                        break;
                    }
                    //等待
                    wait(delay);
                    now = System.currentTimeMillis() - base;
                }
            }
        }
    
    	//millis 毫秒 nanos纳秒
    	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 void join() throws InterruptedException {
            join(0);
        }
    
    • sleep方法

    sleep使当前线程睡眠指定时间

    • 获取当前线程的lock,currentThread.lock()
    • 通过while (true)循环sleep当前线程,并检测睡眠时间达到传输参数时间,break当前循环
    	//static的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
            sleep(millis);
        }
    
    	//本地方法
    	public static native void sleep(long millis) throws InterruptedException;
    

    join 和 sleep 区别

    join内部调用的是wait方法,wait可以释放锁,而sleep是持有锁的

    sleep是静态方法,通过currentThread.lock()获取当前线程的锁,只能作用于当前线程

    join(0)是一直等待线程执行完成,只有这个线程执行完后,才能执行其他线程,中间通过循环lock.wait(delay)实现,它是非静态方法

    join方法的作用

    主线程生成并启动了子线程,而子线程里要进行大量的耗时的运算(这里可以借鉴下线程的作用),当主线程处理完其他的事务后,需要用到子线程的处理结果,这个时候就要用到join();方法了。

    不如就借鉴下API里的说法:

    “等待该线程终止。”

    解释一下,是主线程等待子线程的终止。也就是说主线程的代码块中,如果碰到了t.join()方法,此时主线程需要等待(阻塞),等待子线程结束了(Waits for this thread to die.),才能继续执行t.join()之后的代码块。

    9. stop和interrupt

    • stop方法

    stop方法已经被弃用,强行调用的话会抛出UnsupportedOperationException异常

        @Deprecated
        public final void stop() {
            SecurityManager security = System.getSecurityManager();
            if (security != null) {
                checkAccess();
                if (this != Thread.currentThread()) {
                    security.checkPermission(SecurityConstants.STOP_THREAD_PERMISSION);
                }
            }
            // A zero status value corresponds to "NEW", it can't change to
            // not-NEW because we hold the lock.
            if (threadStatus != 0) {
                resume(); // Wake up thread if it was suspended; no-op otherwise
            }
    
            // The VM can handle all thread states
            stop0(new ThreadDeath());
        }
    
        @Deprecated
        public final synchronized void stop(Throwable obj) {
            throw new UnsupportedOperationException();
        }
    
    

    stop为什么被弃用

    该方法具有固有的不安全性。用 Thread.stop 来终止线程将释放它已经锁定的所有监视器(作为沿堆栈向上传播的未检查 ThreadDeath 异常的一个自然后果)。如果以前受这些监视器保护的任何对象都处于一种不一致的状态,则损坏的对象将对其他线程可见,这有可能导致任意的行为。stop 的许多使用都应由只修改某些变量以指示目标线程应该停止运行的代码来取代。目标线程应定期检查该变量,并且如果该变量指示它要停止运行,则从其运行方法依次返回。如果目标线程等待很长时间(例如基于一个条件变量),则应使用 interrupt 方法来中断该等待。

    • interrupt方法

    interrupt方法的目的是给线程发出中断信号,但是不保证线程真的会中断

    如果该线程处在可中断状态下,(调用了xx.wait(),或者Selector.select(),Thread.sleep()等特定会发生阻塞的api),那么该线程会立即被唤醒,同时会受到一个InterruptedException,同时,如果是阻塞在io上,对应的资源会被关闭。如果该线程接下来不执行“Thread.interrupted()方法(不是interrupt),那么该线程处理任何io资源的时候,都会导致这些资源关闭。当然,解决的办法就是调用一下interrupted(),不过这里需要程序员自行根据代码的逻辑来设定,根据自己的需求确认是否可以直接忽略该中断,还是应该马上退出。

        /**
         * 尝试中断线程
         */ 
    	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();
        }
    
    	/**
    	 * 此方法清除线程的中断状态,这意味着如果要连续两次调用此方法,则第二次调用将返回false。 
    	 * 如果线程的中断状态为true,则此方法将状态设置为false。
    	 */
    	public static boolean interrupted() {
            return currentThread().isInterrupted(true);
        }
    
    	//判断线程是否已经中断。线程的“中断状态”不受该方法的影响。
    	public boolean isInterrupted() {
            return isInterrupted(false);
        }
    
    	private native boolean isInterrupted(boolean ClearInterrupted);
    

    10. 线程状态

    public enum State {
           	//线程创建但未执行,也就是没有调用start方法
            NEW,
    
            //Java线程中将就绪和运行中两种状态笼统的称为“运行”
        	//线程对象创建后,其他线程(比如main线程)调用了该对象的start()方法。
        	//该状态的线程位于可运行线程池中,等待被线程调度选中,
        	//获取CPU的使用权,此时处于就绪状态(ready)。
        	//就绪状态的线程在获得CPU时间片后变为运行中状态(running)。
            RUNNABLE,
    
            //表示线程阻塞于锁
            BLOCKED,
    
            //基于上个BLOCKED状态来说,WAITING就是拿到锁了,
       		//处于wait过程中的状态,注意它是特指无限期的等待,
        	//也就是join()或者wait()等,它是join或者直接wait方法当获取到lock执行后,
        	//处于等待notify的WAITING状态。
            WAITING,
    
            //与上面WAITING相对,WAITING是指无限期的等待,
        	//TIMED_WAITING就是有限期的等待状态,包括join(long),wait(long),sleep(long)等。
            TIMED_WAITING,
    
            //线程执行完成,run结束的状态
            TERMINATED;
        }
    

    在这里插入图片描述

    参考

    https://www.cnblogs.com/waterystone/p/4920007.html

    https://juejin.im/post/5e047407e51d4557ee3a522d

    https://www.cnblogs.com/duanxz/p/5038471.html

    https://blog.csdn.net/pange1991/article/details/53860651

    https://www.yiibai.com/java_multithreading/java-thread-interrupted-method.html

    http://redisliu.blog.sohu.com/131647795.html

    https://blog.csdn.net/hl_java/article/details/79162097

    https://blog.csdn.net/paincupid/article/details/47626819

    展开全文
  • Thread 源码分析

    2018-06-26 12:13:00
    分析线程Thread源码时候需要了解几个知识点: 1.线程状态(NEW ,RUNABLE,BLOCKED,WATING,TIMED_WATING,TERMINATED) 2.线程的基础特性 3.如何创建线程 (构造函数) 4.jvm本地方法如何通过native方法实现run,...

     

    在分析线程Thread源码时候需要了解几个知识点:

    1.线程状态(NEW ,RUNABLE,BLOCKED,WATING,TIMED_WATING,TERMINATED)

    2.线程的基础特性

    3.如何创建线程 (构造函数)

    4.jvm本地方法如何通过native方法实现run,start,yeild,sleep,interrupt,interrupted,join,suspend ,resume等方法使用和原理。

    线程状态

      • NEW
        至今尚未启动的线程处于这种状态。
      • RUNNABLE
        正在 Java 虚拟机中执行的线程处于这种状态。
      • BLOCKED
        受阻塞并等待某个监视器锁的线程处于这种状态。
      • WAITING
        无限期地等待另一个线程来执行某一特定操作的线程处于这种状态。 WAITING 这个状态下是指线程拥有了某个锁之后, 调用了他的wait方法, 等待其他线程/锁拥有者调用 notify / notifyAll 一遍该线程可以继续下一步操作, 这里要区分 BLOCKED 和 WATING 的区别, 一个是在临界点外面等待进入, 一个是在理解点里面wait等待别人notify, 线程调用了join方法 join了另外的线程的时候, 也会进入WAITING状态, 等待被他join的线程执行结束
      • TIMED_WAITING
        等待另一个线程来执行取决于指定等待时间的操作的线程处于这种状态。
      • TERMINATED
        已退出的线程处于这种状态。

         

    在给定时间点上,一个线程只能处于一种状态。这些状态是虚拟机状态,它们并没有反映所有操作系统线程状态。

    线程的一些基本特性

    • 每个线程均有优先级  
    • private volatile String name;
          private int            priority;
          private Thread         threadQ;
          private long           eetop;

      线程优先级通过priority属性

    • 线程能被标记为守护线程

      守护线程在所有前台线程执行完毕后会自动kill掉。

    • 每个线程均分配一个name

              默认为线程分配一个名字,也可以自定义,默认为thread-i(++)线程名字为默认自增,线程安全方法。

    创建线程的几种方式

        1.继承thread并重写run方法

        class PrimeThread extends Thread {

        long minPrime; PrimeThread(long minPrime) {

            this.minPrime = minPrime;

         }

          public void run() { //需要实现的方法  . . . }

        }

      PrimeThread p = new PrimeThread(143);
      p.start();
    2.实现runable接口并实现run方法
    class PrimeRun implements Runnable {
    long minPrime; PrimeRun(long minPrime) {
    this.minPrime = minPrime;
    }
    public void run() { // 需要实现的方法 }
    PrimeRun p = new PrimeRun(143);
    new Thread(p).start();
    线程常用方法和主要原理
    当线程在初始化的时候会在调用本地方法,registerNatives()这个方法由jvm 虚拟机实现;

    (1)线程复用

    像线程池类高效的原因在于,线程池中的线程在完成任务后,不会销毁,而且缓存起来,每当用户请求一个线程处理任务时,线程池可以利用缓存的空闲线程来处理用户任务,这样避免了线程创建销毁带来的开销。

    在Thread类中有一个Runnable target的域,只需将target替换成新的Runnable即可。

    在实现run方法的时候

    (2)wait()和notify/notifyAll()方法

    wait()方法

    • 线程进入WAITING状态,并且释放掉它所占有的“锁标志”,从而使别的线程有机会抢占该锁,等待其他线程调用“锁标志“对象的notify或notifyAll方法恢复
    • wait方法是一个本地方法,其底层是通过一个叫做监视器锁的对象来完成的,所以调用wait方式时必须获取到monitor对象的所有权即通过Synchronized关键字,否则抛出IllegalMonitorStateException异常

    notify/notifyAll()方法

    • 在同一对象上去调用notify/notifyAll方法,就可以唤醒对应对象monitor上等待的线程了。notify和notifyAll的区别在于前者只能唤醒monitor上的一个线程,对其他线程没有影响,而notifyAll则唤醒所有的线程

    (3)sleep/yield/join方法解析

    sleep

    • sleep方法的作用是让当前线程暂停指定的时间(毫秒)
    • wait方法依赖于同步,而sleep方法可以直接调用
    • sleep方法只是暂时让出CPU的执行权,并不释放锁。而wait方法则需要释放锁

    yield

    • yield方法的作用是暂停当前线程,以便其他线程有机会执行,不过不能指定暂停的时间,并且也不能保证当前线程马上停止
    • yield只能使同优先级或更高优先级的线程有执行的机会

    join

    • 等待调用join方法的线程结束,再继续执行。如:t.join(),主要用于等待t线程运行结束
    • 作用是父线程等待子线程执行完成后再执行,换句话说就是将异步执行的线程合并为同步的线程

    suspend()和resume()

    • 这两个方法是配套使用的,suspend()是暂停线程,但并不释放资源,容易造成死锁情况

    stop()

    • 因为调用stop会使线程释放所有的锁,导致不安全情况,在调用stop时候,由锁保护的临界区可能处于状态不一致的情况,这不一致状态将暴露给其他线程
    • 推荐的做法是,维护一个状态变量,当线程需要停止时更改这一状态变量,该线程应检查这一状态变量,看该线程是否应该终止了

    (5)关于interrupt()中断函数

    • 其实调用这个函数并不是真的中断线程,这个函数只是将Thread中的interrupt标志设置为true,用户需自行检测这一变量,停止线程,这种做法避免了stop带来的问题
    而jvm有是如何来让内核和cup帮助我们工作的呢?
    当执行Thread.run 会内核函数调用fock产生一个线程
    Java线程在Windows及Linux平台上的实现方式,现在看来,是内核线程的实现方式。这种方式实现的线程,是直接由操作系统内核支持的——由内核完成线程切换,内核通过操纵调度器(Thread Scheduler)实现线程调度,并将线程任务反映到各个处理器上。
    内核线程是内核的一个分身。程序一般不直接使用该内核线程,而是使用其高级接口,即轻量级进程(LWP),也即线程。这看起来可能很拗口。看图:

    KLT即内核线程Kernel Thread,是“内核分身”。每一个KLT对应到进程P中的某一个轻量级进程LWP(也即线程),期间要经过用户态、内核态的切换,并在Thread Scheduler 下反应到处理器CPU上。如果是多N:1 会造成性能瓶颈,所有线程都在一个cup上。
    从线程模型上看是1:1(内核线程),Java SE最常用的JVM是Oracle/Sun研发的HotSpot VM。在这个JVM的较新版本所支持的所有平台上,它都是使用1:1线程模型的——除了Solaris之外,它是个特例。
    这种线程模型也有一定的缺陷,就是程序在使用内核线程时候会造成操作系统上多次来回切换内核态和用户态度,而且lwp支持数是有限的。应用程序开辟的线程至少等于cpu的内核数量。
    java 最少内核数量Runtime.getRuntime().availableProcessors();能获得最小线程数。
    这样就会带来一个问题,如何开辟合适的线程数?这里需要知道具体应用计算密集型还是io密集型。显然大部分java都是io密集型应用。
    如果所有的任务都是计算密集型的,这个最小线程数量就是我们需要的线程数。在某个线程在执行io操作时候,那么线程被阻塞,处理器会立刻切换到另一个合适的线程去执行,如果我们只拥有与内核数量一样多的线程,即使我们有任务要执行,他们也不能执行,因为处理器没有可以用来调度的线程。
    如果线程有50%的时间被阻塞,线程的数量就应该是内核数量的2倍。如果更少的比例被阻塞,那么它们就是计算密集型的,则需要开辟较少的线程。如果有更多的时间被阻塞,那么就是IO密集型的程序,则可以开辟更多的线程。于是我们可以得到下面的线程数量计算公式:
    线程数量=内核数量 / (1 - 阻塞率)
    我们可以通过相应的分析工具或者java的management包来得到阻塞率的数值。
     
     
     
     
     
    
    


     

     

    转载于:https://www.cnblogs.com/Cjbolgs/p/9228089.html

    展开全文
  • thread 源码分析

    2015-12-08 15:36:17
    线程是程序的一个执行流程,Java虚拟机允许多个线程同时并发...public Thread() { init(null, null, "Thread-" + nextThreadNum(), 0); } // target - 任务 public Thread(Runnable target) { ini
    线程是程序的一个执行流程,Java虚拟机允许多个线程同时并发执行。

    1. 构造方法
    Java代码  收藏代码
    1. public Thread() {  
    2.     init(nullnull"Thread-" + nextThreadNum(), 0);  
    3. }  
    4.   
    5. // target - 任务  
    6. public Thread(Runnable target) {  
    7.     init(null, target, "Thread-" + nextThreadNum(), 0);  
    8. }  
    9.   
    10. // group - 线程组,target - 任务  
    11. public Thread(ThreadGroup group, Runnable target) {  
    12.     init(group, target, "Thread-" + nextThreadNum(), 0);  
    13. }  
    14.   
    15. // name - 线程名称  
    16. public Thread(String name) {  
    17.     init(nullnull, name, 0);  
    18. }  
    19.   
    20. // group - 线程组,name - 线程名称  
    21. public Thread(ThreadGroup group, String name) {  
    22.     init(group, null, name, 0);  
    23. }  
    24.   
    25. // target - 任务,name - 线程名称  
    26. public Thread(Runnable target, String name) {  
    27.     init(null, target, name, 0);  
    28. }  
    29.   
    30. // group - 线程组,target - 任务,name - 线程名称  
    31. public Thread(ThreadGroup group, Runnable target, String name) {  
    32.     init(group, target, name, 0);  
    33. }  
    34.   
    35. // group - 线程组,target - 任务,name - 线程名称, stackSize - 栈大小  
    36. // 这里的stackSize只是提供一个参考值,和平台相关,JVM根据情况会做适当的调整。  
    37. // stackSize大一些,线程就会不容易抛StackOverflowError。  
    38. // stackSize小一些,多个线程并发执行不容易抛OutOfMemoryError。  
    39. // 栈大小,最大递归深度和并发水平是和平台相关的。  
    40. public Thread(ThreadGroup group, Runnable target, String name,  
    41.                  long stackSize) {  
    42.     init(group, target, name, stackSize);  
    43. }  


    2. 线程启动
    Java代码  收藏代码
    1. public synchronized void start() {  
    2.     if (threadStatus != 0 || this != me)  
    3.         throw new IllegalThreadStateException();  
    4.     group.add(this);  
    5.     start0();  
    6.     if (stopBeforeStart) {  
    7.         stop0(throwableFromStop);  
    8.     }  
    9. }  
    10.   
    11. private native void start0();  


    3. 任务执行

    一般由JVM来调用该方法,也可以手动调用。

    a. 如果子类覆盖了run()方法,则调用子类的run()
    b. 如果a不成立:
       b1. 指定了target,则调用target的run()
       b2. 没有指定target,该方法不做任何事并返回
    Java代码  收藏代码
    1. public void run() {  
    2.     if (target != null) {  
    3.         target.run();  
    4.     }  
    5. }  


    所以,下面MyThread的run方法会执行:
    Java代码  收藏代码
    1. public class MyThread extends Thread {  
    2.     public void run() {  
    3.         System.out.println("MyThread.run()");  
    4.     }  
    5. }  
    6.   
    7. new MyThread(new Runnable() {  
    8.     public void run() {  
    9.         System.out.println("Runnable.run()");  
    10.     }  
    11. }).start();  


    4. 清理工作

    由系统来调用该方法,在线程退出之前清理分配的资源。
    Java代码  收藏代码
    1. private void exit() {  
    2.     if (group != null) {  
    3.         group.remove(this);  
    4.         group = null;  
    5.     }  
    6.   
    7.     target = null;  
    8.     threadLocals = null;  
    9.     inheritableThreadLocals = null;  
    10.     inheritedAccessControlContext = null;  
    11.     blocker = null;  
    12.     uncaughtExceptionHandler = null;  
    13. }  


    5. yield方法

    使当前执行线程暂停一会,让其它线程得以执行。只是临时让出时间片,不会释放拥有的锁。
    Java代码  收藏代码
    1. public static native void yield();  


    6. sleep方法

    使当前执行线程休眠指定的时间,不释放持有的锁。
    Java代码  收藏代码
    1. public static native void sleep(long millis) throws InterruptedException;  
    2.   
    3. public static void sleep(long millis, int nanos)   
    4.     throws InterruptedException {  
    5.     if (millis < 0) {  
    6.         throw new IllegalArgumentException("timeout value is negative");  
    7.     }  
    8.   
    9.     if (nanos < 0 || nanos > 999999) {  
    10.         throw new IllegalArgumentException(  
    11.             "nanosecond timeout value out of range");  
    12.     }  
    13.   
    14.     if (nanos >= 500000 || (nanos != 0 && millis == 0)) { // 纳秒四舍五入,或者毫秒为0且纳秒不为0时  
    15.         millis++;  
    16.     }  
    17.   
    18.     sleep(millis);  
    19. }  


    7. join方法

    等待该线程执行,直到超时或者终止。

    可以作为线程通信的一种方式:A线程调用B线程的join方法(阻塞),等待B完成后再往下执行。

    Java代码  收藏代码
    1. public final synchronized void join(long millis)   
    2.     throws InterruptedException {  
    3.     long base = System.currentTimeMillis();  
    4.     long now = 0;  
    5.   
    6.     if (millis < 0) {  
    7.         throw new IllegalArgumentException("timeout value is negative");  
    8.     }  
    9.   
    10.     if (millis == 0) { // 0代表没有时间限制  
    11.         while (isAlive()) {  
    12.             wait(0); // 无限期的等待  
    13.         }  
    14.     } else {  
    15.         while (isAlive()) {  
    16.             long delay = millis - now;  
    17.             if (delay <= 0) {  
    18.                 break;  
    19.             }  
    20.             wait(delay); // 有限期的等待  
    21.             now = System.currentTimeMillis() - base;  
    22.         }  
    23.     }  
    24. }  
    25.   
    26. public final synchronized void join(long millis, int nanos)   
    27.     throws InterruptedException {  
    28.   
    29.     if (millis < 0) {  
    30.         throw new IllegalArgumentException("timeout value is negative");  
    31.     }  
    32.   
    33.     if (nanos < 0 || nanos > 999999) {  
    34.         throw new IllegalArgumentException(  
    35.             "nanosecond timeout value out of range");  
    36.     }  
    37.   
    38.     if (nanos >= 500000 || (nanos != 0 && millis == 0)) { // 纳秒四舍五入,或者毫秒为0且纳秒不为0时  
    39.         millis++;  
    40.     }  
    41.   
    42.     join(millis);  
    43. }  
    44.   
    45. public final void join() throws InterruptedException {  
    46.     join(0);  
    47. }  


    8. 中断
    Java代码  收藏代码
    1. public void interrupt() {  
    2.     if (this != Thread.currentThread())  
    3.         checkAccess();  
    4.   
    5.     synchronized (blockerLock) {  
    6.         Interruptible b = blocker;  
    7.         if (b != null) {  
    8.             interrupt0();       // Just to set the interrupt flag  
    9.             b.interrupt();  
    10.             return;  
    11.         }  
    12.     }  
    13.     interrupt0();  
    14. }  
    15.   
    16. // 静态方法:查看当前线程是否中断,并且清除中断标志。  
    17. public static boolean interrupted() {  
    18.     return currentThread().isInterrupted(true);  
    19. }  
    20.   
    21. // 查看该线程是否中断,但不清除中断标志。  
    22. public boolean isInterrupted() {  
    23.     return isInterrupted(false);  
    24. }  
    25.   
    26. private native boolean isInterrupted(boolean ClearInterrupted);  


    9. 守护/普通进程

    JVM有很多守护进程:如垃圾回收线程等。没有前台(Non-Daemon)线程时,JVM会退出。

    Java代码  收藏代码
    1. public final void setDaemon(boolean on) {  
    2.     checkAccess();  
    3.     if (isAlive()) {  
    4.         throw new IllegalThreadStateException();  
    5.     }  
    6.     daemon = on;  
    7. }  


    10. 上下文类加载器
    Java代码  收藏代码
    1. public ClassLoader getContextClassLoader() {  
    2.     if (contextClassLoader == null)  
    3.         return null;  
    4.     SecurityManager sm = System.getSecurityManager();  
    5.     if (sm != null) {  
    6.         ClassLoader ccl = ClassLoader.getCallerClassLoader(); // 调用者的类加载器  
    7.         if (ccl != null && ccl != contextClassLoader &&   
    8.             !contextClassLoader.isAncestor(ccl)) { // 调用者得类加载器不为空,且不和该线程的类加载器相同,也不是该类加载器的祖先时  
    9.             sm.checkPermission(SecurityConstants.GET_CLASSLOADER_PERMISSION);  
    10.         }  
    11.     }  
    12.     return contextClassLoader;  
    13. }  
    14.   
    15. public void setContextClassLoader(ClassLoader cl) {  
    16.     SecurityManager sm = System.getSecurityManager();  
    17.     if (sm != null) {  
    18.         sm.checkPermission(new RuntimePermission("setContextClassLoader"));  
    19.     }  
    20.     contextClassLoader = cl;  
    21. }  


    11. 线程的优先级
    Java代码  收藏代码
    1. // 最小的优先级  
    2. public final static int MIN_PRIORITY = 1;  
    3.   
    4. // 正常的优先级  
    5. public final static int NORM_PRIORITY = 5;  
    6.   
    7. // 最大的优先级  
    8. public final static int MAX_PRIORITY = 10;  
    9.   
    10. public final void setPriority(int newPriority) {  
    11.     ThreadGroup g;  
    12.     checkAccess();  
    13.     if (newPriority > MAX_PRIORITY || newPriority < MIN_PRIORITY) {  
    14.         throw new IllegalArgumentException();  
    15.     }  
    16.     if((g = getThreadGroup()) != null) {  
    17.         if (newPriority > g.getMaxPriority()) {  
    18.             newPriority = g.getMaxPriority();  
    19.         }  
    20.         setPriority0(priority = newPriority);  
    21.     }  
    22. }  

    a. 线程的优先级在不同的系统平台上,对应的系统优先级会不同;可能多个优先级对应同一个系统优先级。

    b. 优先级高的线程并不一定会优先执行,这个由JVM来解译并向系统提供参考。

    12. 线程的状态

    Java代码  收藏代码
    1. public enum State {  
    2.     // 新建的线程,还没调用start()方法  
    3.     NEW,  
    4.   
    5.     // 可以运行,需要等到其它资源(如CPU)就绪才能运行  
    6.     RUNNABLE,  
    7.   
    8.     // 线程调用wait()后等待内置锁进入同步块或方法  
    9.     BLOCKED,  
    10.   
    11.     // 在调用无参的wait(),Thread.join()或LockSupport.lock()方法后进入等待状态  
    12.     WAITING,  
    13.   
    14.     // 调用Thread.sleep(), 有时间参数的wait(), 有时间参数的Thread.join(), LockSupport.parkNanos或LockSupport.parkUtil方法后进行有期限的等待状态  
    15.     TIMED_WAITING,  
    16.   
    17.     // 执行完毕的线程状态  
    18.     TERMINATED;  
    19. }  
    20.   
    21. public State getState() {  
    22.     // get current thread state  
    23.     return sun.misc.VM.toThreadState(threadStatus);  
    24. }  


    13. 未检查异常处理器
    Java代码  收藏代码
    1. public interface UncaughtExceptionHandler {   
    2.     void uncaughtException(Thread t, Throwable e);  
    3. }  
    4.   
    5. private volatile UncaughtExceptionHandler uncaughtExceptionHandler;  
    6.   
    7. private static volatile UncaughtExceptionHandler defaultUncaughtExceptionHandler;  
    8.   
    9. // 设置线程默认的未检查异常处理器  
    10. public static void setDefaultUncaughtExceptionHandler(UncaughtExceptionHandler eh) {  
    11.     SecurityManager sm = System.getSecurityManager();  
    12.     if (sm != null) {  
    13.         sm.checkPermission(  
    14.             new RuntimePermission("setDefaultUncaughtExceptionHandler")  
    15.         );  
    16.     }  
    17.   
    18.     defaultUncaughtExceptionHandler = eh;  
    19. }  
    20.   
    21. public UncaughtExceptionHandler getUncaughtExceptionHandler() {   
    22.     return uncaughtExceptionHandler != null ?  
    23.         uncaughtExceptionHandler : group;  
    24. }  
    25.   
    26. public void setUncaughtExceptionHandler(UncaughtExceptionHandler eh) {   
    27.     checkAccess();  
    28.     uncaughtExceptionHandler = eh;  
    29. }  
    30.   
    31. // 只由JVM调用  
    32. private void dispatchUncaughtException(Throwable e) {  
    33.     getUncaughtExceptionHandler().uncaughtException(this, e);  
    34. }  
    分享到:

    展开全文
  • Runnable和Thread源码分析

    千次阅读 2019-01-04 21:35:51
    Runnable和Thread源码分析 问题的引入: Runnable是一个接口,它只有一个run()抽象方法,run()抽象方法需要被实现后才能使用。Thread类继承了Runnable接口,并且Thread中实现了run()方法。最后,通过Thread类的...

    Runnable和Thread源码分析

    问题的引入
    Runnable是一个接口,它只有一个run()抽象方法,run()抽象方法需要被实现后才能使用。Thread类继承了Runnable接口,并且Thread中实现了run()方法。最后,通过Thread类的start()方法就可以启动线程了。这是我们平时在应用中使用线程的常用方法。如果多加思考你会发现以下几个问题:
    1、为什么调用start()方法就可以启动线程,而不是调用run()。start()方法和决定线程运行内容的run()方法又有什么联系呢?
    2、在Runnable层面实现run()方法和Thread层面实现run()方法的区别。
    3、怎么用Runnable实现run方法,并在Thread中执行Runnable实例化后重写的run方法。
    不着急下结论,我们通过分析源码来理清两者的关系。
    Runnable源码
    打开如下Runnable的源码,我们可以发现,它只含有一个抽象的run()方法。因为它是接口,所以需要被继承后重写run()方法才能使用。我们可以简单思考一下,如果需要启动一个新的线程,这意味着需要分配给它CPU资源来执行这个线程,而CPU是不归JVM(Java虚拟机)直接管辖的,自然需要通过JVM通过外部的接口来实现和操作系统的对话,调整CPU资源的分配,所以,线程的创建一定是一个native方法(实现Java调用底层的C、C++代码)。接口中的run()方法中的内容,仅仅代表线程运行的内容。一个结论出来了,Thread类中必然有native方法,Runnable无法脱离Thread类来新建线程。如果我们在new一个线程对象后直接调用run方法,也只是让当前线程去执行run()方法中的语句罢了,并没有实现多线程。

    public interface Runnable {
        public abstract void run();
    }
    

    Thread源码
    接下来我们看看Thread类的源码。
    从下面这句我们可以看出Thread继承了Runnbale接口。

    public class Thread implements Runnable {
    

    下面是Thread类的run()方法。里面的target对象是什么?为什么target能调用run()方法?这个run()方法是谁的run()方法?

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

    为了解决问题,我们把关注点放到Thread类的这几行代码上。在Thread类的一个构造方法中Runnable接口的实例化对象target被传入其中,通过几个方法的调用,并通过this.target = target;语句把Runnable的实例化对象保存在了Thread类中,成为了Thread类的一个成员变量,那么它调用的run()方法自然是在Runnable实例化中重写的run()方法。

        private Runnable target;
        public Thread(Runnable target) {
            init(null, target, "Thread-" + nextThreadNum(), 0);        
        }    
       private void init(ThreadGroup g, Runnable target, String name, long stackSize) {
            init(g, target, name, stackSize, null);
        }
       private void init(ThreadGroup g, Runnable target, String name,long stackSize, AccessControlContext acc) {
              ....................  //省略
             this.target = target;
             ......................//省略
        }
    

    这里大家可能会有一点疑问,Runnable是一个接口,接口为什么会有对象?
    Runnable入口参数的两种实现形式
    1、定义一个类继承Runnable接口,并重写run()方法,创建这个类的实例化对象,在new一个Thread类对象的时候作为入口参数传入构造方法。根据里氏代换原则,任何基类(父类,在这里接口也适用)可以出现的地方,子类也可以出现(此处可以理解为向上转型),这个类的对象可以传入Thread类的构造方法。实现代码如下:

    class MyThread implements Runnable{
    	public void run() {	    
    		while(ticket>0)
    		{
    			System.out.println("");
    		}
    	}
    }
    

    2、通过匿名内部类,直接new一个Runnable然后紧接着在后面重写run方法,把它作为参数传入构造方法,因为没有对象名,叫做匿名内部类,它可以实例化接口和抽象类。类似如下实现:

              Thread th1 = new Thread(new Runnable()
             	{
      		         public void run()
      		         {
      		        	 System.out.println("");
      		         }
      			});
    

    start()方法
    最后再看看线程创建的核心,start()方法。这里我们不关注其他的变量和内容,只关注线程的实现。我们发现在这个方法中调用了start0()方法。

        public synchronized void start() {
            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()方法就是我们一直在寻找的native方法,它没有Java的方法体,它关系到下层C、C++语言的调用,执行这个方法就会为线程申请物理资源并启动线程,线程运行后,便会执行run()方法。

        private native void start0();
        /**
         * If this thread was constructed using a separate
         * <code>Runnable</code> run object, then that
         * <code>Runnable</code> object's <code>run</code> method is called;
         * otherwise, this method does nothing and returns.
         * <p>
         * Subclasses of <code>Thread</code> should override this method.
         *
         * @see     #start()
         * @see     #stop()
         * @see     #Thread(ThreadGroup, Runnable, String)
         */
    

    思路整理
    关键:run()方法存在于Thread类和Runnable接口中。
    1、如果我们选择创建一个类继承Thread,然后重写run()方法,原先的run()方法被子类重写的run()方法覆盖,原先run()方法中的 target.run();不可能被调用,线程对象调用start()方法后运行的是重写后的run()方法中的内容。
    2、如果我们实例化Runnable对象,重写它的run()方法(注意哦!这里的run()方法不是Thread类中的run()方法),然后
    在创建Thread对象的时候把它传入,接下来用Thread对象调用start()方法执行的就是target.run(),也就是Runnable实例化后重写的run()方法的方法体了。
    3、如果在传入Runnable实例化对象后又重写了Thread类的run()方法,最终调用的是Thread类中重写的run()方法。

    Runnable和Thread在应用上的区别

    一个简单的买票程序
    一、写一个类继承Thread,重写run()方法,new若干线程执行买票程序。
    1、代码实现

    public class Test {	
    	    public static void main(String[] args)
    	    {
    	    	MyThread mt1 = new MyThread();
    	    	MyThread mt2 = new MyThread();
    	    	MyThread mt3 = new MyThread();
    	    	mt1.start();
    	    	mt2.start();
    	    	mt3.start();
    	    }
    }
    class MyThread extends Thread{
        private int ticket=10;
    	public void run() {	    
    		while(ticket>0)
    		{
    			System.out.println("卖出了ticket:"+ticket--);
    		}
    	}
    }
    

    2、运行结果
    在这里插入图片描述
    3、结果分析:每一张票都被买了3次,因为在每次new一个对象后,都会在java的堆中开辟一个新的空间来存储对象的信息,这就包括成员变量信息,在这里就是ticket变量,new了3个对象,就有3个地方存储了ticket值,彼此独立,因此每个线程执行着彼此独立的买票流程。
    4、解决方法:可以ticket变量加上static修饰,使得它变为被所有对象共享的静态变量,可以解决这个问题。当然上述方法并不能体现我们这次讨论的实质,根据结果分析我们可以知道在Thread重写run()方法这种方式下多线程不能共享资源,它们彼此独立。另外,使用Runnable的run()方法实现也可以解决这个问题,下面来看第二种方法。
    二、实现Runnable接口,作为参数传入Thread类。
    1、代码实现

    public class Test {	
    	    public static void main(String[] args)
    	    {
    	    	MyThread mt = new MyThread();
    	    	Thread th1 = new Thread(mt);
    	    	Thread th2 = new Thread(mt);
    	    	Thread th3 = new Thread(mt);
    	    	th1.start();
    	    	th2.start();
    	    	th3.start();
    	    }
    }
    class MyThread implements Runnable{
        private int ticket=10;
    	public void run() {	    
    		while(ticket>0)
    		{
    			System.out.println("卖出了ticket:"+ticket--);
    		}
    	}	
    }
    

    2、运行结果
    在这里插入图片描述
    3、结果分析:实现了10张票就卖出10次,因为通过前面的源码分析我们可以知道,每个线程都持有着Runnable实例化对象的引用,调用的都是这个对象的run()方法,因此是对同一个ticket变量进行操作。至于图中出现的顺序问题,是因为多线程操作的时候,修改变量的速度远大于打印出这个变量的速度,在改变了ticket值之后还没来得及打印出来,下一个线程就继续对变量进行操作了。
    总结:在多线程的应用中我们更加倾向于使用Runnable来实现run()方法,因为这可以使得多线程在执行相同的代码的同时操作相同成员变量(多线程共享资源)。因此,多线程的最佳打开方式是:写一个类继承Runnable接口,重写run()方法,再以Thread为媒介启动线程。

    展开全文
  • 18-Thread源码分析

    2018-12-03 15:40:52
    Thread源码分析 volatile Object parkBlocker 属性 阻塞者,一个用来记录当前线程阻塞信息的对象。该属性只有在线程被阻塞时才有意义,没有setter/getter方法,通过unsafe.objectFieldOffset进行修改。 当程序...
  • 1.线程的使用 ...5.nativeCreate()的简单代码分析 创建线程。 调用run()方法 nativeCreate()方法具体的代码看博客 https://blog.csdn.net/android_text/article/details/70256166
  • java Thread源码分析

    2018-12-30 23:09:00
    一、使用 java 多线程 java多线程其中两种使用方式: 1、继承 Thread 类 2、实现 Runnable 接口 1 public class ThreadTest { ... 2 public static void main... 3 Thread t1 = new MyThread("thread-0001"); ...
  • ceph thread源码分析

    2018-11-20 10:47:19
    可以说,thread是ceph中最基本的模块,因此我们的ceph源码分析thread开始.本篇主要介绍三部分内容: thread mutex condition variable thread 源码文件: src/common/thread.h src/common/thread....
  • Java Thread源码分析

    2018-03-06 19:47:00
    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 ...
  • java thread源码分析

    2015-12-26 23:16:09
    线程的六种状态[通过枚举定义]: /** * A thread state. A thread can be in one of the following states: * * {@link #NEW} * A thread that has not yet started is in this state. *

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 2,501
精华内容 1,000
关键字:

thread源码分析