1、多線程編程方式
- 創(chuàng)建資源類
- 創(chuàng)建資源類
- 資源類里創(chuàng)建同步方法苏章、同步代碼塊
- 高內(nèi)聚低耦合
2寂嘉、為什么需要Synchronized?
編寫一個基本的多線程操作例子
/**
* 給成員變量賦值操作
* 資源類
*/
public class Task {
//成員變量存儲在堆內(nèi)存里面枫绅,多個線程訪問同一個堆內(nèi)存泉孩,
//即多個線程可以同時修改num的值,這樣會導(dǎo)致線程安全問題
private int num=0;
public void changeNum(boolean flag){
if(flag){
num = 88;
System.out.println(Thread.currentThread().getName() + "=====" + "begin");
System.out.println(Thread.currentThread().getName() + "=====" + num);
System.out.println(Thread.currentThread().getName() + "=====" + "over");
}else{
num = 66;
System.out.println(Thread.currentThread().getName() + "=====" + "begin");
System.out.println(Thread.currentThread().getName() + "=====" + num);
System.out.println(Thread.currentThread().getName() + "=====" + "over");
}
}
}
public class SynchronizedTest01 {
public static void main(String[] args) {
//多個線程控制一個對象(一個資源類)
Task task = new Task();
Thread t1 = new Thread(){
public void run(){
task.changeNum(true);
}
};
Thread t2 = new Thread(){
public void run(){
task.changeNum(false);
}
};
t1.start();
t2.start();
}
}
結(jié)果:
Thread-1=====begin
Thread-0=====begin
Thread-1=====66
Thread-0=====66
Thread-1=====over
Thread-0=====over
分析:
可以發(fā)現(xiàn)數(shù)據(jù)跟我們所想的并不一致并淋,兩個線程都打印出num等于66寓搬,這就出現(xiàn)了線程安全的問題,出現(xiàn)這個問題的原因是成員變量存儲在堆內(nèi)存中县耽,兩個線程共享堆內(nèi)存句喷,即兩個線程可以對同一個num進行修改曼尊,如果num定義在方法中,那么就存在棧中脏嚷,線程各自就會在棧中對其修改,則不會出現(xiàn)線程不安全的問題瞒御。
程序執(zhí)行分析:
cpu執(zhí)行t1線程父叙,將num修改為88,之后cpu開始執(zhí)行t2線程肴裙,將num修改為66趾唱,打印出66,cpu開始執(zhí)行t1線程蜻懦,打印num的值甜癞,此時num的值是66。
內(nèi)存圖解:
3宛乃、同步和異步
比如你要給A,B,C三人發(fā)消息
同步:先給A發(fā)悠咱,等A回復(fù)后,再給B發(fā)征炼,等B回復(fù)后析既,再給C發(fā),排隊等待
異步:直接給A,B,C發(fā)消息谆奥,中間不需要等某人回復(fù)之后再給其他人發(fā)消息眼坏,不用排隊等待
要想解決上述線程不安全的問題,可以將方法定義為同步方法
public synchronized void changeNum(boolean flag)
在方法上加入synchronized關(guān)鍵字酸些,這樣在執(zhí)行多個線程時看哪個線程先執(zhí)行這個方法宰译,假設(shè)有t1,t2魄懂,t3三個線程中都調(diào)用了changeNum方法沿侈,t1線程先執(zhí)行了這個方法,那么t1會先在Task對象上面加鎖市栗,加鎖后肋坚,別的線程就無法執(zhí)行當(dāng)前Task對象上的changeNum方法,直到t1執(zhí)行結(jié)束changeNum方法之后肃廓,t2,t3中的一個線程才可以執(zhí)行這個方法智厌,這就保證了在某個時間段內(nèi)只有一個線程執(zhí)行changeNum方法,解決了線程安全問題盲赊。
注意:synchronized鎖住的是當(dāng)前對象铣鹏,如果t1線程和t2線程里面是不同的對象,則不需要同步哀蘑,因為不會發(fā)生線程安全問題诚卸。如下代碼:
public class SynchronizedTest01 {
public static void main(String[] args) {
//創(chuàng)建兩個Task對象
Task task1 = new Task();
Task task2 = new Task();
//兩個線程t1和t2使用的是不同的Task對象葵第,不會發(fā)生線程安全問題
Thread t1 = new Thread(){
public void run(){
task1.changeNum(true);
}
};
Thread t2 = new Thread(){
public void run(){
task2.changeNum(false);
}
};
t1.start();
t2.start();
}
}
//此時并不會出現(xiàn)線程不安全的問題,每個線程執(zhí)行各自的資源類合溺,并不會出現(xiàn)線程不安全的問題