线程基础知识_线程生命周期_从JVM内存结构看多线程下的共享资源
線程生命周期
線程狀態(tài)
New: 線程創(chuàng)建(new Thread()) Runnable: 線程可運(yùn)行(thread.start()), 注: 調(diào)用start并不一定是運(yùn)行狀態(tài), 可能在等待CPU調(diào)度 Running: 線程進(jìn)入運(yùn)行狀態(tài) Blocked: 阻塞狀態(tài)(object.wait, Thread.sleep) Terminal: 死亡狀態(tài)(線程正常/非正常結(jié)束運(yùn)行)thread.start()方法分析線程狀態(tài)
start源碼:
public synchronized void start() {//使用if判斷thread狀態(tài), threadStatus為0表示當(dāng)前線程處于new狀態(tài)if (threadStatus != 0)throw new IllegalThreadStateException();//將線程添加到對(duì)應(yīng)線程組中group.add(this);boolean started = false;try {//native方法start0();started = true;} finally {try {//根據(jù)線程是否啟動(dòng)成功, 來(lái)決定是否添加threadStartFailed中if (!started) {group.threadStartFailed(this);}} catch (Throwable ignore) {}}}Thread使用threadStatus判斷線程狀態(tài), 當(dāng)線程啟動(dòng)后, 再次啟動(dòng), 或者線程處于死亡狀態(tài), 還對(duì)線程進(jìn)行start, 就會(huì)因?yàn)閠hreadStatus拋出IllegalThreadStateException
Thread中的模板設(shè)計(jì)模式
模板設(shè)計(jì)模式: 接口定義算法骨架, 在不同的子類(lèi)中, 對(duì)其算法骨架具有不同的實(shí)現(xiàn)方式
由于Thread實(shí)現(xiàn)了Runnable接口, 在Runnable中定義了算法骨架(run方法的定義), 因此創(chuàng)建線程一般都是重新run方法(繼承Thread, 或者實(shí)現(xiàn)Runnable)
Thread中的run方法:
從上面的run方法得出, 創(chuàng)建線程的2中常用方式:
1. new Thread(){@Overridepublic void run() {//.........}}; 2.new Thread(new Runnable() {@Overridepublic void run() {//.......}});二者的區(qū)別:
方式1中, 不能做到run方法重用, 方式2中, 可以做到run方法重用(一個(gè)runnable對(duì)象能給多個(gè)thread使用)
這里一定要注意共享資源的使用, 這里很容易讓人誤解: 多個(gè)線程共享一個(gè)資源的時(shí)候, 必須保證這樣的資源只有一份, 解決方式有: 當(dāng)多個(gè)runnable對(duì)象時(shí), 多個(gè)thread, 可以設(shè)置共享變量是static類(lèi)型, 當(dāng)只存在一份runnbable對(duì)象, 可以設(shè)置共享變量是普通成員變量, 其實(shí)這兩種方式都是類(lèi)似的, 只是取決于使用哪種方式創(chuàng)建線程, 使用多個(gè)Runnable對(duì)象, 還是一個(gè)Runnable對(duì)象.
Thread的init
調(diào)用線程的構(gòu)造方法的時(shí)候, 就會(huì)自動(dòng)調(diào)用Thread的init方法
public Thread() {init(null, null, "Thread-" + nextThreadNum(), 0);}//線程初始化操作//stackSize: 設(shè)置線程內(nèi)遞歸深度private void init(ThreadGroup g, Runnable target, String name,long stackSize, AccessControlContext acc,boolean inheritThreadLocals) {//此時(shí)獲取的線程是main線程, main線程執(zhí)行子線程的初始化操作, 只有當(dāng)子線程//執(zhí)行run操作的時(shí)候, 才意味著子線程啟動(dòng)Thread parent = currentThread();SecurityManager security = System.getSecurityManager();//設(shè)置當(dāng)當(dāng)前線程的groupif (g == null) {if (security != null) {g = security.getThreadGroup();}if (g == null) {g = parent.getThreadGroup();}}g.checkAccess();if (security != null) {if (isCCLOverridden(getClass())) {security.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);}}g.addUnstarted();//根據(jù)父線程屬性, 設(shè)置當(dāng)前線程的狀態(tài)this.group = g;this.daemon = parent.isDaemon();this.priority = parent.getPriority();if (security == null || isCCLOverridden(parent.getClass()))this.contextClassLoader = parent.getContextClassLoader();elsethis.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);this.stackSize = stackSize;tid = nextThreadID();}從init可以看出
當(dāng)線程創(chuàng)建的時(shí)候, 如果不指定group, priority等屬性的時(shí)候, 默認(rèn)使用父線程(例如: main線程創(chuàng)建一個(gè)新的線程)的值
一個(gè)線程的創(chuàng)建, 依賴與另外一個(gè)線程(注: main線程的創(chuàng)建依賴于JVM)
當(dāng)創(chuàng)建線程的時(shí)候, 沒(méi)有指定線程的Group, 默認(rèn)使用父線程所在的Group
ThreadGroup測(cè)試
思考引發(fā): 能否使用ThreadGroup進(jìn)行線程通信
/*** 測(cè)試ThreadGroup* @author regotto*/ public class ThreadGroupTest1 {public static void main(String[] args) {ThreadGroup group = new ThreadGroup("g1");System.out.println(group.getParent().getName());new Thread(group, "t1"){@Overridepublic void run(){//當(dāng)前線程組可以對(duì)父類(lèi)線程組或父類(lèi)線程組中線程進(jìn)行只讀操作, 寫(xiě)操作將會(huì)出現(xiàn)異常System.out.println(Thread.currentThread().getThreadGroup().getParent().getName());}}.start();try {Thread.sleep(2_000);} catch (InterruptedException e) {e.printStackTrace();}//當(dāng)線程組內(nèi)線程(不是守護(hù)線程)都沒(méi)有存活, 線程組不會(huì)destroy, 需要主動(dòng)destroySystem.out.println("group.isDestroyed()" + group.isDestroyed());group.destroy();System.out.println("group.isDestroyed()" + group.isDestroyed());//將中斷group中所有線程 // group.interrupt();//將group中線程copy到線程數(shù)組中 // group.enumerate(new Thread[10]);} }結(jié)論:
可以使用ThreadGroup進(jìn)行線程通信, 但是這種通信存在局限性, 可以使用ChildThreadGroup獲取ParentThreadGroup信息, 但是反過(guò)來(lái)就不行, 并且Child只能讀取Parent的信息, 不能進(jìn)行寫(xiě)操作
守護(hù)線程
用通俗的話來(lái)描述, 守護(hù)線程就是后臺(tái)線程, 處理后臺(tái)任務(wù), 當(dāng)前臺(tái)任務(wù)執(zhí)行完, 守護(hù)線程自動(dòng)銷(xiāo)毀, 例如JVM就是守護(hù)線程
守護(hù)線程一般用于垃圾清理, 監(jiān)控
T線程啟動(dòng), 啟動(dòng)一個(gè)守護(hù)線程t1開(kāi)始心跳, 當(dāng)T線程死亡, 守護(hù)線程自動(dòng)退出心跳結(jié)束
ThreadHook
ThreadHook: 線程鉤子, 功能類(lèi)似于守護(hù)線程(守護(hù)線程在前臺(tái)線程運(yùn)行中, 也都一直運(yùn)行, 但是線程鉤子只有最后程序結(jié)束,時(shí) 才執(zhí)行thread.start操作), 當(dāng)程序退出時(shí), 負(fù)責(zé)垃圾清理或執(zhí)行最后處理工作
ThreadHook測(cè)試
從JVM內(nèi)存結(jié)構(gòu)看多線程下的共享資源
各個(gè)模塊的簡(jiǎn)單描述
1.方法區(qū): 存放.class文件, 類(lèi)信息, 常量, 靜態(tài)變量 2.程序計(jì)數(shù)器: 當(dāng).class文件加載JVM的方法區(qū)中時(shí), 當(dāng)前線程利用程序計(jì)數(shù)器執(zhí)行方法區(qū)中的字節(jié)碼指令由于不同的線程執(zhí)行指令不能相互影響, 因此設(shè)置程序計(jì)數(shù)器為線程私有 3.Java虛擬機(jī)棧: Java虛擬機(jī)棧是一個(gè)棧結(jié)構(gòu), 存放棧幀, 每一個(gè)棧幀就是一次方法調(diào)用(棧幀中存放方法局部變量表, 操作棧, 動(dòng)態(tài)鏈接, 方法出口等), 不同線程執(zhí)行過(guò)程調(diào)用的方法順序不同, 因此Java虛擬機(jī)棧就是線程私有的每一個(gè)線程的Java虛擬機(jī)棧生命期與線程相同 4.本地方法棧: 與Java虛擬機(jī)棧類(lèi)似, 只是本地方法棧存放線程調(diào)用native方法的棧幀情況 5.堆內(nèi)存: 存放公共資源, JVM運(yùn)行過(guò)程中存放所有生成的對(duì)象, 被所有線程共享 7.直接內(nèi)存: 此塊區(qū)域歸屬于native方法(C/C++)執(zhí)行時(shí)用的堆空間注: 由于Java虛擬機(jī)棧屬于線程私有, 當(dāng)每個(gè)線程的Java虛擬機(jī)空間越大, 那么能創(chuàng)建的線程就越少
總結(jié)
以上是生活随笔為你收集整理的线程基础知识_线程生命周期_从JVM内存结构看多线程下的共享资源的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: python pyecharts 折线图
- 下一篇: php+redis+设置前缀,sprin