文章目錄
- 部落格概述
- 線程安全概念
- 線程安全案例引入
- 同步鎖關鍵字:synchronized
- 多個線程多個鎖
- 對象鎖的同步和異步
部落格概述
程式開發中一個不可避免的問題就是多線程并發通路,為了這種場景特意準備了一個專欄記錄多線程的具體知識與代碼。部落客寫部落格的思路不是長篇大論的寫文字,而是通過代碼去說明問題。通過代碼配合注釋,去了解某種技術。
線程安全概念
當多個線程通路某一個類或者方法時,這個類始終都能表現出正确的行為,那麼這個類就是線程安全的。
線程安全案例引入
import java.util.concurrent.atomic.AtomicInteger;
/**
* 線程安全概念:當多個線程通路某一個類(對象或方法)時,這個對象始終都能表現出正确的行為,那麼這個類(對象或方法)就是線程安全的。
* synchronized:可以在任意對象及方法上加鎖,而加鎖的這段代碼稱為"互斥區"或"臨界區"
* @author alienware
*
*/
public class MyThread extends Thread{
private int count = 5 ;
//synchronized加鎖
public void run(){
count--;
System.out.println(this.currentThread().getName() + " count = "+ count);
}
public static void main(String[] args) {
/**
* 分析:當多個線程通路myThread的run方法時,以排隊的方式進行處理(這裡排對是按照CPU配置設定的先後順序而定的),
* 一個線程想要執行synchronized修飾的方法裡的代碼:
* 1 嘗試獲得鎖
* 2 如果拿到鎖,執行synchronized代碼體内容;拿不到鎖,這個線程就會不斷的嘗試獲得這把鎖,直到拿到為止,
* 而且是多個線程同時去競争這把鎖。(也就是會有鎖競争的問題)
*/
MyThread myThread = new MyThread();
Thread t1 = new Thread(myThread,"t1");
Thread t2 = new Thread(myThread,"t2");
Thread t3 = new Thread(myThread,"t3");
Thread t4 = new Thread(myThread,"t4");
Thread t5 = new Thread(myThread,"t5");
t1.start();
t2.start();
t3.start();
t4.start();
t5.start();
}
}
該程式列印出來的代碼不會按照43210去執行,這就出現了線程問題。
同步鎖關鍵字:synchronized
可以在任意的對象與方法上加鎖,加鎖的這段代碼稱為互斥區,臨界區。需要注意的是,在android開發中,使用api19是無法在對象上進行加鎖的,隻能對方法加鎖。采取的技巧可以是私有化靜态變量,然後對該變量提供公有靜态方法對外開放,使用sync關鍵字修飾,就可以保證某變量的線程安全問題。
對上面的例子隻要在run方法前面加上sync關鍵字就可以實作線程安全。使通路的線程排隊通路,但是效率會降低,而且會産生鎖競争的問題。這個鎖競争問題會讓cpu使用率瞬間激增。這就會導緻應用運作慢,甚至當機。
多個線程多個鎖
/**
* 關鍵字synchronized取得的鎖都是對象鎖,而不是把一段代碼(方法)當做鎖,
* 是以代碼中哪個線程先執行synchronized關鍵字的方法,哪個線程就持有該方法所屬對象的鎖(Lock),
*
* 在靜态方法上加synchronized關鍵字,表示鎖定.class類,類一級别的鎖(獨占.class類)。
* @author alienware
*
*/
public class MultiThread {
private int num = 0;
/** static */
public synchronized void printNum(String tag){
try {
if(tag.equals("a")){
num = 100;
System.out.println("tag a, set num over!");
Thread.sleep(1000);
} else {
num = 200;
System.out.println("tag b, set num over!");
}
System.out.println("tag " + tag + ", num = " + num);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//注意觀察run方法輸出順序
public static void main(String[] args) {
//倆個不同的對象
final MultiThread m1 = new MultiThread();
final MultiThread m2 = new MultiThread();
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
m1.printNum("a");
}
});
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
m2.printNum("b");
}
});
t1.start();
t2.start();
}
對象鎖的同步和異步
/**
* 對象鎖的同步和異步問題
*
*/
public class MyObject {
public synchronized void method1(){
try {
System.out.println(Thread.currentThread().getName());
Thread.sleep(4000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
/** synchronized */
public void method2(){
System.out.println(Thread.currentThread().getName());
}
public static void main(String[] args) {
final MyObject mo = new MyObject();
/**
* 分析:
* t1線程先持有object對象的Lock鎖,t2線程可以以異步的方式調用對象中的非synchronized修飾的方法
* t1線程先持有object對象的Lock鎖,t2線程如果在這個時候調用對象中的同步(synchronized)方法則需等待,也就是同步
*/
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
mo.method1();
}
},"t1");
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
mo.method2();
}
},"t2");
t1.start();
t2.start();
}
}