Java併發編程原理與實戰一(線程狀態及創建線程的多種方式)

一、為什麼要學習併發編程

1.發揮多處理的強大能力

2.建模的簡單性

3.異步事件的簡化處理

4.響應更加靈敏的用戶界面

二、併發的缺點

1.安全性問題

多線程環境下

多個線程共享一個資源

對資源進行非原子性操作

2.活躍性問題(飢餓)

1、死鎖

2、飢餓

飢餓與公平

1)高優先級吞噬所有低優先級的CPU時間片

2)線程被永久堵塞在一個等待進入同步塊的狀態

3)等待的線程永遠不被喚醒

如何儘量避免飢餓問題

  • 設置合理的優先級
  • 使用鎖來代替synchronized

3、活鎖

3.性能問題

三、線程的狀態

線程在一定條件下,狀態會發生變化。線程一共有以下幾種狀態:

1、新建狀態(New):新創建了一個線程對象。

2、就緒狀態(Runnable):線程對象創建後,其他線程調用了該對象的start()方法。該狀態的線程位於“可運行線程池”中,變得可運行,只等待獲取CPU的使用權。即在就緒狀態的進程除CPU之外,其它的運行所需資源都已全部獲得。

3、運行狀態(Running):就緒狀態的線程獲取了CPU,執行程序代碼。

4、阻塞狀態(Blocked):阻塞狀態是線程因為某種原因放棄CPU使用權,暫時停止運行。直到線程進入就緒狀態,才有機會轉到運行狀態。

阻塞的情況分三種:

(1)、等待阻塞:運行的線程執行wait()方法,該線程會釋放佔用的所有資源,JVM會把該線程放入“等待池”中。進入這個狀態後,是不能自動喚醒的,必須依靠其他線程調用notify()或notifyAll()方法才能被喚醒,

(2)、同步阻塞:運行的線程在獲取對象的同步鎖時,若該同步鎖被別的線程佔用,則JVM會把該線程放入“鎖池”中。

(3)、其他阻塞:運行的線程執行sleep()或join()方法,或者發出了I/O請求時,JVM會把該線程置為阻塞狀態。當sleep()狀態超時、join()等待線程終止或者超時、或者I/O處理完畢時,線程重新轉入就緒狀態。

5、死亡狀態(Dead):線程執行完了或者因異常退出了run()方法,該線程結束生命週期。

線程變化的狀態轉換圖如下:


Java併發編程原理與實戰一(線程狀態及創建線程的多種方式)


四、創建線程的多種方式

1、繼承Thread類

public class Demo1 extends Thread {
public Demo1(String name) {
super(name);
}
@Override
public void run() {
while(!interrupted()) {
System.out.println(getName() + "線程執行了 .. ");
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
Demo1 d1 = new Demo1("first-thread");
Demo1 d2 = new Demo1("second-thread");
d1.start();
d2.start();
// d1.stop();
d1.interrupt();
}
}

2、實現Runnable接口

public class Demo2 implements Runnable {
@Override
public void run() {
while(true) {
System.out.println("thread running ...");

}
}
public static void main(String[] args) {
Thread thread = new Thread(new Demo2());
thread.start();
}
}

3、匿名內部類的方式

public class Demo3 {
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("runnable");
}
}) {
public void run() {
System.out.println("sub");
};
}.start();
}
}

4、帶返回值的線程

import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
public class Demo4 implements Callable<integer> {
public static void main(String[] args) throws Exception {
Demo4 d = new Demo4();
FutureTask<integer> task = new FutureTask<>(d);
Thread t = new Thread(task);
t.start();
System.out.println("我先乾點別的。。。");
Integer result = task.get();
System.out.println("線程執行的結果為:" + result);
}
@Override
public Integer call() throws Exception {

System.out.println("正在進行緊張的計算....");
Thread.sleep(3000);
return 1;
}
}
/<integer>/<integer>

5、定時器(quartz)

import java.util.Timer;
import java.util.TimerTask;
public class Demo5 {
public static void main(String[] args) {
Timer timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
// 實現定時任務
System.out.println("timertask is run");
}
}, 0, 1000);
}
}

6、線程池的實現

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* 線程池
* @author Administrator
*
*/
public class Demo6 {
public static void main(String[] args) {
ExecutorService threadPool = Executors.newCachedThreadPool();
for (int i = 0; i < 1000; i++) {
threadPool.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}

});
}
threadPool.shutdown();
}
}

7、Lambda表達式實現

import java.util.Arrays;
import java.util.List;
/**
* lambda並行計算
* @author Administrator
*
*/
public class Demo7 {
public static void main(String[] args) {
List<integer> values = Arrays.asList(10,20,30,40);
int res = new Demo7().add(values);
System.out.println("計算的結果為:" + res);
}
public int add (List<integer> values) {
values.parallelStream().forEach(System.out :: println);
return values.parallelStream().mapToInt( i -> i * 2).sum();
}
}
/<integer>/<integer>

8、Spring實現多線程

五、Synchronized原理與使用

1、內置鎖

2、互斥鎖

1、修飾普通方法

2、修飾靜態方法

3、修飾代碼塊

public class Sequence {
private int value;
/**
* synchronized 放在普通方法上,內置鎖就是當前類的實例
* @return
*/
public synchronized int getNext() {
return value ++;
}
/**
* 修飾靜態方法,內置鎖是當前的Class字節碼對象
* Sequence.class
* @return
*/
public static synchronized int getPrevious() {
// return value --;
return 0;
}
public int xx () {
// monitorenter
synchronized (Sequence.class) {
if(value > 0) {
return value;
} else {
return -1;
}
}
// monitorexit
}
public static void main(String[] args) {
Sequence s = new Sequence();
// while(true) {
// System.out.println(s.getNext());
// }
new Thread(new Runnable() {
@Override
public void run() {
while(true) {
System.out.println(Thread.currentThread().getName() + " " + s.getNext());
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();

}
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
while(true) {
System.out.println(Thread.currentThread().getName() + " " + s.getNext());
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
while(true) {
System.out.println(Thread.currentThread().getName() + " " + s.getNext());
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
}
}

六、任何對象都可以作為鎖,那麼鎖信息又存在對象的什麼地方呢?

存在對象頭中

對象頭中的信息

Mark Word:線程id、Epoch、對象的分代年齡信息、是否是偏向鎖、鎖標誌位

Class Metadata Address

Array Length

偏向鎖

每次獲取鎖和釋放鎖會浪費資源

很多情況下,競爭鎖不是由多個線程,而是由一個線程在使用。

只有一個線程在訪問同步代碼塊的場景

重量級鎖

七、設置線程優先級

public class Target implements Runnable {
@Override
public void run() {
while(true) {
System.out.println(Thread.currentThread().getName() + " ...");
// Thread.sleep(1);
}
}
}
public class Demo {
public static void main(String[] args) {
Thread t1 = new Thread(new Target());
Thread t2 = new Thread(new Target());
t1.setPriority(1);

t2.setPriority(Thread.MIN_PRIORITY);
t1.start();
t2.start();
}
}

八、單例模式與線程安全性問題

餓漢式

沒有線程安全性問題

public class Singleton {
// 私有化構造方法
private Singleton () {}
private static Singleton instance = new Singleton();
public static Singleton getInstance() {
return instance;
}
}

懶漢式

雙重檢查加鎖解決線程安全性問題

public class Singleton2 {
private Singleton2() {}
//volatile 解決指令重排序導致的線程安全性問題、過多將導致cpu緩存優化失效
private static volatile Singleton2 instance;
/**
* 雙重檢查加鎖
*

* @return
*/
public static Singleton2 getInstance () {
// 自旋 while(true)
if(instance == null) {
synchronized (Singleton2.class) {
if(instance == null) {
instance = new Singleton2(); // 指令重排序
// 申請一塊內存空間 // 1
// 在這塊空間裡實例化對象 // 2
// instance的引用指向這塊空間地址 // 3
}
}
}
return instance;
}
}

九、鎖重入

public class Demo {
public synchronized void a () {
System.out.println("a");
// b();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public synchronized void b() {
System.out.println("b");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static void main(String[] args) {
//同一個對對象將會阻塞
Demo d1= new Demo();

Demo d2= new Demo();
new Thread(new Runnable() {
@Override
public void run() {
d1.a();
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
d2.b();
}
}).start();
}
}

十、自旋鎖

import java.util.Random;
/**
* 多個線程執行完畢之後,打印一句話,結束
* @author worker
*
*/
public class Demo2 {
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " 線程執行...");
try {
Thread.sleep(new Random().nextInt(2000));
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " 線程執行完畢了...");
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " 線程執行...");
try {
Thread.sleep(new Random().nextInt(2000));

} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " 線程執行完畢了...");
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " 線程執行...");
try {
Thread.sleep(new Random().nextInt(2000));
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " 線程執行完畢了...");
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " 線程執行...");
try {
Thread.sleep(new Random().nextInt(2000));
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " 線程執行完畢了...");
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " 線程執行...");
try {
Thread.sleep(new Random().nextInt(2000));
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " 線程執行完畢了...");
}
}).start();
while(Thread.activeCount() != 1) {
// 自旋
}
System.out.println("所有的線程執行完畢了...");

}
}

十一、死鎖

public class Demo3 {
private Object obj1 = new Object();
private Object obj2 = new Object();
public void a () {
synchronized (obj1) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (obj2) {
System.out.println("a");
}
}
}
public void b () {
synchronized (obj2) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (obj1) {
System.out.println("b");
}
}
}
public static void main(String[] args) {
Demo3 d = new Demo3();
new Thread(new Runnable() {
@Override
public void run() {
d.a();
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
d.b();
}
}).start();
}

}

十二、輕量級鎖

Volatile

Volatile稱之為輕量級鎖,被volatile修飾的變量,在線程之間是可見的。

可見:一個線程修改了這個變量的值,在另外一個線程中能夠讀到這個修改後的值。

Synchronized除了線程之間互斥意外,還有一個非常大的作用,就是保證可見性

public class Demo2 {
public volatile boolean run = false;
public static void main(String[] args) {
Demo2 d = new Demo2();
new Thread(new Runnable() {
@Override
public void run() {
for(int i = 1;i<=10;i++) {
System.err.println("執行了第 " + i + " 次");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
d.run = true;
}
}).start();
new Thread(new Runnable() {

@Override
public void run() {
while(!d.run) {
// 不執行
}
System.err.println("線程2執行了...");
}
}).start();
}
}

Lock指令

在多處理器的系統上

1、將當前處理器緩存行的內容寫回到系統內存

2、這個寫回到內存的操作會使在其他CPU裡緩存了該內存地址的數據失效

硬盤 – 內存 – CPU的緩存

多個線程可以同時

十三、JDK提供的原子類原理及使用

1、原子更新基本類型、原子更新數組、原子更新抽象類型、原子更新字段

public class User {
private String name;
public volatile int old;

public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getOld() {
return old;
}
public void setOld(int old) {
this.old = old;
}
}
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicIntegerArray;
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
import java.util.concurrent.atomic.AtomicReference;
public class Sequence {
private AtomicInteger value = new AtomicInteger(0);
private int [] s = {2,1,4,6};
AtomicIntegerArray a = new AtomicIntegerArray(s);
AtomicReference<user> user = new AtomicReference<>();
AtomicIntegerFieldUpdater<user> old = AtomicIntegerFieldUpdater.newUpdater(User.class, "old");
/**
* @return
*/
public int getNext() {
User user = new User();
System.out.println(old.getAndIncrement(user));
System.out.println(old.getAndIncrement(user));
System.out.println(old.getAndIncrement(user));
a.getAndIncrement(2);
a.getAndAdd(2, 10);
return value.getAndIncrement();
}
public static void main(String[] args) {
Sequence s = new Sequence();
new Thread(new Runnable() {
@Override
public void run() {
// while(true) {
System.out.println(Thread.currentThread().getName() + " " + s.getNext());
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
// }
}
}).start();

}
}
/<user>/<user>

十四、Lock接口的認識與使用

Lock與Synchronized的區別:

Lock需要顯示地獲取和釋放鎖,繁瑣能讓代碼更靈活

Synchronized不需要顯示地獲取和釋放鎖,簡單

Lock的優勢:

使用Lock可以方便的實現公平性

非阻塞的獲取鎖

能被中斷的獲取鎖

超時獲取鎖

自己實現一個Lock

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Sequence {
private int value;
Lock lock = new ReentrantLock();
Lock l1 = new ReentrantLock();
/**
* @return
*/

public int getNext() {
lock.lock();
int a = value ++;
lock.unlock();
return a;
}
public static void main(String[] args) {
Sequence s = new Sequence();
new Thread(new Runnable() {
@Override
public void run() {
while(true) {
System.out.println(Thread.currentThread().getName() + " " + s.getNext());
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
while(true) {
System.out.println(Thread.currentThread().getName() + " " + s.getNext());
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
while(true) {
System.out.println(Thread.currentThread().getName() + " " + s.getNext());
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
}
}

最後針對於互聯網公司java程序員涉及到的絕大部分難題我做成了文檔和架構視頻資料免費分享給大家(包括Dubbo、Redis、Netty、zookeeper、Spring cloud、分佈式、高併發等架構技術資料),希望能幫助到且找到一個好的工作,也節省大家在網上搜索資料的時間來學習,也可以關注我一下以後會有更多幹貨分享。

資料領取方式:

關注+轉發後,私信關鍵詞 【資料或者java】免費獲取!

重要的事情說三遍,轉發、轉發、轉發後再發私信,才可以拿到!

Java併發編程原理與實戰一(線程狀態及創建線程的多種方式)


分享到:


相關文章: