JUC線程高級(jí)教程

????????????????????????JUC

????????????????????????????????原創(chuàng)者:文思磷雇,感謝尚硅谷,資料來(lái)源于尚硅谷

目錄:

1望艺、volatile關(guān)鍵字與內(nèi)存可見(jiàn)性

2苛秕、原子變量與CAS算法

3、同步容器類

4找默、閉鎖操作

5艇劫、Callable接口(常用)

6、Lock同步鎖

7惩激、讀寫鎖

8店煞、線程八鎖

9岖赋、線程池

10既琴、線程調(diào)度

11、ForkJoinPool分支/合并框架 工作竊取

一绍赛、JUC

1骡技、volatile關(guān)鍵字與內(nèi)存可見(jiàn)性

??? Java5.0提供了java.util.concurrent(簡(jiǎn)稱JUC )包鸣个,在此包中增加了在并發(fā)編程中很常用的實(shí)用工具類,用于定義類似于線程的自定義子系統(tǒng)哮兰,包括線程池毛萌、異步IO 和輕量級(jí)任務(wù)框架苟弛。提供可調(diào)的喝滞、靈活的線程池。還提供了設(shè)計(jì)用于多線程上下文中的Collection實(shí)現(xiàn)等膏秫。

內(nèi)存可見(jiàn)性(Memory Visibility)是指當(dāng)某個(gè)線程正在使用對(duì)象狀態(tài)而另一個(gè)線程在同時(shí)修改該狀態(tài)右遭,需要確保當(dāng)一個(gè)線程修改了對(duì)象狀態(tài)后,其他線程能夠看到發(fā)生的狀態(tài)變化缤削。

???可見(jiàn)性錯(cuò)誤是指當(dāng)讀操作與寫操作在不同的線程中執(zhí)行時(shí)窘哈,我們無(wú)法確保執(zhí)行讀操作的線程能適時(shí)地看到其他線程寫入的值,有時(shí)甚至是根本不可能的事情亭敢。

如下代碼示例:

public classTestVolatile {

??? public static void main(String[] args){

?????? Testt = new Test();

?????? new Thread(t).start();

?????? while(true){

?????????? if(t.getFlag()){

????????????? System.out.println("-----enter main while if----");

????????????? break;

?????????? }

?????? }

??? }

}

class Test implementsRunnable{

??? private boolean flag = false;

??? @Override

??? public void run() {

?????? try{

?????????? Thread.sleep(1000);

?????? }catch(Exception e){ }

?????? flag = true;

?????? System.out.println("------this is test child thread-----");

??? }

??? public boolean getFlag(){

?????? return this.flag;

??? }

}

運(yùn)行結(jié)果:

----------this is test child thread-----------

主線程main的------enter main while if-------一直沒(méi)進(jìn)入執(zhí)行滚婉。這就是發(fā)生了內(nèi)存可見(jiàn)性錯(cuò)誤,上述程序執(zhí)行流程:

由于布爾變量flag偏底層帅刀,所以jvm執(zhí)行布爾運(yùn)算時(shí)非橙酶梗快,所以在test子線程內(nèi)復(fù)制修改flag之后扣溺,還沒(méi)有來(lái)得及寫入main主線程骇窍,main主線程就進(jìn)入到while中,導(dǎo)致如上效果锥余。

如果用同步synchronized鎖則往往是噩夢(mèng)的開(kāi)始:

while(true){

?????????? synchronized (t) {

????????????? if(t.getFlag()){

????????????????? System.out.println("-----enter main while if-----");

????????????????? break;

????????????? }

?????????? }?????????

?????? }

這樣會(huì)導(dǎo)致極低的效率腹纳。同步鎖具有互斥性,如果多個(gè)縣城訪問(wèn)這塊,如果一個(gè)線程已持有這個(gè)鎖嘲恍,另一個(gè)線程發(fā)現(xiàn)后就阻塞在這里等待鎖的釋放足画,然后就等待另一個(gè)線程調(diào)用完釋放鎖,然后cpu再調(diào)度蛔钙。所以不要輕易甚至不要使用同步synchronized鎖锌云。這就需要volatile關(guān)鍵字:private volatile boolean flag = false。

/*

?*一吁脱、volatile關(guān)鍵字當(dāng)多個(gè)線程進(jìn)行操作共享數(shù)據(jù)時(shí)桑涎,可以保證內(nèi)存中的數(shù)據(jù)可見(jiàn)。

?*? ? ? 相較于 synchronized 是一種較為輕量級(jí)的同步策略兼贡。

?*注意:

?* 1. volatile不具備“互斥性”

?* 2. volatile不能保證變量的“原子性”

?*/

public classTestVolatile {

??? public static void main(String[] args){

?????? Testt = new Test();

?????? new Thread(t).start();?????

?????? while(true){

?????????? if(t.getFlag()){

????????????? System.out.println("------enter main while if------");

????????????? break;

?????????? }?????????

?????? }

??? }

}

class Test implementsRunnable{

??? private volatile boolean flag = false;

??? @Override

??? public void run() {

?????? try{

?????????? Thread.sleep(1000);

?????? }catch(Exception e){????

?????? }

?????? flag = true;

?????? System.out.println("-------this is test child thread-----");

??? }

??? public boolean getFlag(){

?????? return this.flag;

??? }

}

運(yùn)行結(jié)果:

-----------enter main while if---------

----------this is test child thread-----------

程序執(zhí)行流程:

子線程不會(huì)同步變量到自己的線程緩存中攻冷,直接修改主內(nèi)存中的變量。

Java提供了一種稍弱的同步機(jī)制遍希,即volatile 變量等曼,用來(lái)確保將變量的更新操作通知到其他線程≡渌猓可以將volatile 看做一個(gè)輕量級(jí)的鎖禁谦,但是又與鎖有些不同:

?對(duì)于多線程,不是一種互斥關(guān)系

?不能保證變量狀態(tài)的“原子性”操作

2废封、原子變量與CAS算法

volatile只能解決變量同步問(wèn)題州泊,但解決不了并發(fā)中的原子性問(wèn)題(原子性問(wèn)題不再演示)。i++ 的操作實(shí)際上分為三個(gè)步驟“讀-改-寫”

int i = 10; i = i++; 相當(dāng)于:int temp = i; i = i + 1; i = temp;可看出僅僅依靠volatile解決不了原子性問(wèn)題漂洋。

Jdk提供了原子變量來(lái)解決此問(wèn)題:

Java.util.concurrent.atomic類的小工具包遥皂,支持在單個(gè)變量上解除鎖的線程安全編程。事實(shí)上刽漂,此包中的類可將volatile 值演训、字段和數(shù)組元素的概念擴(kuò)展到那些也提供原子條件更新操作的類.

類AtomicBoolean、AtomicInteger贝咙、AtomicLong 和AtomicReference 的實(shí)例各自提供對(duì)相應(yīng)類型單個(gè)變量的訪問(wèn)和更新样悟。每個(gè)類也為該類型提供適當(dāng)?shù)膶?shí)用工具方法。AtomicIntegerArray庭猩、AtomicLongArray 和AtomicReferenceArray 類進(jìn)一步擴(kuò)展了原子操作窟她,對(duì)這些類型的數(shù)組提供了支持。這些類在為其數(shù)組元素提供volatile 訪問(wèn)語(yǔ)義方面也引人注目眯娱,這對(duì)于普通數(shù)組來(lái)說(shuō)是不受支持的礁苗。

核心方法:booleancompareAndSet(expectedValue, updateValue)

java.util.concurrent.atomic 包下提供了一些原子操作的常用類:

?AtomicBoolean 、AtomicInteger 徙缴、AtomicLong 试伙、AtomicReference

?AtomicIntegerArray 嘁信、AtomicLongArray

?AtomicMarkableReference

?AtomicReferenceArray

?AtomicStampedReference

CAS (Compare-And-Swap) 是一種硬件對(duì)并發(fā)的支持,針對(duì)多處理器操作而設(shè)計(jì)的處理器中的一種特殊指令疏叨,用于管理對(duì)共享數(shù)據(jù)的并發(fā)訪問(wèn)

CAS 是一種無(wú)鎖的非阻塞算法的實(shí)現(xiàn)

CAS 包含了3 個(gè)操作數(shù):

1潘靖、需要讀寫的內(nèi)存值V

2、進(jìn)行比較的值A(chǔ)

3蚤蔓、擬寫入的新值B

當(dāng)且僅當(dāng)V 的值等于A 時(shí)卦溢,CAS 通過(guò)原子方式用新值B 來(lái)更新V 的值,否則不會(huì)執(zhí)行任何操作

示例:

/*

?*一秀又、i++ 的原子性問(wèn)題:i++ 的操作實(shí)際上分為三個(gè)步驟“讀-改-寫”

?*??? ??inti = 10;

?*??? ??i = i++; //10

?*??? ??inttemp= i;

?*??? ??i = i + 1;

?*??? ??i =temp;

?*二单寂、原子變量:在 java.util.concurrent.atomic 包下提供了一些原子變量。

?*??? 1.volatile保證內(nèi)存可見(jiàn)性

?*??? 2.CAS(Compare-And-Swap) 算法保證數(shù)據(jù)變量的原子性

?*??????? CAS算法是硬件對(duì)于并發(fā)操作的支持

?*??????? CAS包含了三個(gè)操作數(shù):

?*??????? ①內(nèi)存值? V

?*??????? ②預(yù)估值? A

?*??????? ③更新值? B

?*??????? 當(dāng)且僅當(dāng) V == A 時(shí)吐辙, V = B; 否則宣决,不會(huì)執(zhí)行任何操作。

?*/

public classTestAtomicDemo {

??? public static void main(String[] args) {

?????? AtomicDemoa = new AtomicDemo();

?????? for(int i=0;i<10;i++){//啟動(dòng)10個(gè)子線程

?????????? new Thread(a).start();

?????? }

??? }

}

class AtomicDemo implements Runnable{

??? private AtomicInteger number = new AtomicInteger(0);

??? public void run() {

?????? try{

?????????? Thread.sleep(2000);

?????? }catch(Exception e){????

?????? }

?????? System.out.println("number="+this.handlerNumber());

??? }

??? public int handlerNumber(){//子線程里加1

?????? return number.getAndIncrement();

??? }

}

以上使用原子性變量解決了原子性問(wèn)題昏苏。CAS算法是基于硬件底層算法實(shí)現(xiàn)的尊沸,也不是使用鎖的互斥原理,不會(huì)釋放cpu的控制權(quán)贤惯,所以效率比鎖高洼专。

3、同步容器類

HashMap與HashTable的區(qū)別之一是HashMap線程不安全孵构,HashTable線程安全屁商,所以HashTable的效率也低。

Java 5在java.util.concurrent 包中提供了多種并發(fā)容器類來(lái)改進(jìn)同步容器的性能浦译。

ConcurrentHashMap同步容器類是Java 5 增加的一個(gè)線程安全的哈希表棒假。對(duì)與多線程的操作溯职,介于HashMap 與Hashtable 之間精盅。內(nèi)部采用“鎖分段”機(jī)制替代Hashtable 的獨(dú)占鎖,進(jìn)而提高性能谜酒。

此包還提供了設(shè)計(jì)用于多線程上下文中的Collection 實(shí)現(xiàn):ConcurrentHashMap叹俏、ConcurrentSkipListMap、ConcurrentSkipListSet僻族、CopyOnWriteArrayList 和CopyOnWriteArraySet粘驰。當(dāng)期望許多線程訪問(wèn)一個(gè)給定collection 時(shí),ConcurrentHashMap 通常優(yōu)于同步的HashMap述么,ConcurrentSkipListMap 通常優(yōu)于同步的TreeMap蝌数。當(dāng)期望的讀數(shù)和遍歷遠(yuǎn)遠(yuǎn)大于列表的更新數(shù)時(shí),CopyOnWriteArrayList 優(yōu)于同步的ArrayList度秘。

JDK1.8對(duì)ConcurrentSkipListMap又取消了分段鎖顶伞,又采用回了CAS。

示例:

import java.util.Iterator;

import java.util.concurrent.CopyOnWriteArrayList;

/*

?*CopyOnWriteArrayList/CopyOnWriteArraySet :“寫入并復(fù)制”

?*注意:添加操作多時(shí),效率低唆貌,因?yàn)槊看翁砑訒r(shí)都會(huì)進(jìn)行復(fù)制滑潘,開(kāi)銷非常的大。并發(fā)迭代操作多時(shí)可以選擇锨咙。

?*/

public class TestCopyOnWriteArrayList {

?????? publicstatic void main(String[] args) {

????????????? HelloThreadht = new HelloThread();

????????????? for(int i = 0; i < 10; i++) {

???????????????????? newThread(ht).start();

????????????? }

?????? }

}

class HelloThread implements Runnable{

?????? private staticCopyOnWriteArrayList list = new CopyOnWriteArrayList<>();

?????? static{

????????????? list.add("AA");

????????????? list.add("BB");

????????????? list.add("CC");

?????? }

?????? @Override

?????? publicvoid run() {

????????????? Iteratorit = list.iterator();

????????????? while(it.hasNext()){

???????????????????? System.out.println(it.next());

???????????????????? list.add("AA");

????????????? }

?????? }

}

4语卤、閉鎖操作

CountDownLatch 一個(gè)同步輔助類,在完成一組正在其他線程中執(zhí)行的操作之前酪刀,它允許一個(gè)或多個(gè)線程一直等待粹舵。

閉鎖可以延遲線程的進(jìn)度直到其到達(dá)終止?fàn)顟B(tài),閉鎖可以用來(lái)確保某些活動(dòng)直到其他活動(dòng)都完成才繼續(xù)執(zhí)行:

1骂倘、確保某個(gè)計(jì)算在其需要的所有資源都被初始化之后才繼續(xù)執(zhí)行;

2齐婴、確保某個(gè)服務(wù)在其依賴的所有其他服務(wù)都已經(jīng)啟動(dòng)之后才啟動(dòng);

3、等待直到某個(gè)操作所有參與者都準(zhǔn)備就緒再繼續(xù)執(zhí)行

示例:

/*

?* CountDownLatch:閉鎖稠茂,在完成某些運(yùn)算是柠偶,只有其他所有線程的運(yùn)算全部完成,當(dāng)前運(yùn)算才繼續(xù)執(zhí)行

?*/

public classTestCountDownLatch {

??? public static void main(String[] args) {

//new CountDownLatch(10)里面的參數(shù)是倒計(jì)時(shí)初始化個(gè)數(shù)睬关,因?yàn)橄旅鏁?huì)創(chuàng)建10個(gè)線程诱担,每啟動(dòng)一個(gè)線程就減1,當(dāng)為0是就說(shuō)明所有線程創(chuàng)建完畢

?????? final CountDownLatch latch = new CountDownLatch(10);

?????? LatchDemold = new LatchDemo(latch);

?????? long startTime= System.currentTimeMillis();

?????? for(int i=0;i<10;i++){

?????????? new Thread(ld).start();

?????? }

?????? try {

?????????? latch.await();

?????? }catch(InterruptedException e) {

?????? }

?????? long endTime= System.currentTimeMillis();

?????? System.out.println("----------"+(endTime-startTime));

??? }

}

class LatchDemo implements Runnable{

??? private CountDownLatch latch;

??? publicLatchDemo(CountDownLatch latch){

?????? this.latch = latch;

??? }

??? @Override

??? public void run() {

?????? for(int i=0;i<50000;i++){

?????????? if(1%2==0){

????????????? System.out.println(i);

?????????? }

?????? }

?????? latch.countDown();

??? }

}

一定要在await()之前必須調(diào)用countDown()進(jìn)行遞減操作电爹。

5蔫仙、Callable接口(常用)

以前都認(rèn)為創(chuàng)建線程的方式是兩種:繼承Thread類和實(shí)現(xiàn)Runnable接口。現(xiàn)在可歸結(jié)為四種丐箩,還有:實(shí)現(xiàn)Callable接口與

Java 5.在java.util.concurrent 提供了一個(gè)新的創(chuàng)建執(zhí)行線程的方式:Callable 接口摇邦。

Callable 接口類似于Runnable,兩者都是為那些其實(shí)例可能被另一個(gè)線程執(zhí)行的類設(shè)計(jì)的屎勘。但是Runnable 不會(huì)返回結(jié)果施籍,并且無(wú)法拋出經(jīng)過(guò)檢查的異常

Callable 需要依賴FutureTask 概漱。

根據(jù)以上分析FutureTask 也可以用作閉鎖丑慎,實(shí)現(xiàn)閉鎖的效果。

示例:

/*

?*一瓤摧、創(chuàng)建執(zhí)行線程的方式三:實(shí)現(xiàn)Callable 接口竿裂。 相較于實(shí)現(xiàn) Runnable 接口的方式,方法可以有返回值照弥,并且可以拋出異常腻异。

?*二、執(zhí)行Callable 方式这揣,需要 FutureTask 實(shí)現(xiàn)類的支持悔常,用于接收運(yùn)算結(jié)果敢会。? FutureTask是? Future接口的實(shí)現(xiàn)類

?*/

public classTestCallable {

??? public static void main(String[] args) {

?????? int t = 0;

?????? ThreadDemotd = new ThreadDemo(t);

?????? FutureTaskresult= newFutureTask(td);

?????? long starttime= System.currentTimeMillis();

?????? new Thread(result).start();

?????? try {

?????????? IntegerresultInt= result.get();

?????????? long endtime= System.currentTimeMillis();

?????????? System.out.println("---time---"+(endtime-starttime));

?????????? System.out.println("---resultInt---"+resultInt);

?????? }catch(InterruptedException e) {

?????? }catch(ExecutionException e){

? ? ? ?}

??? }

}

class ThreadDemo implements Callable{

??? private Integer count;

??? public ThreadDemo(Integer count){

?????? this.count = count;

??? }

??? @Override

??? public Integer call() throws Exception {

?????? for(int i=0;i<1000000000;i++){

?????????? count++;

?????? }

?????? return count;

??? }

}

運(yùn)行結(jié)果:

---time---3369

---resultInt---1000000000

6、Lock同步鎖

在jdk1.5以前这嚣,解決線程安全只能用同步代碼塊和同步方法以及volatile 鸥昏。

Jdk1.5后可以用同步鎖Lock,這是一個(gè)顯示鎖姐帚,需要通過(guò)lock()方法上鎖吏垮,通過(guò)unlock()方法釋放鎖。但并不是一種替代內(nèi)置鎖的方法罐旗,而是當(dāng)內(nèi)置鎖不適用時(shí)膳汪,作為一種可選擇的高級(jí)功能。

ReentrantLock 實(shí)現(xiàn)了Lock 接口九秀,并提供了與synchronized 相同的互斥性和內(nèi)存可見(jiàn)性遗嗽。但相較于synchronized 提供了更高的處理鎖的靈活性。

不加鎖的同步問(wèn)題:

加鎖示例:

/*

?*一鼓蜒、用于解決多線程安全問(wèn)題的方式:

?* synchronized:隱式鎖

?* 1.同步代碼塊. 2. 同步方法

?*jdk1.5后:

?* 3.同步鎖 Lock

?*注意:是一個(gè)顯示鎖痹换,需要通過(guò) lock() 方法上鎖,必須通過(guò) unlock() 方法進(jìn)行釋放鎖

?*/

public classTestLock {

??? public static void main(String[] args){

?????? LockDemold = new LockDemo();

?????? new Thread(ld,"1號(hào)售票點(diǎn),").start();

?????? new Thread(ld,"2號(hào)售票點(diǎn),").start();

?????? new Thread(ld,"3號(hào)售票點(diǎn),").start();

??? }

}

class LockDemo implements Runnable{

??? private volatile int count=10;

??? private Lock lock = new ReentrantLock();

??? @Override

??? public void run() {

?????? while(count>0){

?????????? lock.lock();

?????????? try {

????????????? Thread.sleep(100);

?????????? }catch(InterruptedException e) {

?????????? }finally{

????????????? lock.unlock();

?????????? }

?????????? count--;

?????????? System.out.println(Thread.currentThread().getName()+"剩余票數(shù):"+count);

?????? }??

??? }

}

7都弹、讀寫鎖

寫寫,讀寫 需要"互斥"娇豫,讀讀 不需要"互斥",這樣即避免了線程安全問(wèn)題又提高了效率畅厢。

一個(gè)讀寫鎖維護(hù)了一個(gè)讀鎖和一個(gè)寫鎖冯痢,其中讀鎖可以多個(gè)線程使用,寫鎖只能一個(gè)線程獨(dú)占框杜。

/**

?*寫寫,讀寫 需要"互斥"

?*讀讀 不需要"互斥"

?*/

public classTestReadWriteLock {

??? public static void main(String[] args){

?????? ReadWriteLockDemod = new ReadWriteLockDemo();

?????? new Thread(new Runnable(){

?????????? @Override

?????????? public void run() {

????????????? d.write(1000000000);

?????????? }

?????? },"write").start();

?????? for(int i=0;i<10;i++){

?????????? new Thread(new Runnable(){

????????????? @Override

????????????? public void run() {

????????????????? d.read();

????????????? }

?????????? },"read").start();

?????? }

??? }

}

class ReadWriteLockDemo{

??? private int number = 0;

??? ReadWriteLocklock= newReentrantReadWriteLock();

??? public void read(){

?????? lock.readLock().lock();;

?????? try{

??????? System.out.println(Thread.currentThread().getName()+":number="+this.number);

?????? }finally{

?????????? lock.readLock().unlock();

?????? }

??? }

??? public void write(int number){

?????? lock.writeLock().lock();

?????? try{

?????????? System.out.println(Thread.currentThread().getName());

?????????? this.number = number;

?????? }finally{

?????????? lock.writeLock().unlock();

?????? }?????

??? }

}

運(yùn)行結(jié)果:

write

read:number=1000000000

read:number=1000000000

read:number=1000000000

read:number=1000000000

read:number=1000000000

read:number=1000000000

read:number=1000000000

read:number=1000000000

read:number=1000000000

read:number=1000000000

8浦楣、線程八鎖

a:兩個(gè)普通同步方法,兩個(gè)線程咪辱,標(biāo)準(zhǔn)打印

public classTestThread8demo {

??? public static void main(String[] args) {

?????? Numbern = new Number();

?????? new Thread(new Runnable(){

?????????? @Override

?????????? public void run() {

????????????? n.getOne();

?????????? }

?????? }).start();

?????? new Thread(new Runnable(){

?????????? @Override

?????????? public void run() {

????????????? n.getTwo();

?????????? }

?????? }).start();

??? }

}

class Number{

??? public synchronized void getOne(){

?????? System.out.println("One");

??? }

??? public synchronized void getTwo(){

?????? System.out.println("Two");

??? }

}

運(yùn)行結(jié)果:立刻打印出

One

Two

b:兩個(gè)普通同步方法振劳,其中一個(gè)線程休眠3秒后,兩個(gè)線程再標(biāo)準(zhǔn)打印

public synchronizedvoidgetOne(){

?????? try {

?????????? Thread.sleep(3000);

?????? }catch(InterruptedException e) {

?????? }

?????? System.out.println("One");

??? }

??? public synchronized void getTwo(){

?????? System.out.println("Two");

??? }

運(yùn)行結(jié)果:3秒后打印出

One

Two

推論:

一個(gè)對(duì)象里面如果有多個(gè)synchronized方法梧乘,某一個(gè)時(shí)刻內(nèi)澎迎,只要一個(gè)線程去調(diào)用其中的一個(gè)synchronized方法了庐杨,其它的線程都只能等待选调,換句話說(shuō),某一個(gè)時(shí)刻內(nèi)灵份,只能有唯一一個(gè)線程去訪問(wèn)這些synchronized方法仁堪。

鎖的是當(dāng)前對(duì)象this,被鎖定后填渠,其它的線程都不能進(jìn)入到當(dāng)前對(duì)象的其它的synchronized方法弦聂。

c:新增一個(gè)普通方法(這樣一個(gè)同步鎖并休眠3秒鸟辅,一個(gè)同步鎖,一個(gè)普通方法)

public classTestThread8demo {

??? public static void main(String[] args) {

?????? Numbern = new Number();

?????? new Thread(new Runnable(){

?????????? @Override

?????????? public void run() {

????????????? n.getOne();

?????????? }

?????? }).start();

?????? new Thread(new Runnable(){

?????????? @Override

?????????? public void run() {

?????????? ??? n.getTwo();

?????????? }

?????? }).start();

?????? new Thread(new Runnable(){

?????????? @Override

?????????? public void run() {

????????????? n.getThree();

?????????? }

?????? }).start();

??? }

}

class Number{

??? public synchronized void getOne(){

?????? try {

?????????? Thread.sleep(3000);

?????? }catch(InterruptedException e) {

?????? }

?????? System.out.println("One");

??? }

??? public synchronized void getTwo(){

?????? System.out.println("Two");

??? }

??? public void getThree(){

?????? System.out.println("Three");

??? }

}

運(yùn)行結(jié)果:

Three

3秒后……

One

Two

推論:普通方法與同步鎖無(wú)關(guān)莺葫。

d:兩個(gè)普通的同步方法匪凉,同時(shí)兩個(gè)對(duì)象訪問(wèn),打印

public classTestThread8demo {

??? public static void main(String[] args) {

?????? Numbern = new Number();

?????? Numbern2 = new Number();

?????? new Thread(new Runnable(){

?????????? @Override

?????????? public void run() {

????????????? n.getOne();

?????????? }

?????? }).start();

?????? new Thread(new Runnable(){

?????????? @Override

?????????? public void run() {

????????????? n2.getTwo();

?????????? }

?????? }).start();

??? }

}

class Number{

??? public synchronized void getOne(){

?????? try {

?????????? Thread.sleep(3000);

?????? }catch(InterruptedException e) {

?????? }

?????? System.out.println("One");

??? }

??? public synchronized void getTwo(){

?????? System.out.println("Two");

??? }??

}

運(yùn)行結(jié)果:

Two

3秒后……

One

推論:

換成兩個(gè)對(duì)象后捺檬,就不是同一把鎖了再层,彼此不受影響。更進(jìn)一步印證了synchronized最小作用域的范圍是對(duì)象堡纬。

也就是說(shuō)如果一個(gè)實(shí)例對(duì)象的非靜態(tài)同步方法獲取鎖后聂受,該實(shí)例對(duì)象的其他非靜態(tài)同步方法必須等待獲取鎖的方法釋放鎖后才能獲取鎖,可是別的實(shí)例對(duì)象的非靜態(tài)同步方法因?yàn)楦搶?shí)例對(duì)象的非靜態(tài)同步方法用的是不同的鎖烤镐,所以毋須等待該實(shí)例對(duì)象已獲取鎖的非靜態(tài)同步方法釋放鎖就可以獲取他們自己的鎖蛋济。

e:修改getOne為靜態(tài)同步,然后一個(gè)對(duì)象訪問(wèn)打印

public classTestThread8demo {

??? public static void main(String[] args) {

?????? Numbern = newNumber();

?????? new Thread(new Runnable(){

?????????? @Override

?????????? public void run() {

????????????? n.getOne();

?????????? }

?????? }).start();

?????? new Thread(new Runnable(){

?????????? @Override

?????????? public void run() {

????????????? n.getTwo();

?????????? }

?????? }).start();

??? }

}

class Number{

??? public static synchronized void getOne(){

?????? try {

?????????? Thread.sleep(3000);

?????? }catch(InterruptedException e) {

?????? }

?????? System.out.println("One");

??? }

??? public synchronized void getTwo(){

?????? System.out.println("Two");

??? }

}

運(yùn)行結(jié)果:

Two

3秒后……

One

推論:

所有的靜態(tài)同步方法用的也是同一把鎖:類對(duì)象本身炮叶,和非靜態(tài)對(duì)象鎖是兩個(gè)不同的對(duì)象碗旅,所以靜態(tài)同步方法與非靜態(tài)同步方法之間是不會(huì)有競(jìng)態(tài)條件的。所以two先執(zhí)行了镜悉,one休眠后繼續(xù)執(zhí)行扛芽。但是請(qǐng)看g

f:修改getOne和getTwo都為靜態(tài)同步,然后一個(gè)對(duì)象訪問(wèn)打印

public classTestThread8demo {

??? public static void main(String[] args) {

?????? Numbern = new Number();

?????? Numbern2= newNumber();

?????? new Thread(new Runnable(){

?????????? @Override

?????????? public void run() {

????????????? n.getOne();

?????????? }

?????? }).start();

?????? new Thread(new Runnable(){

?????????? @Override

?????????? public void run() {

????????????? n.getTwo();

?????????? }

?????? }).start();

??? }

}

class Number{

??? public static synchronized void getOne(){

?????? try {

?????? ??? Thread.sleep(3000);

?????? }catch(InterruptedException e) {

?????? }

?????? System.out.println("One");

??? }

??? public static synchronized void getTwo(){

?????? System.out.println("Two");

??? }

}

運(yùn)行結(jié)果:

3秒后…

One

Two

推論:

進(jìn)一步且只印證了所有的靜態(tài)同步方法用的也是同一把鎖:類對(duì)象本身积瞒,但是請(qǐng)看g

g:修改getOne和getTwo都為靜態(tài)同步川尖,然后兩個(gè)對(duì)象訪問(wèn)打印

public classTestThread8demo {

??? public static void main(String[] args) {

?????? Numbern = new Number();

?????? Numbern2 = newNumber();

?????? new Thread(new Runnable(){

?????????? @Override

?????????? public void run() {

????????????? n.getOne();

?????????? }

?????? }).start();

?????? new Thread(new Runnable(){

?????????? @Override

?????????? public void run() {

????????????? n2.getTwo();

?????????? }

?????? }).start();

??? }

}

class Number{

??? public static synchronized void getOne(){

?????? try {

?????????? Thread.sleep(3000);

?????? }catch(InterruptedException e) {

?????? }

?????? System.out.println("One");

??? }

??? public static synchronized void getTwo(){

?????? System.out.println("Two");

??? }

}

運(yùn)行結(jié)果:

3秒后……

One

Two

推論:

所有的靜態(tài)同步方法用的也是同一把鎖:類對(duì)象本身,和非靜態(tài)對(duì)象鎖是兩個(gè)不同的對(duì)象茫孔,所以靜態(tài)同步方法與非靜態(tài)同步方法之間是不會(huì)有競(jìng)態(tài)條件的叮喳。但是一旦一個(gè)靜態(tài)同步方法獲取鎖后,其他的靜態(tài)同步方法都必須等待該方法釋放鎖后才能獲取鎖缰贝。

h:只修改getOne為靜態(tài)同步馍悟,然后兩個(gè)對(duì)象訪問(wèn)打印

public classTestThread8demo {

??? public static void main(String[] args) {

?????? Numbern = new Number();

?????? Numbern2 = new Number();

?????? new Thread(new Runnable(){

?????????? @Override

?????????? public void run() {

????????????? n.getOne();

?????????? }

?????? }).start();

?????? new Thread(new Runnable(){

?????????? @Override

?????????? public void run() {

????????????? n2.getTwo();

?????????? }

?????? }).start();

??? }

}

class Number{

??? public static synchronized void getOne(){

?????? try {

?????????? Thread.sleep(3000);

?????? }catch(InterruptedException e) {

?????? }

?????? System.out.println("One");

??? }

??? public synchronized void getTwo(){

?????? System.out.println("Two");

??? }

}

運(yùn)行結(jié)果:

Two

3秒后……

One

推論:進(jìn)一步印證了靜態(tài)同步方法的鎖是類對(duì)象本身,非靜態(tài)方法的鎖是對(duì)象本身剩晴,兩類鎖不是同一把鎖锣咒。

線程八鎖關(guān)鍵點(diǎn)總結(jié):

I:非靜態(tài)方法的鎖默認(rèn)為? this,? 靜態(tài)方法的鎖為 對(duì)應(yīng)的 Class 實(shí)例。對(duì)象鎖與類鎖不互相干擾赞弥,不會(huì)有競(jìng)態(tài)條件毅整。

Ii:某一個(gè)時(shí)刻內(nèi),只能有一個(gè)線程持有鎖绽左,無(wú)論幾個(gè)方法悼嫉。

9、線程池

第四種獲取線程的方法:線程池拼窥。

提供一個(gè)線程隊(duì)列戏蔑,隊(duì)列中保存著所有等待狀態(tài)的線程蹋凝,避免了創(chuàng)建與銷毀的開(kāi)銷,提高了響應(yīng)的速度总棵。

線程池的體系結(jié)構(gòu):

?* java.util.concurrent.Executor :負(fù)責(zé)線程的使用與調(diào)度的根接口

?*??????? |--**ExecutorService子接口: 線程池的主要接口

?*?????????????? |--ThreadPoolExecutor線程池的實(shí)現(xiàn)類

?*?????????????? |--ScheduledExecutorService子接口:負(fù)責(zé)線程的調(diào)度

?*??????????????????????????? |--ScheduledThreadPoolExecutor:繼承 ThreadPoolExecutor鳍寂, 實(shí)現(xiàn) ScheduledExecutorService

可以看到,雖然ExecutorService下有許多子接口和實(shí)現(xiàn)類情龄,但是這里建議使用Executors來(lái)獲取線程伐割。

工具類: Executors

?* ExecutorServicenewFixedThreadPool() :創(chuàng)建固定大小的線程池

?* ExecutorServicenewCachedThreadPool() :緩存線程池,線程池的數(shù)量不固定刃唤,可以根據(jù)需求自動(dòng)的更改數(shù)量隔心。可自動(dòng)進(jìn)行線程回收尚胞。

?* ExecutorServicenewSingleThreadExecutor() :創(chuàng)建單個(gè)線程池硬霍。線程池中只有一個(gè)線程

?*ScheduledExecutorService newScheduledThreadPool() :創(chuàng)建固定大小的線程,可以延遲或定時(shí)的執(zhí)行任務(wù)笼裳。

示例1:

public classTestThreadPool {

??? public static void main(String[] args) {

?????? //1創(chuàng)建有5個(gè)線程的線程池

?????? ExecutorServiceexecutorService=? Executors.newFixedThreadPool(5);

?????? //執(zhí)行10次線程唯卖,為線程分配任務(wù)

?????? for (int i = 0; i < 10; i++) {

?????????? executorService.submit(new Runnable(){

????????????? @Override

????????????? public void run() {

????????????????? int sum = 0;

????????????????? for (int i = 0; i < 100; i++) {

???????????????????? sum ++;

????????????????? }??

??? ??? System.out.println(Thread.currentThread().getName()+"="+sum);

????????????? }

?????????? });

?????? }

?????? //關(guān)閉線程,executorService.shutdownNow()是立即關(guān)閉

?????? executorService.shutdown();

??? }

}

運(yùn)行結(jié)果:

pool-1-thread-1:1

pool-1-thread-1:3

pool-1-thread-1:4

pool-1-thread-2:0

pool-1-thread-3:7

pool-1-thread-3:9

pool-1-thread-5:94

pool-1-thread-2:98

pool-1-thread-2:99

可以看到始終thread1-5,都用的線程池里的5個(gè)線程躬柬。

示例2:使用帶返回值的Callable接口

public classTestThreadPool {

??? public static void main(String[] args) throwsInterruptedException,ExecutionException {

?????? //1創(chuàng)建有5個(gè)線程的線程池

?????? ExecutorServiceexecutorService=? Executors.newFixedThreadPool(5);

?????? //為線程分配10個(gè)任務(wù)

?????? for(int i=0;i<10;i++){

?????????? Futureresult= executorService.submit(new Callable(){

????????????? @Override

????????????? public Integer call() throws Exception {

????????????????? int count = 0;

????????????????? for(int i=0;i<100;i++){

???????????????????? count++;

????????????????? }

????????????????? return count;

????????????? }??

?????????? });

??? ?????? System.out.println(result.get());????????

?????? }

?????? //關(guān)閉線程拜轨,executorService.shutdownNow()是立即關(guān)閉

?????? executorService.shutdown();

??? }

}

10、線程調(diào)度

即ScheduledExecutorService的使用示例:

public classTestScheduledThreadPool {

??? public static void main(String[] args) throws InterruptedException,

ExecutionException {

?????? ScheduledExecutorServicepool= Executors.newScheduledThreadPool(5);

?????? //3個(gè)參數(shù),第1個(gè)是線程執(zhí)行體,第2個(gè)延時(shí)多久,第3個(gè)延時(shí)單位

?????? for(int i=0;i<3;i++){

?????????? Futureresult= pool.schedule(newCallable(){

????????????? @Override

????????????? public Integer call() throws Exception {

?????? ?????????? int t = newRandom().nextInt();

?????????????????? System.out.println(Thread.currentThread().getName()+"="+t);

????????????????? return t;

????????????? }?????

?????????? },3, TimeUnit.SECONDS);//延時(shí)3秒執(zhí)行

?????? }

?????? pool.shutdown();

??? }

}

運(yùn)算結(jié)果:

3秒后……

pool-1-thread-2=-1843839421

pool-1-thread-3=-1333144512

pool-1-thread-1=1075550443

程序中Future result = pool.schedule(new?Callable(){ 括號(hào)外加輸出語(yǔ)句:

public staticvoidmain(String[] args)throwsInterruptedException, ExecutionException {

?????? ScheduledExecutorServicepool= Executors.newScheduledThreadPool(5);

?????? //3個(gè)參數(shù),第1個(gè)是線程執(zhí)行體,第2個(gè)延時(shí)多久,第3個(gè)延時(shí)單位

?????? for(int i=0;i<3;i++){

?????????? Futureresult= pool.schedule(newCallable(){

????????????? @Override

????????????? public Integer call() throws Exception {

????????????????? int t = newRandom().nextInt();??????? ??? System.out.println(Thread.currentThread().getName()+"="+t);

????????????????? return t;

????????????? }?????

?????????? },3, TimeUnit.SECONDS);//延時(shí)3秒執(zhí)行

?????? ??? System.out.println(Thread.currentThread().getName()+"="+result.get());

?????? }

?????? pool.shutdown();

??? }

運(yùn)行結(jié)果:

3秒后……

pool-1-thread-1=-506702518

main=-506702518

pool-1-thread-1=173222792

main=173222792

pool-1-thread-2=-1115855720

main=-1115855720

發(fā)現(xiàn)pool.schedule體外就是main主線程了允青。

二橄碾、ForkJoinPool分支/合并框架 工作竊取

Fork/Join 框架:就是在必要的情況下,將一個(gè)大任務(wù)颠锉,進(jìn)行拆分(fork)成若干個(gè)小任務(wù)(拆到不可再拆時(shí))法牲,再將一個(gè)個(gè)的小任務(wù)運(yùn)算的結(jié)果進(jìn)行join 匯總。

采用“工作竊取”模式(work-stealing):

當(dāng)執(zhí)行新的任務(wù)時(shí)它可以將其拆分分成更小的任務(wù)執(zhí)行琼掠,并將小任務(wù)加到線程隊(duì)列中拒垃,然后再?gòu)囊粋€(gè)隨機(jī)線程的隊(duì)列中偷一個(gè)并把它放在自己的隊(duì)列中。

相對(duì)于一般的線程池實(shí)現(xiàn)瓷蛙,fork/join框架的優(yōu)勢(shì)體現(xiàn)在對(duì)其中包含的任務(wù)的處理方式上.在一般的線程池中悼瓮,如果一個(gè)線程正在執(zhí)行的任務(wù)由于某些原因無(wú)法繼續(xù)運(yùn)行,那么該線程會(huì)處于等待狀態(tài)艰猬。而在fork/join框架實(shí)現(xiàn)中横堡,如果某個(gè)子問(wèn)題由于等待另外一個(gè)子問(wèn)題的完成而無(wú)法繼續(xù)運(yùn)行。那么處理該子問(wèn)題的線程會(huì)主動(dòng)尋找其他尚未運(yùn)行的子問(wèn)題來(lái)執(zhí)行.這種方式減少了線程的等待時(shí)間姥宝,提高了性能翅萤。

jdk7和jdk8的使用不一樣,現(xiàn)在分別舉例:

首先20億循環(huán)計(jì)算查看耗時(shí):

1731毫秒腊满。

jdk7反面示例1(拆分臨界值太小,導(dǎo)致拆分太多):

public classTestForkJoinPool {

??? public static void main(String[] args) {

?????? long starttime= System.currentTimeMillis();

?????? ForkJoinPoolpool= newForkJoinPool();

?????? ForkJoinTasktask= newForkJoinSumCalculate(0l,2000000000l);

?????? long sum = pool.invoke(task);

?????? long endtime= System.currentTimeMillis();

?????? System.out.println(sum);

?????? System.out.println("---20億循環(huán)計(jì)算耗時(shí):"+(endtime-starttime));

??? }

}

class ForkJoinSumCalculate extends RecursiveTask{

??? private static final long serialVersionUID= -259195479995561737L;

??? private long start;

??? private long end;

??? private static final long THURSHOLD= 10000L;//任務(wù)拆分臨界值(1萬(wàn))

??? public ForkJoinSumCalculate(long start,long end){

?????? this.start = start;

?????? this.end = end;

??? }

??? @Override

??? protected Long compute() {

?????? long length = end -start;

?????? if(length<THURSHOLD){

?????????? long sum = 0L;

?????????? for(long i=start;i<=end;i++){

????????????? sum+=1;

?????????? }

?????????? return sum;

?????? }else{

?????????? long middle = (start-end)/2;

?????????? ForkJoinSumCalculateleft= newForkJoinSumCalculate(start,middle);

?????????? left.fork();//進(jìn)行拆分并壓入線程隊(duì)列

?????????? ForkJoinSumCalculateright= newForkJoinSumCalculate(middle,end);

?????????? right.fork();//進(jìn)行拆分并壓入線程隊(duì)列

?????????? return left.join() + right.join();

?????? }

??? }

}

運(yùn)行結(jié)果:

Exception in thread "main"java.lang.StackOverflowError

?????? atsun.reflect.GeneratedConstructorAccessor1.newInstance(Unknown Source)

?????? at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)

?????? atjava.lang.reflect.Constructor.newInstance(Constructor.java:423)

?????? atjava.util.concurrent.ForkJoinTask.getThrowableException(ForkJoinTask.java:598)

?????? at java.util.concurrent.ForkJoinTask.reportException(ForkJoinTask.java:677)

棧溢出了套么。大家知道內(nèi)存分堆(堆內(nèi)存)和棧(棧內(nèi)存),棧主要存變量及定義碳蛋。我猜測(cè)拆分的變量都放在棧中胚泌,臨界值小,從而拆的多肃弟,所以棧中的變量多玷室,就益處了,那調(diào)大臨界值試試看笤受,從1萬(wàn)調(diào)到100萬(wàn)試試看:

private static final long THURSHOLD = 100000000L;//任務(wù)拆分臨界值100萬(wàn)穷缤,還報(bào)棧內(nèi)存溢出 ,那就改成1000萬(wàn):private static final long THURSHOLD = 1000000000L;//任務(wù)拆分臨界值1000萬(wàn)

不報(bào)錯(cuò)了箩兽,成功拆分津肛,但貌似性能比不拆分慢很多,所以這種任務(wù)拆分不一定能提升性能汗贫,要根據(jù)機(jī)器硬件來(lái)適當(dāng)選擇身坐。

使用jdk1.8示例:

public classTestForkJoinPool {

??? public static void main(String[] args) {

?????? long starttime= System.currentTimeMillis();

?????? Long sum= LongStream.rangeClosed(0L, 2000000000l)

????????????? ?.parallel()

????????????? ?.reduce(0L, Long::sum);

?????? long endtime= System.currentTimeMillis();

?????? System.out.println(sum);

?????? System.out.println("---20億循環(huán)計(jì)算耗時(shí):"+(endtime-starttime));

??? }

}

運(yùn)行結(jié)果:

2000000001000000000

---20億循環(huán)計(jì)算耗時(shí):1620

用了1620毫秒,快了一點(diǎn)落包。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末部蛇,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子咐蝇,更是在濱河造成了極大的恐慌涯鲁,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,324評(píng)論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件有序,死亡現(xiàn)場(chǎng)離奇詭異撮竿,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)笔呀,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,356評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門幢踏,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人许师,你說(shuō)我怎么就攤上這事房蝉。” “怎么了微渠?”我有些...
    開(kāi)封第一講書人閱讀 162,328評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵搭幻,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我逞盆,道長(zhǎng)檀蹋,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書人閱讀 58,147評(píng)論 1 292
  • 正文 為了忘掉前任云芦,我火速辦了婚禮俯逾,結(jié)果婚禮上贸桶,老公的妹妹穿的比我還像新娘。我一直安慰自己桌肴,他們只是感情好皇筛,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,160評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著坠七,像睡著了一般水醋。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上彪置,一...
    開(kāi)封第一講書人閱讀 51,115評(píng)論 1 296
  • 那天拄踪,我揣著相機(jī)與錄音,去河邊找鬼拳魁。 笑死惶桐,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的的猛。 我是一名探鬼主播耀盗,決...
    沈念sama閱讀 40,025評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼卦尊!你這毒婦竟也來(lái)了叛拷?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書人閱讀 38,867評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤岂却,失蹤者是張志新(化名)和其女友劉穎忿薇,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體躏哩,經(jīng)...
    沈念sama閱讀 45,307評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡署浩,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,528評(píng)論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了扫尺。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片筋栋。...
    茶點(diǎn)故事閱讀 39,688評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖正驻,靈堂內(nèi)的尸體忽然破棺而出弊攘,到底是詐尸還是另有隱情,我是刑警寧澤姑曙,帶...
    沈念sama閱讀 35,409評(píng)論 5 343
  • 正文 年R本政府宣布襟交,位于F島的核電站,受9級(jí)特大地震影響伤靠,放射性物質(zhì)發(fā)生泄漏捣域。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,001評(píng)論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望焕梅。 院中可真熱鬧迹鹅,春花似錦、人聲如沸丘侠。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 31,657評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)蜗字。三九已至,卻和暖如春脂新,著一層夾襖步出監(jiān)牢的瞬間挪捕,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 32,811評(píng)論 1 268
  • 我被黑心中介騙來(lái)泰國(guó)打工争便, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留级零,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,685評(píng)論 2 368
  • 正文 我出身青樓滞乙,卻偏偏與公主長(zhǎng)得像奏纪,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子斩启,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,573評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容

  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語(yǔ)法序调,類相關(guān)的語(yǔ)法,內(nèi)部類的語(yǔ)法兔簇,繼承相關(guān)的語(yǔ)法发绢,異常的語(yǔ)法,線程的語(yǔ)...
    子非魚_t_閱讀 31,622評(píng)論 18 399
  • 本文主要講了java中多線程的使用方法垄琐、線程同步边酒、線程數(shù)據(jù)傳遞、線程狀態(tài)及相應(yīng)的一些線程函數(shù)用法狸窘、概述等墩朦。 首先講...
    李欣陽(yáng)閱讀 2,454評(píng)論 1 15
  • 【現(xiàn)代】我想知道的未來(lái)(5) 開(kāi)學(xué),遲到 清晨翻擒,太陽(yáng)剛剛升起氓涣,光芒照射在柏油馬路旁的人行道上,有...
    的人QHS閱讀 223評(píng)論 0 2
  • 我身邊有很多和我曖昧不清的對(duì)象,但好像只是曖昧對(duì)象恩伺。閨密說(shuō)赴背,因?yàn)閷?duì)方?jīng)]有說(shuō)破。是啊,這樣我也不好做出什么行動(dòng)凰荚,...
    90后楠語(yǔ)閱讀 247評(píng)論 2 2
  • 新生大學(xué)今天推送的文章對(duì)我?guī)椭艽笕脊郏獬宋易罱闹械囊恍┮苫蟆? 為什么有些人恐懼孤獨(dú),有些人卻可...
    克洛岱爾情結(jié)閱讀 436評(píng)論 0 1