溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

淺談多線程_讓程序更高效的運(yùn)行

發(fā)布時間:2020-10-21 06:12:46 來源:腳本之家 閱讀:151 作者:romanjoy 欄目:編程語言

Java Thread 的一些認(rèn)識:

Java是搶占式線程,一個線程就是進(jìn)程中單一的順序控制流,單個進(jìn)程可以擁有多個并發(fā)任務(wù),其底層是切分CPU時間,多線程和多任務(wù)往往是使用多處理器系統(tǒng)的最合理方式

進(jìn)程可以看作一個程序或者一個應(yīng)用;線程是進(jìn)程中執(zhí)行的一個任務(wù),多個線程可以共享資源

一個Java 應(yīng)用從main 方法開始運(yùn)行,main 運(yùn)行在一個線程內(nèi),也被稱為 “主線程”,Runnable也可以理解為Task (任務(wù))

JVM啟動后,會創(chuàng)建一些守護(hù)線程來進(jìn)行自身的常規(guī)管理(垃圾回收,終結(jié)處理),以及一個運(yùn)行main函數(shù)的主線程

隨著硬件水平的提高,多線程能使系統(tǒng)的運(yùn)行效率得到大幅度的提高,同時異步操作也增加復(fù)雜度和各種并發(fā)問題

淺談多線程_讓程序更高效的運(yùn)行

■ 線程 VS 進(jìn)程

在一個已有進(jìn)程中創(chuàng)建一個新線程比創(chuàng)建一個新進(jìn)程快的多

終止一個線程比終止一個進(jìn)程快的多

同一個進(jìn)程內(nèi)線程間切換比進(jìn)程間切換更快

線程提供了不同的執(zhí)行程序間通信的效率,同一個進(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ù)行為,一般為另一個線程執(zhí)行

■ 阻塞和非阻塞

當(dāng)一個線程占用臨界區(qū)資源,其他線程也想要使用該資源就必須等待,等待會導(dǎo)致線程的掛起,也就是阻塞(線程變成阻塞狀態(tài))。

此時若占用資源的線程一直不愿意釋放資源,那么其他所有阻塞在該臨界區(qū)的線程都會被掛起,變成阻塞狀態(tài),不能正常工作,直到占用線程釋放資源

非阻塞強(qiáng)調(diào)沒有一個線程可以妨礙其他線程執(zhí)行,所有線程都會嘗試去做下一步工作

■ 臨界資源與臨界區(qū)

一般指的是公共共享資源,即可以被多個線程共享使用。但同一時間只能由一個線程去訪問和操作臨界區(qū)的資源,一旦臨界區(qū)資源被一個線程占用,其他線程也想要使用該資源就必須等待,

就好比好多人想上大號,但只有一個坑,一個人占了坑,其他人就得排隊等待嘍

臨界區(qū)可以認(rèn)為是一段代碼,線程會在該端代碼中訪問共享資源,因此臨界區(qū)的界定標(biāo)準(zhǔn)就是是否訪問共享(臨界)資源(有點(diǎn)類似形成閉包的概念);一次只允許有一個程序(進(jìn)程/線程)在該臨界區(qū)中

■ 類定義

public class Thread implements Runnable {
  /* Make sure registerNatives is the first thing <clinit> does. 
    初始化時調(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)建一個指定Runnable的線程
 * 其中name規(guī)則為 "Thread-" + nextThreadNum()
 */
public Thread(Runnable target) {
 init(null, target, "Thread-" + nextThreadNum(), 0);
}
/**
 * 創(chuàng)建一個指定所屬線程組和Runnable的線程
 * 其中name規(guī)則為 "Thread-" + nextThreadNum()
 */
public Thread(ThreadGroup group, Runnable target) {
 init(group, target, "Thread-" + nextThreadNum(), 0);
}
/**
 * 創(chuàng)建一個指定name線程
 */
public Thread(String name) {
 init(null, null, name, 0);
}
/**
 * 創(chuàng)建一個指定所屬線程組和name的線程
 */
public Thread(ThreadGroup group, String name) {
 init(group, null, name, 0);
}
/**
 * 創(chuàng)建一個指定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)建一個新的Thread對象,同時滿足以下條件:
 *   1.該線程擁有一個指定的Runnable對象用于方法執(zhí)行
 *   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將調(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被開啟時,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會變更其優(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)建時被顯示地標(biāo)記為守護(hù)線程,新創(chuàng)建的線程才會被初始化為一個守護(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)建一個新的Thread對象,同時滿足以下條件:
 *   1.該線程擁有一個指定的Runnable對象用于方法執(zhí)行
 *   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分配給該線程的棧的地址(內(nèi)存)空間大小,這個參數(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>
 *  在一些平臺上,棧容量越高,(會在棧溢出之前)允許線程完成更深的遞歸(換句話說就是棧空間更深) 
 *  同理,若棧容量越小,(在拋出內(nèi)存溢出之前)允許同時存在更多的線程數(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會將指定的棧容量作為一個參考依據(jù),但當(dāng)小于平臺最小值時會直接使用最小值,最大值同理
 *  同樣,JVM會動態(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é)一下就是:這個值嚴(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同時有個安全管理器,該線程組由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方法(等同于一個空方法)
 * @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時,JVM就會忽略該值的設(shè)置
 * @throws SecurityException
 *   if the current thread cannot create a thread in the specified thread group
 *    如果當(dāng)前線程在一個指定的線程組中不能創(chuàng)建一個新的線程時將拋出 安全異常
 * @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方法最終會調(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. 給匿名線程自動編號,并按編號起名字*/
private static int threadInitNumber;
/* ThreadLocal values pertaining to this thread. This map is maintained by the ThreadLocal class. 
 * 當(dāng)前線程附屬的ThreadLocal,而ThreadLocalMap會被ThreadLocal維護(hù)(ThreadLocal會專門分析)
 */
ThreadLocal.ThreadLocalMap threadLocals = null;
/*
 * InheritableThreadLocal values pertaining to this thread. This map is
 * maintained by the InheritableThreadLocal class.
 * 主要作用:為子線程提供從父線程那里繼承的值
 * 在創(chuàng)建子線程時,子線程會接收所有可繼承的線程局部變量的初始值,以獲得父線程所具有的值
 * 創(chuàng)建一個線程時如果保存了所有 InheritableThreadLocal 對象的值,那么這些值也將自動傳遞給子線程
 * 如果一個子線程調(diào)用 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.
 * 棧容量:當(dāng)設(shè)置為0時,JVM會忽略該值;該值嚴(yán)重依賴于JVM平臺,有些VM甚至?xí)苯雍鲆曉撝? * 該值越大,線程棧空間變大,允許的并發(fā)線程數(shù)就越少;該值越小,線程棧空間變小,允許的并發(fā)線程數(shù)就越多
 */
private long stackSize;
/* JVM-private state that persists after native thread termination.*/
private long nativeParkEventPointer;
/* Thread ID. 每個線程都有專屬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è)置為中斷狀態(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)是個空的數(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)用該方法:所有該方法是類中的最靠前的一個靜態(tài)方法
 * clinit:在JVM第一次加載class文件時調(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();
}
/** 主動讓出CPU資源,當(dāng)時可能又立即搶到資源 **/
public static native void yield();
/** 休眠一段時間,讓出資源但是并不會釋放對象鎖 **/
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.
 *  初始化一個線程
 * @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是個本地方法
 Thread parent = currentThread();
 //安全管理器根據(jù)Java安全策略文件決定將哪組權(quán)限授予類
 //如果想讓應(yīng)用使用安全管理器和安全策略,可在啟動JVM時設(shè)定-Djava.security.manager選項
 //還可以同時指定安全策略文件
 //如果在應(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ù)量計數(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將會調(diào)用這個線程run方法
 *  當(dāng)獲取到CPU時間片時,會立即執(zhí)行run方法,此時線程會直接變成運(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.
 *  一個線程只能被start一次,特別是線程不會在執(zhí)行完畢后重新start
 *  當(dāng)線程已經(jīng)start了,再次執(zhí)行會拋出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.
  * 該方法不會被主線程或系統(tǒng)線程組調(diào)用,若未來有新增功能,也會被添加到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ù)需要-1,對應(yīng)init中的+1
 group.add(this);
 boolean started = false;
 try {
  //調(diào)用本地方法,將內(nèi)存中的線程狀態(tài)變更為就緒態(tài)
  //同時JVM會立即調(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出錯,會被調(diào)用棧直接通過
  }
 }
}
-------------
//start之后會立即調(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初始化時有指定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.
 *  測試線程是否處于活動狀態(tài)
 *  活動狀態(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...===========================");
 }

}

線程都會有自己的名字

獲取線程對象的方法: Thread.currentThread()

目標(biāo) run() 結(jié)束后線程完成

JVM線程調(diào)度程序決定實(shí)際運(yùn)行哪個處于可運(yùn)行狀態(tài)的線程

使用線程池執(zhí)行處理任務(wù)

線程狀態(tài)流程圖:

淺談多線程_讓程序更高效的運(yùn)行

■ 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.
 *  使線程睡眠一段毫秒時間,但線程并不會丟失已有的任何監(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);
}
/** 我們一般會直接調(diào)用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.
 * 暗示線程調(diào)度器當(dāng)前線程將釋放自己當(dāng)前占用的CPU資源
 * 線程調(diào)度器會自由選擇是否忽視此暗示
 * <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.
 * 該方法會放棄當(dāng)前的CPU資源,將它讓給其他的任務(wù)去占用CPU執(zhí)行時間
 * 但放棄的時間不確定,可能剛剛放棄又獲得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包設(shè)計
 */
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.
 *  如果當(dāng)前線程不是被自己中斷,可能會拋出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)用該中斷方法會拋出InterruptedException同時中斷狀態(tài)會被清除
 * <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è)置為中斷同時拋出
 * 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è)置為中斷同時從選取方法中立即返回
 *  該選取方法通常會返回一個非0值,當(dāng)wakeup方法正好被調(diào)用時
 * <p> If none of the previous conditions hold then this thread's interrupt status will be set. </p>
 *  非上述情況都會將線程狀態(tài)設(shè)置為中斷
 * <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
   // 調(diào)用interrupt方法僅僅是在當(dāng)前線程中打了一個停止的標(biāo)記,并不是真的停止線程!
   interrupt0();   
   b.interrupt(this);
   return;
  }
 }
 interrupt0();
}

■ Daemon

分類:在JAVA中分成兩種線程:用戶線程和守護(hù)線程

特性:當(dāng)進(jìn)程中不存在非守護(hù)線程時,則全部的守護(hù)線程會自動化銷毀

應(yīng)用: JVM在啟動后會生成一系列守護(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ù)線程計數(shù):" + i);
  }
 }
}, "kira");
t2.setDaemon(true);
t2.start();
Thread.sleep(500);
-------------
//輸出:
......
守護(hù)線程計數(shù):113755
守護(hù)線程計數(shù):113756
守護(hù)線程計數(shù):113757
守護(hù)線程計數(shù):113758
//結(jié)束打?。簳l(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)前線程放入對象的等待隊列中

調(diào)用notify()或notifyAll()方法之后,等待線程不會從wait()返回,需要notify()方法所在同步塊代碼執(zhí)行完畢而釋放鎖之后,等待線程才可以獲取到該對象鎖并從wait()返回

notify()方法將隨機(jī)選擇一個等待線程從等待隊列中移到同步隊列中;notifyAll()方法會將等待隊列中的所有等待線線程全部移到同步隊列中,被移動線程狀態(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í)行完畢自動釋放鎖)
     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ī)制還理解比較淺,所以本文會持續(xù)更新…… ********

以上這篇淺談多線程_讓程序更高效的運(yùn)行就是小編分享給大家的全部內(nèi)容了,希望能給大家一個參考,也希望大家多多支持億速云。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI