• java创建线程的三种方式——附源码说明


    首先我们知道启动一个线程都是通过调用Thread类的start()来开启一个新线程,那么我们就来一起看看Thread类的start()方法源码:

        /**
         * Causes this thread to begin execution; the Java Virtual Machine
         * calls the <code>run</code> method of this thread.
         * <p>
         * The result is that two threads are running concurrently: the
         * current thread (which returns from the call to the
         * <code>start</code> method) and the other thread (which executes its
         * <code>run</code> method).
         * <p>
         * It is never legal to start a thread more than once.
         * In particular, a thread may not be restarted once it has completed
         * execution.
         *
         * @exception  IllegalThreadStateException  if the thread was already
         *               started.
         * @see        #run()
         * @see        #stop()
         */
        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 */
                }
            }
        }
    
        private native void start0();

    start方法做了两件事:1、将此线程加入线程组中,2、启动这个线程。由于开启线程是通过start0()这个native方法,我们看不到里面的细节,但是通过注释 —— the Java Virtual Machine calls the run method of this thread —— 我们可以知道run方法其实就相当于是个job,线程就是执行这个job的程序,专门为run方法服务的。所以后面三种方式不管那种其实都是围绕着run方法进行实现这个线程的“job”。

    1、通过继承Thread类,覆盖run方法创建线程

      这种方式最粗暴简洁,直接给这个线程分配了“job”。

    2、通过传入Runnable类型对象创建线程。

      

     1     public Thread(Runnable target) {
     2         init(null, target, "Thread-" + nextThreadNum(), 0);
     3     }
     4 
     5     private void init(ThreadGroup g, Runnable target, String name,
     6                       long stackSize) {
     7         init(g, target, name, stackSize, null, true);
     8     }
     9 
    10 
    11     /**
    12      * Initializes a Thread.
    13      *
    14      * @param g the Thread group
    15      * @param target the object whose run() method gets called
    16      * @param name the name of the new Thread
    17      * @param stackSize the desired stack size for the new thread, or
    18      *        zero to indicate that this parameter is to be ignored.
    19      * @param acc the AccessControlContext to inherit, or
    20      *            AccessController.getContext() if null
    21      * @param inheritThreadLocals if {@code true}, inherit initial values for
    22      *            inheritable thread-locals from the constructing thread
    23      */
    24     private void init(ThreadGroup g, Runnable target, String name,
    25                       long stackSize, AccessControlContext acc,
    26                       boolean inheritThreadLocals) {
    27         if (name == null) {
    28             throw new NullPointerException("name cannot be null");
    29         }
    30 
    31         this.name = name;
    32 
    33         Thread parent = currentThread();
    34         SecurityManager security = System.getSecurityManager();
    35         if (g == null) {
    36             /* Determine if it's an applet or not */
    37 
    38             /* If there is a security manager, ask the security manager
    39                what to do. */
    40             if (security != null) {
    41                 g = security.getThreadGroup();
    42             }
    43 
    44             /* If the security doesn't have a strong opinion of the matter
    45                use the parent thread group. */
    46             if (g == null) {
    47                 g = parent.getThreadGroup();
    48             }
    49         }
    50 
    51         /* checkAccess regardless of whether or not threadgroup is
    52            explicitly passed in. */
    53         g.checkAccess();
    54 
    55         /*
    56          * Do we have the required permissions?
    57          */
    58         if (security != null) {
    59             if (isCCLOverridden(getClass())) {
    60                 security.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
    61             }
    62         }
    63 
    64         g.addUnstarted();
    65 
    66         this.group = g;
    67         this.daemon = parent.isDaemon();
    68         this.priority = parent.getPriority();
    69         if (security == null || isCCLOverridden(parent.getClass()))
    70             this.contextClassLoader = parent.getContextClassLoader();
    71         else
    72             this.contextClassLoader = parent.contextClassLoader;
    73         this.inheritedAccessControlContext =
    74                 acc != null ? acc : AccessController.getContext();
    75         this.target = target;
    76         setPriority(priority);
    77         if (inheritThreadLocals && parent.inheritableThreadLocals != null)
    78             this.inheritableThreadLocals =
    79                 ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
    80         /* Stash the specified stack size in case the VM cares */
    81         this.stackSize = stackSize;
    82 
    83         /* Set thread ID */
    84         tid = nextThreadID();
    85     }

    通过上面的源码我们可以看到,当你用 Thread t = new Thread(runnable); 创建线程时会调用Thread类的init方法,最关键的是第75行,将传进来的runnable对象赋给Thread类的target属性,这个时候再看看我们的“job”——也就是run方法:

     1     /**
     2      * If this thread was constructed using a separate
     3      * <code>Runnable</code> run object, then that
     4      * <code>Runnable</code> object's <code>run</code> method is called;
     5      * otherwise, this method does nothing and returns.
     6      * <p>
     7      * Subclasses of <code>Thread</code> should override this method.
     8      *
     9      * @see     #start()
    10      * @see     #stop()
    11      * @see     #Thread(ThreadGroup, Runnable, String)
    12      */
    13     @Override
    14     public void run() {
    15         if (target != null) {
    16             target.run();
    17         }
    18     }

    如果target不为null,那么我们就执行target的run方法,这种方式就相当于job由用户写好,然后交给线程去执行。

    3、通过传入FutureTask类型对象创建线程

    FutureTask实现了Runnable接口,所以传入FutureTask类型对象和传入Runnable类型的对象本质上原理一样,只不过FutureTask可以看作是加强版的Runnable,多了一个存储返回值和获取返回值的功能。

    既然FutureTask实现了Runnable接口,那么一定实现了run方法,直接上源码:

     1     public void run() {
     2         if (state != NEW ||
     3             !UNSAFE.compareAndSwapObject(this, runnerOffset,
     4                                          null, Thread.currentThread()))
     5             return;
     6         try {
     7             Callable<V> c = callable;
     8             if (c != null && state == NEW) {
     9                 V result;
    10                 boolean ran;
    11                 try {
    12                     result = c.call();
    13                     ran = true;
    14                 } catch (Throwable ex) {
    15                     result = null;
    16                     ran = false;
    17                     setException(ex);
    18                 }
    19                 if (ran)
    20                     set(result);
    21             }
    22         } finally {
    23             // runner must be non-null until state is settled to
    24             // prevent concurrent calls to run()
    25             runner = null;
    26             // state must be re-read after nulling runner to prevent
    27             // leaked interrupts
    28             int s = state;
    29             if (s >= INTERRUPTING)
    30                 handlePossibleCancellationInterrupt(s);
    31         }
    32     }

    run方法主要是执行Callable对象的call方法(上方源码第12行),接受call方法的返回值,并调用set方法将result存储起来。

        /**
         * Sets the result of this future to the given value unless
         * this future has already been set or has been cancelled.
         *
         * <p>This method is invoked internally by the {@link #run} method
         * upon successful completion of the computation.
         *
         * @param v the value
         */
        protected void set(V v) {
            if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
                outcome = v;
                UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // final state
                finishCompletion();
            }
        }

    set方法主要是将返回值赋给outcome属性,当调用FutureTask的get方法时,会先判定线程是否正常结束,如果正常结束会将outcome返回。否则会抛出异常。

     1     /**
     2      * Creates a {@code FutureTask} that will, upon running, execute the
     3      * given {@code Callable}.
     4      *
     5      * @param  callable the callable task
     6      * @throws NullPointerException if the callable is null
     7      */
     8     public FutureTask(Callable<V> callable) {
     9         if (callable == null)
    10             throw new NullPointerException();
    11         this.callable = callable;
    12         this.state = NEW;       // ensure visibility of callable
    13     }

    callable对象是创建FutureTask对象时传进来的,所以这种方式的“job”来自Callable对象的call方法。

      

  • 相关阅读:
    C# richtextbox 自动下拉到最后 方法 & RichTextBox读取txt中文后出现乱码
    SQL基础问题整理
    xgqfrms™, xgqfrms® : xgqfrms's offical website of GitHub!
    xgqfrms™, xgqfrms® : xgqfrms's offical website of GitHub!
    xgqfrms™, xgqfrms® : xgqfrms's offical website of GitHub!
    xgqfrms™, xgqfrms® : xgqfrms's offical website of GitHub!
    xgqfrms™, xgqfrms® : xgqfrms's offical website of GitHub!
    Springcloud +redis集群
    详述一次大量删除导致MySQL慢查的分析
    2019年开源数据库报告发布:MySQL仍卫冕!
  • 原文地址:https://www.cnblogs.com/leonandyou/p/15427053.html
Copyright © 2020-2023  润新知