1. 程式人生 > >Java多執行緒之Thread

Java多執行緒之Thread

執行緒的定義

  • 是作業系統進行運算排程的最小單位。它被包含在程序之中,是程序中的實際運作單位。 一個執行緒指的是程序中一個單一順序的控制流,一個程序中可以並行多個執行緒,每條執行緒並行執行不同的任務。
  • 每個執行緒都有一個優先順序,預設是5(1~10,值越大,優先順序越高)
  • 建立執行緒的兩種方式:繼承Thread類、實現Runnable介面
  • 每個執行緒都有一個用於標識的名稱,一個名稱並不一定對應一個執行緒
    上下文切換:
    CPU在同一時刻只能執行一個執行緒,當執行一個執行緒的時候去執行另外一個執行緒,這個過程就是上下文切換。(其實就是儲存和恢復CPU狀態的過程)

Thread類的構造方法

public Thread (Runnable runnable)
public Thread (ThreadGroup group , Runnable runnale)
public Thread (String name)
public Thread (ThreadGroup group , String name)
public Thread (Runnable runnable, String name)
public Thread (ThreadGroup group, Runnable runnable, String name)
public Thread (ThreadGroup group
, Runnable runnable, String name, long stackSize)

執行緒的狀態

public enum State {
//執行緒尚未啟動
NEW,

//可執行狀態
RUNNABLE,

/**
* 阻塞狀態
* 處於阻塞狀態的執行緒正在等待監視器鎖進入同步塊/方法,或在呼叫後重新輸入同步塊/方法
*/
BLOCKED,

/**
* 等待狀態,呼叫以下方法可轉至該狀態
* wait()
* join()
* LockSupport.park()
*/
WAITING,

/**
* 具有指定等待時間的等待執行緒的執行緒狀態
* sleep()
* wait()
* join()
* LockSupport.parkNanos()
* LockSupport.parkUntil()
*/
TIMED_WAITING, /** * 終止狀態,已經執行完畢 */ TERMINATED; }

相關方法

  • 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();
 }
  • 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.
      * O 對應 NEW 狀態
      * 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();
  • yield() 使當前執行緒讓出CPU資源給其他執行緒,進入就緒狀態,重新獲取CPU時間片
/**
  * A hint to the scheduler that the current thread is willing to yield
  * its current use of a processor. The scheduler is free to ignore this
  * hint.
  *
  * <p> Yield is a heuristic attempt to improve relative progression
  * between threads that would otherwise over-utilise a CPU. Its use
  * should be combined with detailed profiling and benchmarking to
  * ensure that it actually has the desired effect.
  *
  * <p> It is rarely appropriate to use this method. It may be useful
  * for debugging or testing purposes, where it may help to reproduce
  * bugs due to race conditions. It may also be useful when designing
  * concurrency control constructs such as the ones in the
  * {@link java.util.concurrent.locks} package.
  */
 public static native void yield();
  • join() 當一個執行緒必須等待另外一個執行緒執行完畢才能執行時
/**
  * Waits at most {@code millis} milliseconds for this thread to
  * die. A timeout of {@code 0} means to wait forever.
  *
  * <p> This implementation uses a loop of {@code this.wait} calls
  * conditioned on {@code this.isAlive}. As a thread terminates the
  * {@code this.notifyAll} method is invoked. It is recommended that
  * applications not use {@code wait}, {@code notify}, or
  * {@code notifyAll} on {@code Thread} instances.
  *
  * @param  millis
  *         the time to wait in milliseconds
  *
  * @throws  IllegalArgumentException
  *          if the value of {@code millis} is negative
  *
  * @throws  InterruptedException
  *          if any thread has interrupted the current thread. The
  *          <i>interrupted status</i> of the current thread is
  *          cleared when this exception is thrown.
  */
 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;
         }
     }
 }
  • sleep() 呼叫sleep方法之後執行緒進入阻塞狀態,不會釋放物件鎖
/**
  * Causes the currently executing thread to sleep (temporarily cease
  * execution) for the specified number of milliseconds, subject to
  * the precision and accuracy of system timers and schedulers. The thread
  * does not lose ownership of any monitors.
  *
  * @param  millis
  *         the length of time to sleep in milliseconds
  *
  * @throws  IllegalArgumentException
  *          if the value of {@code millis} is negative
  *
  * @throws  InterruptedException
  *          if any thread has interrupted the current thread. The
  *          <i>interrupted status</i> of the current thread is
  *          cleared when this exception is thrown.
  */
 public static native void sleep(long millis) throws InterruptedException;
  • interrupt()
/**
  * Interrupts this thread.
  *
  * <p> Unless the current thread is interrupting itself, which is
  * always permitted, the {@link #checkAccess() checkAccess} method
  * of this thread is invoked, which may cause a {@link
  * SecurityException} to be thrown.
  *
  * <p> If this thread is blocked in an invocation of the {@link
  * Object#wait() wait()}, {@link Object#wait(long) wait(long)}, or {@link
  * Object#wait(long, int) wait(long, int)} methods of the {@link Object}
  * class, or of the {@link #join()}, {@link #join(long)}, {@link
  * #join(long, int)}, {@link #sleep(long)}, or {@link #sleep(long, int)},
  * methods of this class, then its interrupt status will be cleared and it
  * will receive an {@link InterruptedException}.
  *
  * <p> If this thread is blocked in an I/O operation upon an {@link
  * java.nio.channels.InterruptibleChannel InterruptibleChannel}
  * then the channel will be closed, the thread's interrupt
  * status will be set, and the thread will receive a {@link
  * java.nio.channels.ClosedByInterruptException}.
  *
  * <p> If this thread is blocked in a {@link java.nio.channels.Selector}
  * then the thread's interrupt status will be set and it will return
  * immediately from the selection operation, possibly with a non-zero
  * value, just as if the selector's {@link
  * java.nio.channels.Selector#wakeup wakeup} method were invoked.
  *
  * <p> If none of the previous conditions hold then this thread's interrupt
  * status will be set. </p>
  *
  * <p> Interrupting a thread that is not alive need not have any effect.
  *
  * @throws  SecurityException
  *          if the current thread cannot modify this thread
  *
  * @revised 6.0
  * @spec JSR-51
  */
 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();
 }
  • destroy() 已過時
  • stop() 已過時
  • suspend() 已過時
  • resume() 已過時

執行緒狀態轉換

附:網上找的貼圖一張
執行緒狀態轉換

Java中用到的執行緒排程演算法:
搶佔式,一個執行緒用完CPU之後,作業系統會根據執行緒優先順序、執行緒飢餓情況等資料算出一個總優先順序並分配下一個時間片給某個執行緒執行。
Thread.sleep(0)的作用 ?
由於Java採用的是搶佔式排程演算法,因此可能會出現某條執行緒長春獲取到CPU控制權的情況。為了使某些優先順序比較低的執行緒也能獲取到CPU控制權,使用Thread.sleep(0)可以手動觸發一次作業系統分配時間片的操作,也是平衡CPU控制權的一種操作。
#

守護執行緒

  • thread.setDaemon(true)必須在thread.start()之前設定,否則會跑出一個IllegalThreadStateException異常。你不能把正在執行的常規執行緒設定為守護執行緒。
  • 在Daemon執行緒中產生的新執行緒也是Daemon的
  • 不要認為所有的應用都可以分配給守護執行緒來進行服務,比如讀寫操作或者計算邏輯,java的執行緒池會將守護執行緒轉換為使用者執行緒,所以如果要使用後臺執行緒就不能用java的執行緒池