JAVA多線程——多線程編程
【一】線程創建
- Thread創建:
如果要使用Thread來創建線程,需要新建一個類來繼承Thread,重新run方法
class MyThread extends Thread {
/**
* 這是線程任務
*/
@Override
public void run() {
System.out.println("這是新建線程");
}
}
public class Main {
public static void main(String args[]){
//創建線程
MyThread myThread = new MyThread();
//執行線程任務
myThread.start();
}
}
- Runnable
使用runnable需要寫一個實現這個接口的類,但是這種方法是沒返回值的
class MyTask implements Runnable {
/**
* 這是線程任務
*/
@Override
public void run() {
System.out.println("這是新建線程");
}
}
public class Main {
public static void main(String args[]) {
//創建任務
MyTask task = new MyTask();
//創建線程
Thread thread = new Thread(task);
//執行線程任務
thread.start();
}
}
【二】線程狀態轉換
-
1、新建狀態(New):新創建了一個線程對象。
-
2、就緒狀態(Runnable):線程對象創建後,其他線程調用了該對象的start()方法。該狀態的線程位於“可運行線程池”中,變得可運行,只等待獲取CPU的使用權。即在就緒狀態的進程除CPU之外,其它的運行所需資源都已全部獲得。
-
3、運行狀態(Running):就緒狀態的線程獲取了CPU,執行程序代碼。
-
4、阻塞狀態(Blocked):阻塞狀態是線程因爲某種原因放棄CPU使用權,暫時停止運行。直到線程進入就緒狀態,纔有機會轉到運行狀態
-
5、死亡狀態(Dead):線程執行完了或者因異常退出了run()方法,該線程結束生命週期。
詳細線程狀態轉換請移步到博客:https://blog.csdn.net/wenge1477/article/details/90481125
【三】守護線程
只要當前JVM實例中尚存在任何一個非守護線程沒有結束,守護線程就全部工作;只有當最後一個非守護線程結束時,守護線程隨着JVM一同結束工作。Daemon的作用是爲其他線程的運行提供便利服務,守護線程最典型的應用就是 GC (垃圾回收器),它就是一個很稱職的守護者。
java中線程包括兩種:
1、User Thread(用戶線程)
2、Daemon Thread(守護線程)
class MyTask implements Runnable {
@Override
public void run() {
System.out.println("這是新建線程");
}
}
public class Main {
public static void main(String args[]) {
//創建任務
MyTask task = new MyTask();
//創建線程
Thread thread = new Thread(task);
//設置爲守護線程
thread.setDaemon(true);
//執行線程任務
thread.start();
}
}
- // 設定 daemonThread 爲 守護線程,default false(非守護線程)
daemonThread.setDaemon(true);
- // 驗證當前線程是否爲守護線程,返回 true 則爲守護線程
daemonThread.isDaemon();
【四】線程同步
即當有一個線程在對內存進行操作時,其他線程都不可以對這個內存地址進行操作,直到該線程完成操作, 其他線程才能對該內存地址進行操作,而其他線程又處於等待狀態,實現線程同步的方法有很多,臨界區對象就是其中一種
- synchronied
synchronied進行對象鎖,鎖住對象, - ReentranLock
- ReadWriteLock
- wait和notify
- condition
- 阻塞隊列
- CountDownLatch
- CyclicBarrier
【五】線程死鎖
死鎖是指兩個或兩個以上的進程在執行過程中,由於競爭資源或者由於彼此通信而造成的一種阻塞的現象,若無外力作用,它們都將無法推進下去。此時稱系統處於死鎖狀態或系統產生了死鎖,這些永遠在互相等待的進程稱爲死鎖進程。各自持有資源不釋放,確申請獲取對方手中的資源,形成相互等待
-
互斥條件:一個資源每次只能被一個進程使用。
-
請求與保持條件:一個進程因請求資源而阻塞時,對已獲得的資源保持不放。
-
不剝奪條件:進程已獲得的資源,在末使用完之前,不能強行剝奪。
-
循環等待條件:若干進程之間形成一種頭尾相接的循環等待資源關係。
死鎖例子:
public class DeadLockSample extends Thread{
private String first;
private String second;
public DeadLockSample (String name,String first,String second){
super(name);
this.first = first;
this.second = second;
}
public void run(){
synchronized (first){
System.out.println(this.getName() + " obtained:" + first);
try {
Thread.sleep(1000L);
synchronized (second){
System.out.println(this.getName() +" obtained: " + second);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) throws InterruptedException {
String lockA = "lockA";
String lockB = "lockB";
DeadLockSample t1 = new DeadLockSample("Thread1",lockA,lockB);
DeadLockSample t2 = new DeadLockSample("Thread2",lockB,lockA);
t1.start();
t2.start();
t1.join();
t2.join();
}
}
結果:
Thread1 obtained:lockA
Thread2 obtained:lockB
或者
Thread2 obtained:lockB
Thread1 obtained:lockA
預防死鎖的幾種方法:
-
加鎖順序(線程按照一定的順序加鎖)
如果能確保所有的線程都是按照相同的順序獲得鎖,那麼死鎖就不會發生 -
加鎖時限(線程嘗試獲取鎖的時候加上一定的時限,超過時限則放棄對該鎖的請求,並釋放自己佔有的鎖)
在申請資源的時候設置時間限制,當時間到了就不在申請資源,比如lock的tryLocak方法可以設置時間 -
死鎖檢測
【六】synchronied
synchronized關鍵字是java併發編程中必不可少的工具,synchronized是圍繞一個被稱爲內部鎖或監視鎖的內部實體實現的(Api規範裏經常將其簡單的稱之爲“monitor”)。內部鎖在同步的兩個方面發揮作用:強制獨佔訪問對象狀態和建立對可見性必不可少的happens-before關係
- 同步代碼塊
鎖住的是一個對象
1、第一種
public void getCunt(){
synchronized (this){
System.out.println("這是同步代碼塊!!!");
}
}
2、第二種
public synchronized void getCunt() {
System.out.println("這是同步代碼塊!!!");
}
- 同步方法
鎖住的是當前的類的class
1、第一種
public void getCunt() {
synchronized (Main.class) {
System.out.println("這是同步代碼塊!!!");
}
}
2、第二種
public static synchronized void getCunt() {
System.out.println("這是同步代碼塊!!!");
}
【七】wait和notify
public class Main {
public static void main(String args[]){
String lock = new String();
Stack<Integer> stack = new Stack<>();
AtomicInteger i = new AtomicInteger(1);
//生產者生產數據
Thread producer = new Thread(() -> {
synchronized (lock) {
while (stack.isEmpty()) {
stack.push(new Integer(i.getAndIncrement()));
lock.notifyAll();
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
//消費者消費數據
Thread consumer = new Thread(() -> {
synchronized (lock) {
while (!stack.isEmpty()) {
System.out.print(stack.pop()+" ");
lock.notifyAll();
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
producer.start();
consumer.start();
}
}
【八】鏈接
https://blog.csdn.net/J080624/article/details/82721827
https://www.jianshu.com/p/f2c91afe6266