多線程- 讓程序更高效的運行
阿新 • • 發佈:2017-10-12
creator currently 調用本地 *** hellip ads sleep addition orm
Java Thread 的一些認識:
- Java是搶占式線程,一個線程就是進程中單一的順序控制流,單個進程可以擁有多個並發任務,其底層是切分CPU時間,多線程和多任務往往是使用多處理器系統的最合理方式
- 進程可以看作一個程序或者一個應用;線程是進程中執行的一個任務,多個線程可以共享資源
- 一個Java 應用從main 方法開始運行,main 運行在一個線程內,也被稱為 “主線程”,Runnable也可以理解為Task (任務)
- JVM啟動後,會創建一些守護線程來進行自身的常規管理(垃圾回收,終結處理),以及一個運行main函數的主線程
- 隨著硬件水平的提高,多線程能使系統的運行效率得到大幅度的提高,同時異步操作也增加復雜度和各種並發問題
■ 線程 VS 進程
- 在一個已有進程中創建一個新線程比創建一個新進程快的多
- 終止一個線程比終止一個進程快的多
- 同一個進程內線程間切換比進程間切換更快
- 線程提供了不同的執行程序間通信的效率,同一個進程中的線程共享同一進程內存和文件,無序調用內核就可以互相通信,而進程間通信必須通過內核
■ 同步和異步
- 同步方法一旦開始,調用者必須等到方法調用返回之後,才能繼續後續行為
- 無先後順序,一旦開始,方法調用便立即返回,調用者就可以繼續後續行為,一般為另一個線程執行
■ 阻塞和非阻塞
- 當一個線程占用臨界區資源,其他線程也想要使用該資源就必須等待,等待會導致線程的掛起,也就是阻塞(線程變成阻塞狀態)。
- 非阻塞強調沒有一個線程可以妨礙其他線程執行,所有線程都會嘗試去做下一步工作
■ 臨界資源與臨界區
- 一般指的是公共共享資源,即可以被多個線程共享使用。但同一時間只能由一個線程去訪問和操作臨界區的資源,一旦臨界區資源被一個線程占用,其他線程也想要使用該資源就必須等待,
就好比好多人想上大號,但只有一個坑,一個人占了坑,其他人就得排隊等待嘍 - 臨界區可以認為是一段代碼,線程會在該端代碼中訪問共享資源,因此臨界區的界定標準就是是否訪問共享(臨界)資源(有點類似形成閉包的概念);一次只允許有一個程序(進程/線程)在該臨界區中
■ 類定義
public class Thread implements Runnable { /* Make sure registerNatives is the first thing <clinit> does.
初始化時調用 Java 本地方法,實現了Runnable接口
*/ private static native void registerNatives(); static { registerNatives(); }
■ 構造器
/** * 默認構造器 * 其中name規則為 "Thread-" + nextThreadNum() */ public Thread() { init(null, null, "Thread-" + nextThreadNum(), 0); } /** * 創建一個指定Runnable的線程 * 其中name規則為 "Thread-" + nextThreadNum() */ public Thread(Runnable target) { init(null, target, "Thread-" + nextThreadNum(), 0); } /** * 創建一個指定所屬線程組和Runnable的線程 * 其中name規則為 "Thread-" + nextThreadNum() */ public Thread(ThreadGroup group, Runnable target) { init(group, target, "Thread-" + nextThreadNum(), 0); } /** * 創建一個指定name線程 */ public Thread(String name) { init(null, null, name, 0); } /** * 創建一個指定所屬線程組和name的線程 */ public Thread(ThreadGroup group, String name) { init(group, null, name, 0); } /** * 創建一個指定Runnable和name的線程 */ public Thread(Runnable target, String name) { init(null, target, name, 0); } /** * Allocates a new {@code Thread} object so that it has {@code target} * as its run object, has the specified {@code name} as its name, * and belongs to the thread group referred to by {@code group}. * 創建一個新的Thread對象,同時滿足以下條件: * 1.該線程擁有一個指定的Runnable對象用於方法執行 * 2.該線程具有一個指定的名稱 * 3.該線程屬於一個指定的線程組ThreadGroup * <p>If there is a security manager, its * {@link SecurityManager#checkAccess(ThreadGroup) checkAccess} * method is invoked with the ThreadGroup as its argument. * 若這裏有個安全管理器,則ThreadGroup將調用checkAccess方法進而觸發SecurityManager的checkAccess方法 * <p>In addition, its {@code checkPermission} method is invoked with * the {@code RuntimePermission("enableContextClassLoaderOverride")} * permission when invoked directly or indirectly by the constructor of a subclass which * overrides the {@code getContextClassLoader} or {@code setContextClassLoader} methods. * 當enableContextClassLoaderOverride被開啟時,checkPermission將被重寫子類直接或間接地調用 * <p>The priority of the newly created thread is set equal to the * priority of the thread creating it, that is, the currently running * thread. The method {@linkplain #setPriority setPriority} may be * used to change the priority to a new value. * 新創建的Thread的優先級等同於創建它的線程的優先級,調用setPriority會變更其優先級 * <p>The newly created thread is initially marked as being a daemon * thread if and only if the thread creating it is currently marked * as a daemon thread. The method {@linkplain #setDaemon setDaemon} * may be used to change whether or not a thread is a daemon. * 當且僅當線程創建時被顯示地標記為守護線程,新創建的線程才會被初始化為一個守護線程 * setDaemon方法可以設置當前線程是否為守護線程 */ public Thread(ThreadGroup group, Runnable target, String name) { init(group, target, name, 0); } /** * Allocates a new {@code Thread} object so that it has {@code target} as its run object, * has the specified {@code name} as its name, and belongs to the thread group referred to * by {@code group}, and has the specified <i>stack size</i>. * 創建一個新的Thread對象,同時滿足以下條件: * 1.該線程擁有一個指定的Runnable對象用於方法執行 * 2.該線程具有一個指定的名稱 * 3.該線程屬於一個指定的線程組ThreadGroup * 4.該線程擁有一個指定的棧容量 * <p>This constructor is identical to {@link #Thread(ThreadGroup,Runnable,String)} * with the exception of the fact that it allows the thread stack size to be specified. * The stack size is the approximate number of bytes of address space that the virtual machine * is to allocate for this thread‘s stack. <b>The effect of the {@code stackSize} parameter, * if any, is highly platform dependent.</b> * 棧容量指的是JVM分配給該線程的棧的地址(內存)空間大小,這個參數的效果高度依賴於JVM運行平臺 * <p>On some platforms, specifying a higher value for the {@code stackSize} parameter may allow * a thread to achieve greater recursion depth before throwing a {@link StackOverflowError}. * Similarly, specifying a lower value may allow a greater number of threads to exist * concurrently without throwing an {@link OutOfMemoryError} (or other internal error). * The details of the relationship between the value of the <tt>stackSize</tt> parameter * and the maximum recursion depth and concurrency level are platform-dependent. * <b>On some platforms, the value of the {@code stackSize} parameter * may have no effect whatsoever.</b> * 在一些平臺上,棧容量越高,(會在棧溢出之前)允許線程完成更深的遞歸(換句話說就是棧空間更深) * 同理,若棧容量越小,(在拋出內存溢出之前)允許同時存在更多的線程數 * 對於棧容量、最大遞歸深度和並發水平之間的關系依賴於平臺 * <p>The virtual machine is free to treat the {@code stackSize} parameter as a suggestion. * If the specified value is unreasonably low for the platform,the virtual machine may instead * use some platform-specific minimum value; if the specified value is unreasonably high, * the virtual machine may instead use some platform-specific maximum. * Likewise, the virtual machine is free to round the specified value up or down as it sees fit * (or to ignore it completely). * JVM會將指定的棧容量作為一個參考依據,但當小於平臺最小值時會直接使用最小值,最大值同理 * 同樣,JVM會動態調整棧空間的大小以適應程序的運行或者甚至直接就忽視該值的設置 * <p><i>Due to the platform-dependent nature of the behavior of this constructor, extreme care * should be exercised in its use.The thread stack size necessary to perform a given computation * will likely vary from one JRE implementation to another. In light of this variation, * careful tuning of the stack size parameter may be required,and the tuning may need to * be repeated for each JRE implementation on which an application is to run.</i> * 簡單總結一下就是:這個值嚴重依賴平臺,所以要謹慎使用,多做測試驗證 * @param group * the thread group. If {@code null} and there is a security * manager, the group is determined by {@linkplain * SecurityManager#getThreadGroup SecurityManager.getThreadGroup()}. * If there is not a security manager or {@code * SecurityManager.getThreadGroup()} returns {@code null}, the group * is set to the current thread‘s thread group. * 當線程組為null同時有個安全管理器,該線程組由SecurityManager.getThreadGroup()決定 * 當沒有安全管理器或getThreadGroup為空,該線程組即為創建該線程的線程所屬的線程組 * @param target * the object whose {@code run} method is invoked when this thread * is started. If {@code null}, this thread‘s run method is invoked. * 若該值為null,將直接調用該線程的run方法(等同於一個空方法) * @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. * 當棧容量被設置為0時,JVM就會忽略該值的設置 * @throws SecurityException * if the current thread cannot create a thread in the specified thread group * 如果當前線程在一個指定的線程組中不能創建一個新的線程時將拋出 安全異常 * @since 1.4 */ public Thread(ThreadGroup group, Runnable target, String name,long stackSize) { init(group, target, name, stackSize); }
■ 重要變量
//線程名,用char來保存(String底層實現就是char) private char name[]; //線程優先級 private int priority; //不明覺厲 private Thread threadQ; //不明覺厲 private long eetop; /* 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; /* JVM state. 是否一出生就領便當,默認false*/ private boolean stillborn = false; /* What will be run. Thread的run方法最終會調用target的run方法*/ private Runnable target; /* The group of this thread. 當前線程的所屬線程組*/ private ThreadGroup group; /* The context ClassLoader for this thread 當前線程的ClassLoader*/ private ClassLoader contextClassLoader; /* The inherited AccessControlContext of this thread 當前線程繼承的AccessControlContext*/ 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會被ThreadLocal維護(ThreadLocal會專門分析) */ ThreadLocal.ThreadLocalMap threadLocals = null; /* * InheritableThreadLocal values pertaining to this thread. This map is * maintained by the InheritableThreadLocal class. * 主要作用:為子線程提供從父線程那裏繼承的值 * 在創建子線程時,子線程會接收所有可繼承的線程局部變量的初始值,以獲得父線程所具有的值 * 創建一個線程時如果保存了所有 InheritableThreadLocal 對象的值,那麽這些值也將自動傳遞給子線程 * 如果一個子線程調用 InheritableThreadLocal 的 get() ,那麽它將與它的父線程看到同一個對象 */ 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. * 棧容量:當設置為0時,JVM會忽略該值;該值嚴重依賴於JVM平臺,有些VM甚至會直接忽視該值 * 該值越大,線程棧空間變大,允許的並發線程數就越少;該值越小,線程棧空間變小,允許的並發線程數就越多 */ private long stackSize; /* JVM-private state that persists after native thread termination.*/ private long nativeParkEventPointer; /* Thread ID. 每個線程都有專屬ID,但名字可能重復*/ private long tid; /* For generating thread ID 用於ID生成,每次+1*/ private static long threadSeqNumber; /* * Java thread status for tools,initialized to indicate thread ‘not yet started‘ * 線程狀態 0僅表示已創建 */ private volatile int threadStatus = 0; /** * 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 * 主要是提供給 java.util.concurrent.locks.LockSupport該類使用 */ volatile Object parkBlocker; /* 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. * 中斷阻塞器:當線程發生IO中斷時,需要在線程被設置為中斷狀態後調用該對象的interrupt方法 */ private volatile Interruptible blocker; //阻塞器鎖,主要用於處理阻塞情況 private final Object blockerLock = new Object(); /* 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; /* For generating thread ID 最大優先級*/ public final static int MAX_PRIORITY = 10; /* 用於存儲堆棧信息 默認是個空的數組*/ private static final StackTraceElement[] EMPTY_STACK_TRACE = new StackTraceElement[0]; private static final RuntimePermission SUBCLASS_IMPLEMENTATION_PERMISSION = new RuntimePermission("enableContextClassLoaderOverride"); // null unless explicitly set 線程異常處理器,只對當前線程有效 private volatile UncaughtExceptionHandler uncaughtExceptionHandler; // null unless explicitly set 默認線程異常處理器,對所有線程有效 private static volatile UncaughtExceptionHandler defaultUncaughtExceptionHandler;
■ 本地方法
/* * Make sure registerNatives is the first thing <clinit> does. * 確保clinit最先調用該方法:所有該方法是類中的最靠前的一個靜態方法 * clinit:在JVM第一次加載class文件時調用,用於靜態變量初始化語句和靜態塊的執行 * 所有的類變量初始化語句和類型的靜態初始化語句都被Java編譯器收集到該方法中 * * registerNatives方法被native修飾,即是本地方法,將由C/C++去完成,並被編譯成了.dll,供JAVA調用 * 其主要作用是將C/C++中的方法映射到Java中的native方法,實現方法命名的解耦 */ private static native void registerNatives(); static { registerNatives(); } /** 主動讓出CPU資源,當時可能又立即搶到資源 **/ public static native void yield(); /** 休眠一段時間,讓出資源但是並不會釋放對象鎖 **/ public static native void sleep(long millis) throws InterruptedException; /** 檢查 線程是否存活 **/ public final native boolean isAlive(); /** 檢查線程是否中斷 isInterrupted() 內部使用 **/ private native boolean isInterrupted(boolean ClearInterrupted); /** 返回當前執行線程 **/ public static native Thread currentThread(); public static native boolean holdsLock(Object obj); private native void start0(); private native void setPriority0(int newPriority); private native void stop0(Object o); private native void suspend0(); private native void resume0(); private native void interrupt0(); private native void setNativeName(String name);
■ 線程初始化
/** * 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. */ private void init(ThreadGroup g, Runnable target, String name,long stackSize) { if (name == null) { throw new NullPointerException("name cannot be null"); } //返回當前線程,即創建該hread的線程 currentThread是個本地方法 Thread parent = currentThread(); //安全管理器根據Java安全策略文件決定將哪組權限授予類 //如果想讓應用使用安全管理器和安全策略,可在啟動JVM時設定-Djava.security.manager選項 //還可以同時指定安全策略文件 //如果在應用中啟用了Java安全管理器,卻沒有指定安全策略文件,那麽Java安全管理器將使用默認的安全策略 //它們是由位於目錄$JAVA_HOME/jre/lib/security中的java.policy定義的 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(); if (security != null) { if (isCCLOverridden(getClass())) { security.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION); } } //新建線程數量計數+1 或者說未就緒線程數+1==> nUnstartedThreads++; g.addUnstarted(); this.group = g; this.daemon = parent.isDaemon();//若當前運行線程是守護線程,新建線程也是守護線程 this.priority = parent.getPriority();//默認使用當前運行線程的優先級 this.name = name.toCharArray(); //設置contextClassLoader if (security == null || isCCLOverridden(parent.getClass())) this.contextClassLoader = parent.getContextClassLoader(); else this.contextClassLoader = parent.contextClassLoader; this.inheritedAccessControlContext = AccessController.getContext(); this.target = target; setPriority(priority);//若有指定的優先級,使用指定的優先級 if (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 線程安全,序列號每次同步+1*/ tid = nextThreadID(); } private static synchronized long nextThreadID() { return ++threadSeqNumber; }
■ start 方法
/** * Causes this thread to begin execution; the Java Virtual Machine * calls the <code>run</code> method of this thread. * 線程進入就緒態,隨後JVM將會調用這個線程run方法 * 當獲取到CPU時間片時,會立即執行run方法,此時線程會直接變成運行態 * <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. * 一個線程只能被start一次,特別是線程不會在執行完畢後重新start * 當線程已經start了,再次執行會拋出IllegalThreadStateException異常 * @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. * 該方法不會被主線程或系統線程組調用,若未來有新增功能,也會被添加到VM中 * A zero status value corresponds to state "NEW". * 0對應"已創建"狀態 -> 用常量或枚舉標識多好 */ 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. */ //通知所屬線程組該線程已經是就緒狀態,因而可以被添加到該線程組中 //同時線程組的未就緒線程數需要-1,對應init中的+1 group.add(this); boolean started = false; try { //調用本地方法,將內存中的線程狀態變更為就緒態 //同時JVM會立即調用run方法,獲取到CPU之後,線程變成運行態並立即執行run方法 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出錯,會被調用棧直接通過 } } } ------------- //start之後會立即調用run方法 Thread t = new Thread( new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getName()); } },"roman"); t.start(); //roman
■ run 方法
/** * 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. * Subclasses of <code>Thread</code> should override this method. * 若Thread初始化時有指定Runnable就執行其的run方法,否則doNothing * 該方法必須被子類實現 */ @Override public void run() { if (target != null) { target.run(); } }
■ isAlive 方法
/** * Tests if this thread is alive. A thread is alive if it has * been started and has not yet died. * 測試線程是否處於活動狀態 * 活動狀態:線程處於正在運行或者準備開始運行狀態 * @return <code>true</code> if this thread is alive; * <code>false</code> otherwise. */ public final native boolean isAlive();
? 線程運行 : 模擬電梯運行類
public class LiftOff implements Runnable { private int countDown = 10; //電梯階層 // private static int taskCount = 0; // private final int id = taskCount++; public LiftOff(){ }
// syn countDown private synchronized int getCountDown(){ --countDown; return countDown; } // 獲取電梯狀態 public String status() { return Thread.currentThread().toString()+ "("+ (countDown > 0? countDown: "Liftoff!") + "),"; } @Override public void run(){ while (getCountDown() >0){ System.out.println(status()); Thread.yield(); } } public static void main(String[] args) { // thread‘s start() Thread thread = new Thread(new LiftOff()); thread.start(); // 調用 run() System.out.println("================Waiting for LiftOff...==========================="); } }
- 線程都會有自己的名字
- 獲取線程對象的方法: Thread.currentThread()
- 目標 run() 結束後線程完成
- JVM線程調度程序決定實際運行哪個處於可運行狀態的線程
- 使用線程池執行處理任務
線程狀態流程圖:
■ sleep 方法
/** * Causes the currently executing thread to sleep (temporarily cease execution) * for the specified number of milliseconds plus the specified number of nanoseconds, * subject to the precision and accuracy of system timers and schedulers. * The thread does not lose ownership of any monitors. * 使線程睡眠一段毫秒時間,但線程並不會丟失已有的任何監視器 */ 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); } /** 我們一般會直接調用native方法,這或許是我們主動使用的最多次的native方法了 **/ public static native void sleep(long millis) throws InterruptedException;
■ yield 方法
/** * 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. * 暗示線程調度器當前線程將釋放自己當前占用的CPU資源 * 線程調度器會自由選擇是否忽視此暗示 * <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. * 該方法會放棄當前的CPU資源,將它讓給其他的任務去占用CPU執行時間 * 但放棄的時間不確定,可能剛剛放棄又獲得CPU時間片 * <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. * 該方法的適合使用場景比較少,主要用於Debug,比如Lock包設計 */ public static native void yield();
■ 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. * 如果當前線程不是被自己中斷,可能會拋出SecurityException異常 * <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}. * 若當前線程已被Object.wait()方法、Thread的join()或sleep方法阻塞, * 則調用該中斷方法會拋出InterruptedException同時中斷狀態會被清除 * <p> If this thread is blocked in an I/O operation upon an {@link * java.nio.channels.InterruptibleChannel </code>interruptible channel<code>} * 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}. * 若當前線程在InterruptibleChannel上發生IO阻塞,該通道要被關閉並將線程狀態設置為中斷同時拋出 * 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. * 若該線程被選擇器阻塞,將線程狀態設置為中斷同時從選取方法中立即返回 * 該選取方法通常會返回一個非0值,當wakeup方法正好被調用時 * <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) { // Just to set the interrupt flag // 調用interrupt方法僅僅是在當前線程中打了一個停止的標記,並不是真的停止線程! interrupt0(); b.interrupt(this); return; } } interrupt0(); }
■ Daemon
- 分類:在JAVA中分成兩種線程:用戶線程和守護線程
- 特性:當進程中不存在非守護線程時,則全部的守護線程會自動化銷毀
- 應用: JVM在啟動後會生成一系列守護線程,最有名的當屬GC(垃圾回收器)
1 Thread t2 = new Thread(new Runnable() { 2 @Override 3 public void run() { 4 System.out.println("守護線程運行了"); 5 for (int i = 0; i < 500000;i++){ 6 System.out.println("守護線程計數:" + i); 7 } 8 } 9 }, "kira"); 10 t2.setDaemon(true); 11 t2.start(); 12 Thread.sleep(500); 13 ------------- 14 //輸出: 15 ...... 16 守護線程計數:113755 17 守護線程計數:113756 18 守護線程計數:113757 19 守護線程計數:113758 20 //結束打印:會發現守護線程並沒有打印500000次,因為主線程已經結束運行了
■ wait 和 notify 機制
wait()使線程停止運行,notify()使停止的線程繼續運行
- 使用wait()、notify()、notifyAll()需要先對調用對象加鎖,即只能在同步方法或同步塊中調用這些方法
- 調用wait()方法後,線程狀態由RUNNING變成WAITING,並將當前線程放入對象的等待隊列中
- 調用notify()或notifyAll()方法之後,等待線程不會從wait()返回,需要notify()方法所在同步塊代碼執行完畢而釋放鎖之後,等待線程才可以獲取到該對象鎖並從wait()返回
- notify()方法將隨機選擇一個等待線程從等待隊列中移到同步隊列中;notifyAll()方法會將等待隊列中的所有等待線線程全部移到同步隊列中,被移動線程狀態由WAITING變成BLOCKED
*****各位看客,由於對線程的調度機制還理解比較淺,所以本文會持續更新…… ********
***特別感謝我的好友kira 對本人的友情源碼幫助,謝謝kira 好基友
多線程- 讓程序更高效的運行