淺談多線程_讓程序更高效的運(yùn)行
Java Thread 的一些認(rèn)識:
Java是搶占式線程,一個(gè)線程就是進(jìn)程中單一的順序控制流,單個(gè)進(jìn)程可以擁有多個(gè)并發(fā)任務(wù),其底層是切分CPU時(shí)間,多線程和多任務(wù)往往是使用多處理器系統(tǒng)的最合理方式
進(jìn)程可以看作一個(gè)程序或者一個(gè)應(yīng)用;線程是進(jìn)程中執(zhí)行的一個(gè)任務(wù),多個(gè)線程可以共享資源
一個(gè)Java 應(yīng)用從main 方法開始運(yùn)行,main 運(yùn)行在一個(gè)線程內(nèi),也被稱為 “主線程”,Runnable也可以理解為Task (任務(wù))
JVM啟動(dòng)后,會(huì)創(chuàng)建一些守護(hù)線程來進(jìn)行自身的常規(guī)管理(垃圾回收,終結(jié)處理),以及一個(gè)運(yùn)行main函數(shù)的主線程
隨著硬件水平的提高,多線程能使系統(tǒng)的運(yùn)行效率得到大幅度的提高,同時(shí)異步操作也增加復(fù)雜度和各種并發(fā)問題

■ 線程 VS 進(jìn)程
在一個(gè)已有進(jìn)程中創(chuàng)建一個(gè)新線程比創(chuàng)建一個(gè)新進(jìn)程快的多
終止一個(gè)線程比終止一個(gè)進(jìn)程快的多
同一個(gè)進(jìn)程內(nèi)線程間切換比進(jìn)程間切換更快
線程提供了不同的執(zhí)行程序間通信的效率,同一個(gè)進(jìn)程中的線程共享同一進(jìn)程內(nèi)存和文件,無序調(diào)用內(nèi)核就可以互相通信,而進(jìn)程間通信必須通過內(nèi)核
■ 同步和異步
同步方法一旦開始,調(diào)用者必須等到方法調(diào)用返回之后,才能繼續(xù)后續(xù)行為
無先后順序,一旦開始,方法調(diào)用便立即返回,調(diào)用者就可以繼續(xù)后續(xù)行為,一般為另一個(gè)線程執(zhí)行
■ 阻塞和非阻塞
當(dāng)一個(gè)線程占用臨界區(qū)資源,其他線程也想要使用該資源就必須等待,等待會(huì)導(dǎo)致線程的掛起,也就是阻塞(線程變成阻塞狀態(tài))。
此時(shí)若占用資源的線程一直不愿意釋放資源,那么其他所有阻塞在該臨界區(qū)的線程都會(huì)被掛起,變成阻塞狀態(tài),不能正常工作,直到占用線程釋放資源
非阻塞強(qiáng)調(diào)沒有一個(gè)線程可以妨礙其他線程執(zhí)行,所有線程都會(huì)嘗試去做下一步工作
■ 臨界資源與臨界區(qū)
一般指的是公共共享資源,即可以被多個(gè)線程共享使用。但同一時(shí)間只能由一個(gè)線程去訪問和操作臨界區(qū)的資源,一旦臨界區(qū)資源被一個(gè)線程占用,其他線程也想要使用該資源就必須等待,
就好比好多人想上大號,但只有一個(gè)坑,一個(gè)人占了坑,其他人就得排隊(duì)等待嘍
臨界區(qū)可以認(rèn)為是一段代碼,線程會(huì)在該端代碼中訪問共享資源,因此臨界區(qū)的界定標(biāo)準(zhǔn)就是是否訪問共享(臨界)資源(有點(diǎn)類似形成閉包的概念);一次只允許有一個(gè)程序(進(jìn)程/線程)在該臨界區(qū)中
■ 類定義
public class Thread implements Runnable {
/* Make sure registerNatives is the first thing <clinit> does.
初始化時(shí)調(diào)用 Java 本地方法,實(shí)現(xiàn)了Runnable接口
*/
private static native void registerNatives();
static {
registerNatives();
}
■ 構(gòu)造器
/**
* 默認(rèn)構(gòu)造器
* 其中name規(guī)則為 "Thread-" + nextThreadNum()
*/
public Thread() {
init(null, null, "Thread-" + nextThreadNum(), 0);
}
/**
* 創(chuàng)建一個(gè)指定Runnable的線程
* 其中name規(guī)則為 "Thread-" + nextThreadNum()
*/
public Thread(Runnable target) {
init(null, target, "Thread-" + nextThreadNum(), 0);
}
/**
* 創(chuàng)建一個(gè)指定所屬線程組和Runnable的線程
* 其中name規(guī)則為 "Thread-" + nextThreadNum()
*/
public Thread(ThreadGroup group, Runnable target) {
init(group, target, "Thread-" + nextThreadNum(), 0);
}
/**
* 創(chuàng)建一個(gè)指定name線程
*/
public Thread(String name) {
init(null, null, name, 0);
}
/**
* 創(chuàng)建一個(gè)指定所屬線程組和name的線程
*/
public Thread(ThreadGroup group, String name) {
init(group, null, name, 0);
}
/**
* 創(chuàng)建一個(gè)指定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}.
* 創(chuàng)建一個(gè)新的Thread對象,同時(shí)滿足以下條件:
* 1.該線程擁有一個(gè)指定的Runnable對象用于方法執(zhí)行
* 2.該線程具有一個(gè)指定的名稱
* 3.該線程屬于一個(gè)指定的線程組ThreadGroup
* <p>If there is a security manager, its
* {@link SecurityManager#checkAccess(ThreadGroup) checkAccess}
* method is invoked with the ThreadGroup as its argument.
* 若這里有個(gè)安全管理器,則ThreadGroup將調(diào)用checkAccess方法進(jìn)而觸發(fā)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.
* 當(dāng)enableContextClassLoaderOverride被開啟時(shí),checkPermission將被重寫子類直接或間接地調(diào)用
* <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.
* 新創(chuàng)建的Thread的優(yōu)先級等同于創(chuàng)建它的線程的優(yōu)先級,調(diào)用setPriority會(huì)變更其優(yōu)先級
* <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.
* 當(dāng)且僅當(dāng)線程創(chuàng)建時(shí)被顯示地標(biāo)記為守護(hù)線程,新創(chuàng)建的線程才會(huì)被初始化為一個(gè)守護(hù)線程
* setDaemon方法可以設(shè)置當(dāng)前線程是否為守護(hù)線程
*/
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>.
* 創(chuàng)建一個(gè)新的Thread對象,同時(shí)滿足以下條件:
* 1.該線程擁有一個(gè)指定的Runnable對象用于方法執(zhí)行
* 2.該線程具有一個(gè)指定的名稱
* 3.該線程屬于一個(gè)指定的線程組ThreadGroup
* 4.該線程擁有一個(gè)指定的棧容量
* <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分配給該線程的棧的地址(內(nèi)存)空間大小,這個(gè)參數(shù)的效果高度依賴于JVM運(yùn)行平臺
* <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>
* 在一些平臺上,棧容量越高,(會(huì)在棧溢出之前)允許線程完成更深的遞歸(換句話說就是??臻g更深)
* 同理,若棧容量越小,(在拋出內(nèi)存溢出之前)允許同時(shí)存在更多的線程數(shù)
* 對于棧容量、最大遞歸深度和并發(fā)水平之間的關(guān)系依賴于平臺
* <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會(huì)將指定的棧容量作為一個(gè)參考依據(jù),但當(dāng)小于平臺最小值時(shí)會(huì)直接使用最小值,最大值同理
* 同樣,JVM會(huì)動(dòng)態(tài)調(diào)整??臻g的大小以適應(yīng)程序的運(yùn)行或者甚至直接就忽視該值的設(shè)置
* <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>
* 簡單總結(jié)一下就是:這個(gè)值嚴(yán)重依賴平臺,所以要謹(jǐn)慎使用,多做測試驗(yàn)證
* @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.
* 當(dāng)線程組為null同時(shí)有個(gè)安全管理器,該線程組由SecurityManager.getThreadGroup()決定
* 當(dāng)沒有安全管理器或getThreadGroup為空,該線程組即為創(chuàng)建該線程的線程所屬的線程組
* @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,將直接調(diào)用該線程的run方法(等同于一個(gè)空方法)
* @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.
* 當(dāng)棧容量被設(shè)置為0時(shí),JVM就會(huì)忽略該值的設(shè)置
* @throws SecurityException
* if the current thread cannot create a thread in the specified thread group
* 如果當(dāng)前線程在一個(gè)指定的線程組中不能創(chuàng)建一個(gè)新的線程時(shí)將拋出 安全異常
* @since 1.4
*/
public Thread(ThreadGroup group, Runnable target, String name,long stackSize) {
init(group, target, name, stackSize);
}
■ 重要變量
//線程名,用char來保存(String底層實(shí)現(xiàn)就是char)
private char name[];
//線程優(yōu)先級
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. 是否是守護(hù)線程,默認(rèn)非守護(hù)線程*/
private boolean daemon = false;
/* JVM state. 是否一出生就領(lǐng)便當(dāng),默認(rèn)false*/
private boolean stillborn = false;
/* What will be run. Thread的run方法最終會(huì)調(diào)用target的run方法*/
private Runnable target;
/* The group of this thread. 當(dāng)前線程的所屬線程組*/
private ThreadGroup group;
/* The context ClassLoader for this thread 當(dāng)前線程的ClassLoader*/
private ClassLoader contextClassLoader;
/* The inherited AccessControlContext of this thread 當(dāng)前線程繼承的AccessControlContext*/
private AccessControlContext inheritedAccessControlContext;
/* For autonumbering anonymous threads. 給匿名線程自動(dòng)編號,并按編號起名字*/
private static int threadInitNumber;
/* ThreadLocal values pertaining to this thread. This map is maintained by the ThreadLocal class.
* 當(dāng)前線程附屬的ThreadLocal,而ThreadLocalMap會(huì)被ThreadLocal維護(hù)(ThreadLocal會(huì)專門分析)
*/
ThreadLocal.ThreadLocalMap threadLocals = null;
/*
* InheritableThreadLocal values pertaining to this thread. This map is
* maintained by the InheritableThreadLocal class.
* 主要作用:為子線程提供從父線程那里繼承的值
* 在創(chuàng)建子線程時(shí),子線程會(huì)接收所有可繼承的線程局部變量的初始值,以獲得父線程所具有的值
* 創(chuàng)建一個(gè)線程時(shí)如果保存了所有 InheritableThreadLocal 對象的值,那么這些值也將自動(dòng)傳遞給子線程
* 如果一個(gè)子線程調(diào)用 InheritableThreadLocal 的 get() ,那么它將與它的父線程看到同一個(gè)對象
*/
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.
* 棧容量:當(dāng)設(shè)置為0時(shí),JVM會(huì)忽略該值;該值嚴(yán)重依賴于JVM平臺,有些VM甚至?xí)苯雍鲆曉撝?
* 該值越大,線程??臻g變大,允許的并發(fā)線程數(shù)就越少;該值越小,線程??臻g變小,允許的并發(fā)線程數(shù)就越多
*/
private long stackSize;
/* JVM-private state that persists after native thread termination.*/
private long nativeParkEventPointer;
/* Thread ID. 每個(gè)線程都有專屬ID,但名字可能重復(fù)*/
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'
* 線程狀態(tài) 0僅表示已創(chuàng)建
*/
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.
* 中斷阻塞器:當(dāng)線程發(fā)生IO中斷時(shí),需要在線程被設(shè)置為中斷狀態(tài)后調(diào)用該對象的interrupt方法
*/
private volatile Interruptible blocker;
//阻塞器鎖,主要用于處理阻塞情況
private final Object blockerLock = new Object();
/* The minimum priority that a thread can have. 最小優(yōu)先級*/
public final static int MIN_PRIORITY = 1;
/* The default priority that is assigned to a thread. 默認(rèn)優(yōu)先級*/
public final static int NORM_PRIORITY = 5;
/* For generating thread ID 最大優(yōu)先級*/
public final static int MAX_PRIORITY = 10;
/* 用于存儲堆棧信息 默認(rèn)是個(gè)空的數(shù)組*/
private static final StackTraceElement[] EMPTY_STACK_TRACE = new StackTraceElement[0];
private static final RuntimePermission SUBCLASS_IMPLEMENTATION_PERMISSION =
new RuntimePermission("enableContextClassLoaderOverride");
// null unless explicitly set 線程異常處理器,只對當(dāng)前線程有效
private volatile UncaughtExceptionHandler uncaughtExceptionHandler;
// null unless explicitly set 默認(rèn)線程異常處理器,對所有線程有效
private static volatile UncaughtExceptionHandler defaultUncaughtExceptionHandler;
■ 本地方法
/*
* Make sure registerNatives is the first thing <clinit> does.
* 確保clinit最先調(diào)用該方法:所有該方法是類中的最靠前的一個(gè)靜態(tài)方法
* clinit:在JVM第一次加載class文件時(shí)調(diào)用,用于靜態(tài)變量初始化語句和靜態(tài)塊的執(zhí)行
* 所有的類變量初始化語句和類型的靜態(tài)初始化語句都被Java編譯器收集到該方法中
*
* registerNatives方法被native修飾,即是本地方法,將由C/C++去完成,并被編譯成了.dll,供JAVA調(diào)用
* 其主要作用是將C/C++中的方法映射到Java中的native方法,實(shí)現(xiàn)方法命名的解耦
*/
private static native void registerNatives();
static {
registerNatives();
}
/** 主動(dòng)讓出CPU資源,當(dāng)時(shí)可能又立即搶到資源 **/
public static native void yield();
/** 休眠一段時(shí)間,讓出資源但是并不會(huì)釋放對象鎖 **/
public static native void sleep(long millis) throws InterruptedException;
/** 檢查 線程是否存活 **/
public final native boolean isAlive();
/** 檢查線程是否中斷 isInterrupted() 內(nèi)部使用 **/
private native boolean isInterrupted(boolean ClearInterrupted);
/** 返回當(dāng)前執(zhí)行線程 **/
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.
* 初始化一個(gè)線程
* @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");
}
//返回當(dāng)前線程,即創(chuàng)建該hread的線程 currentThread是個(gè)本地方法
Thread parent = currentThread();
//安全管理器根據(jù)Java安全策略文件決定將哪組權(quán)限授予類
//如果想讓應(yīng)用使用安全管理器和安全策略,可在啟動(dòng)JVM時(shí)設(shè)定-Djava.security.manager選項(xiàng)
//還可以同時(shí)指定安全策略文件
//如果在應(yīng)用中啟用了Java安全管理器,卻沒有指定安全策略文件,那么Java安全管理器將使用默認(rèn)的安全策略
//它們是由位于目錄$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. */
//判斷當(dāng)前運(yùn)行線程是否有變更其線程組的權(quán)限
g.checkAccess();
if (security != null) {
if (isCCLOverridden(getClass())) {
security.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
}
}
//新建線程數(shù)量計(jì)數(shù)+1 或者說未就緒線程數(shù)+1==> nUnstartedThreads++;
g.addUnstarted();
this.group = g;
this.daemon = parent.isDaemon();//若當(dāng)前運(yùn)行線程是守護(hù)線程,新建線程也是守護(hù)線程
this.priority = parent.getPriority();//默認(rèn)使用當(dāng)前運(yùn)行線程的優(yōu)先級
this.name = name.toCharArray();
//設(shè)置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);//若有指定的優(yōu)先級,使用指定的優(yōu)先級
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.
* 線程進(jìn)入就緒態(tài),隨后JVM將會(huì)調(diào)用這個(gè)線程run方法
* 當(dāng)獲取到CPU時(shí)間片時(shí),會(huì)立即執(zhí)行run方法,此時(shí)線程會(huì)直接變成運(yùn)行態(tài)
* <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.
* 一個(gè)線程只能被start一次,特別是線程不會(huì)在執(zhí)行完畢后重新start
* 當(dāng)線程已經(jīng)start了,再次執(zhí)行會(huì)拋出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.
* 該方法不會(huì)被主線程或系統(tǒng)線程組調(diào)用,若未來有新增功能,也會(huì)被添加到VM中
* A zero status value corresponds to state "NEW".
* 0對應(yīng)"已創(chuàng)建"狀態(tài) -> 用常量或枚舉標(biāo)識多好
*/
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. */
//通知所屬線程組該線程已經(jīng)是就緒狀態(tài),因而可以被添加到該線程組中
//同時(shí)線程組的未就緒線程數(shù)需要-1,對應(yīng)init中的+1
group.add(this);
boolean started = false;
try {
//調(diào)用本地方法,將內(nèi)存中的線程狀態(tài)變更為就緒態(tài)
//同時(shí)JVM會(huì)立即調(diào)用run方法,獲取到CPU之后,線程變成運(yùn)行態(tài)并立即執(zhí)行run方法
start0();
started = true;//標(biāo)記為已開啟
} finally {
try {
if (!started) {
group.threadStartFailed(this);//如果變更失敗,要回滾線程和線程組狀態(tài)
}
} catch (Throwable ignore) {
/* do nothing. If start0 threw a Throwable then
it will be passed up the call stack */
//如果start0出錯(cuò),會(huì)被調(diào)用棧直接通過
}
}
}
-------------
//start之后會(huì)立即調(diào)用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初始化時(shí)有指定Runnable就執(zhí)行其的run方法,否則doNothing
* 該方法必須被子類實(shí)現(xiàn)
*/
@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. * 測試線程是否處于活動(dòng)狀態(tài) * 活動(dòng)狀態(tài):線程處于正在運(yùn)行或者準(zhǔn)備開始運(yùn)行狀態(tài) * @return <code>true</code> if this thread is alive; * <code>false</code> otherwise. */ public final native boolean isAlive();
✺ 線程運(yùn)行 : 模擬電梯運(yùn)行類
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;
}
// 獲取電梯狀態(tài)
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(); // 調(diào)用 run()
System.out.println("================Waiting for LiftOff...===========================");
}
}
線程都會(huì)有自己的名字
獲取線程對象的方法: Thread.currentThread()
目標(biāo) run() 結(jié)束后線程完成
JVM線程調(diào)度程序決定實(shí)際運(yùn)行哪個(gè)處于可運(yùn)行狀態(tài)的線程
使用線程池執(zhí)行處理任務(wù)
線程狀態(tài)流程圖:

■ 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.
* 使線程睡眠一段毫秒時(shí)間,但線程并不會(huì)丟失已有的任何監(jiān)視器
*/
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);
}
/** 我們一般會(huì)直接調(diào)用native方法,這或許是我們主動(dòng)使用的最多次的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.
* 暗示線程調(diào)度器當(dāng)前線程將釋放自己當(dāng)前占用的CPU資源
* 線程調(diào)度器會(huì)自由選擇是否忽視此暗示
* <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.
* 該方法會(huì)放棄當(dāng)前的CPU資源,將它讓給其他的任務(wù)去占用CPU執(zhí)行時(shí)間
* 但放棄的時(shí)間不確定,可能剛剛放棄又獲得CPU時(shí)間片
* <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包設(shè)計(jì)
*/
public static native void yield();
■ interrupt 方法
/**
* Interrupts this thread.
* 中斷一個(gè)線程
* <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.
* 如果當(dāng)前線程不是被自己中斷,可能會(huì)拋出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}.
* 若當(dāng)前線程已被Object.wait()方法、Thread的join()或sleep方法阻塞,
* 則調(diào)用該中斷方法會(huì)拋出InterruptedException同時(shí)中斷狀態(tài)會(huì)被清除
* <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}.
* 若當(dāng)前線程在InterruptibleChannel上發(fā)生IO阻塞,該通道要被關(guān)閉并將線程狀態(tài)設(shè)置為中斷同時(shí)拋出
* 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.
* 若該線程被選擇器阻塞,將線程狀態(tài)設(shè)置為中斷同時(shí)從選取方法中立即返回
* 該選取方法通常會(huì)返回一個(gè)非0值,當(dāng)wakeup方法正好被調(diào)用時(shí)
* <p> If none of the previous conditions hold then this thread's interrupt status will be set. </p>
* 非上述情況都會(huì)將線程狀態(tài)設(shè)置為中斷
* <p> Interrupting a thread that is not alive need not have any effect.
* 中斷一個(gè)非活線程不會(huì)有啥影響
* @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
// 調(diào)用interrupt方法僅僅是在當(dāng)前線程中打了一個(gè)停止的標(biāo)記,并不是真的停止線程!
interrupt0();
b.interrupt(this);
return;
}
}
interrupt0();
}
■ Daemon
分類:在JAVA中分成兩種線程:用戶線程和守護(hù)線程
特性:當(dāng)進(jìn)程中不存在非守護(hù)線程時(shí),則全部的守護(hù)線程會(huì)自動(dòng)化銷毀
應(yīng)用: JVM在啟動(dòng)后會(huì)生成一系列守護(hù)線程,最有名的當(dāng)屬GC(垃圾回收器)
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("守護(hù)線程運(yùn)行了");
for (int i = 0; i < 500000;i++){
System.out.println("守護(hù)線程計(jì)數(shù):" + i);
}
}
}, "kira");
t2.setDaemon(true);
t2.start();
Thread.sleep(500);
-------------
//輸出:
......
守護(hù)線程計(jì)數(shù):113755
守護(hù)線程計(jì)數(shù):113756
守護(hù)線程計(jì)數(shù):113757
守護(hù)線程計(jì)數(shù):113758
//結(jié)束打?。簳?huì)發(fā)現(xiàn)守護(hù)線程并沒有打印500000次,因?yàn)橹骶€程已經(jīng)結(jié)束運(yùn)行了
■ wait 和 notify 機(jī)制
wait()使線程停止運(yùn)行,notify()使停止的線程繼續(xù)運(yùn)行
使用wait()、notify()、notifyAll()需要先對調(diào)用對象加鎖,即只能在同步方法或同步塊中調(diào)用這些方法
調(diào)用wait()方法后,線程狀態(tài)由RUNNING變成WAITING,并將當(dāng)前線程放入對象的等待隊(duì)列中
調(diào)用notify()或notifyAll()方法之后,等待線程不會(huì)從wait()返回,需要notify()方法所在同步塊代碼執(zhí)行完畢而釋放鎖之后,等待線程才可以獲取到該對象鎖并從wait()返回
notify()方法將隨機(jī)選擇一個(gè)等待線程從等待隊(duì)列中移到同步隊(duì)列中;notifyAll()方法會(huì)將等待隊(duì)列中的所有等待線線程全部移到同步隊(duì)列中,被移動(dòng)線程狀態(tài)由WAITING變成BLOCKED
// wait/notify 簡單實(shí)例
public class NumberPrint implements Runnable {
private int number;
public byte[] res;
public static int count = 5;
public NumberPrint(int number, byte a[]){
this.number = number;
res = a;
}
@Override
public void run() {
synchronized (res){
while (count-- > 0){
try {
res.notify(); //喚醒等待res資源的線程,把鎖交給線程(該同步鎖執(zhí)行完畢自動(dòng)釋放鎖)
System.out.println(" " + number);
res.wait(); //釋放CPU控制權(quán),釋放res的鎖,本線程阻塞,等待被喚醒
System.out.println("----------線程"+Thread.currentThread().getName() + "獲得鎖,wait()后的代碼繼續(xù)運(yùn)行:"+ number);
} catch (InterruptedException e) {
e.printStackTrace();
}
} //end of while
return;
} //syn
}
public static void main(String[] args) {
final byte[] a = {0}; //以該對象為共享資源
new Thread(new NumberPrint(1,a),"1").start();
new Thread(new NumberPrint(2,a),"2").start();
}
}
*****各位看客,由于對線程的調(diào)度機(jī)制還理解比較淺,所以本文會(huì)持續(xù)更新…… ********
以上這篇淺談多線程_讓程序更高效的運(yùn)行就是小編分享給大家的全部內(nèi)容了,希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
Java高效實(shí)現(xiàn)電商產(chǎn)品排序?qū)崙?zhàn)
這篇文章主要為大家介紹了Java高效實(shí)現(xiàn)電商產(chǎn)品排序?qū)崙?zhàn),有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-11-11
一篇文章帶你了解jdk1.8新特性--為什么使用lambda表達(dá)式
Lambda是一個(gè)匿名函數(shù),我們可以把Lambda表達(dá)式理解為是一段可以傳遞的代碼,本篇文章就帶你了解,希望能給你帶來幫助2021-08-08
Spring Cloud Gateway + Nacos 實(shí)現(xiàn)動(dòng)態(tài)路由
這篇文章主要介紹了Spring Cloud Gateway + Nacos 實(shí)現(xiàn)動(dòng)態(tài)路由的方法,幫助大家實(shí)現(xiàn)路由信息的自動(dòng)更新,感興趣的朋友可以了解下2020-10-10
Java實(shí)現(xiàn)的程序員老黃歷實(shí)例
這篇文章主要介紹了Java實(shí)現(xiàn)的程序員老黃歷實(shí)例,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-05-05
如何利用JAVA正則表達(dá)式輕松替換JSON中的大字段
這篇文章主要給大家介紹了關(guān)于如何利用JAVA正則表達(dá)式輕松替換JSON中大字段的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-12-12

