Java的面向?qū)ο缶幊?/a>">Java的面向?qū)ο缶幊?/a>
775
2025-04-07
3.1,線程的同步安全
線程安全問題
設(shè)計(jì)并發(fā)編程的目的是為了使程序獲得更高的執(zhí)行效率,但絕不能出現(xiàn)數(shù)據(jù)一致性(數(shù)據(jù)準(zhǔn)確)問題。
如果并發(fā)程序連最基本的執(zhí)行結(jié)果準(zhǔn)確性都無法保證,那并發(fā)編程就沒有任何意義。
為什么會(huì)出現(xiàn)數(shù)據(jù)不正確:
如果一個(gè)資源(變量、對(duì)象、文件、數(shù)據(jù)庫)可以同時(shí)被很多線程使用就會(huì)出現(xiàn)數(shù)據(jù)不一致問題,也就是我們說的線程安全問題。這樣的資源被稱為共享資源或臨界區(qū)。
public class Demo24 implements Runnable { private int m = 0; @Override public void run() { for (int i = 0; i < 10000; i++) { m++; } } public static void main(String[] args) { Demo24 run = new Demo24(); Thread thread1 = new Thread(run); Thread thread2 = new Thread(run); thread1.start(); thread2.start(); try { //join()使main線程等待這兩個(gè)線程執(zhí)行結(jié)束后繼續(xù)執(zhí)行下面 thread1.join(); thread2.join(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("m的最終值為:" + run.m); /* * 輸出結(jié)果 * m的最終值為:20000 * */ } }
互斥訪問之synchronized:
互斥鎖:顧名思義,就是互斥訪問目的的鎖。
如果對(duì)臨界資源加上互斥鎖,當(dāng)一個(gè)線程在訪問該臨界資源時(shí),其他線程便只能等待。
在Java中,每一個(gè)對(duì)象都擁有一個(gè)鎖標(biāo)記(monitor),也稱為監(jiān)視器,多線程同時(shí)訪問某個(gè)對(duì)象時(shí),只有擁有該對(duì)象鎖的線程才能訪問。
3.2,線程的同步方法和同步塊
同步代碼塊
同步塊的根本的目的,是控制競爭資源的正確的安全訪問,因此只要在訪問競爭資源的時(shí)候保證同一時(shí)刻只能一個(gè)線程訪問即可,所以Java引入了同步代碼塊的策略,以提高性能。
synchronized(obj) { 同步代碼塊 }
obj叫做同步監(jiān)視器(即鎖對(duì)象),任何線程進(jìn)入下面同步代碼塊之前必須先獲得對(duì)obj的鎖;其他線程無法獲得鎖。
鎖對(duì)象可以是任意對(duì)象,但必須保證是同一對(duì)象任何時(shí)刻只能有一個(gè)線程可以獲得對(duì)同步監(jiān)視器的鎖定。當(dāng)同步代碼塊執(zhí)行完成后該線程會(huì)釋放對(duì)該同步監(jiān)視器的鎖定。
同步方法和同步代碼塊的區(qū)別
/*同步方法*/ class UserRunn implements Runnable { @Override public synchronized void run() { System.out.println("歡迎執(zhí)行"); for (int i = 0; i < 3; i++) { System.out.println(Thread.currentThread().getName() + "," + i); } System.out.println("結(jié)束了,謝謝"); } } /* * 同步塊 * */ class UserRunn1 implements Runnable { @Override public void run() { System.out.println("歡迎執(zhí)行"); synchronized (this) { for (int i = 0; i < 3; i++) { System.out.println(Thread.currentThread().getName() + "," + i); } } System.out.println("結(jié)束了,謝謝"); } } public class Demo25 { public static void main(String[] args) { //UserRunn ur = new UserRunn(); UserRunn1 ur = new UserRunn1(); Thread t1 = new Thread(ur); Thread t2 = new Thread(ur); t1.start(); t2.start(); } /* * 同步方法輸出結(jié)果 * 歡迎執(zhí)行 Thread-0,0 Thread-0,1 Thread-0,2 結(jié)束了,謝謝 歡迎執(zhí)行 Thread-1,0 Thread-1,1 Thread-1,2 結(jié)束了,謝謝 * */ /* * 同步塊輸出結(jié)果 * 歡迎執(zhí)行 Thread-0,0 Thread-0,1 Thread-0,2 歡迎執(zhí)行 結(jié)束了,謝謝 Thread-1,0 Thread-1,1 Thread-1,2 結(jié)束了,謝謝 * */ }
3.3,線程的死鎖
當(dāng)一個(gè)線程永遠(yuǎn)地持有一個(gè)鎖,并且其他線程都嘗試去獲得這個(gè)鎖時(shí),那么它們將永遠(yuǎn)被阻塞。
如果線程A持有鎖L并且想獲得鎖M,線程B持有鎖M并且想獲得鎖L,那么這兩個(gè)線程將永遠(yuǎn)等待下去,這種情況就是最簡單的死鎖形式。
死鎖的案例
class DeadLock { private final Object left = new Object(); private final Object right = new Object(); public void leftRight() throws Exception { synchronized (left) { //一定要有"Thread.sleep(2000)"讓線程休眠,不然一個(gè)線程運(yùn)行了,另一個(gè)線程還沒有運(yùn)行 //先運(yùn)行的線程很有可能就已經(jīng)連續(xù)獲得兩個(gè)鎖了 Thread.sleep(2000); synchronized (right) { System.out.println("leftRight end!"); } } } public void rightLeft() throws Exception { synchronized (left) { //一定要有"Thread.sleep(2000)"讓線程休眠,不然一個(gè)線程運(yùn)行了,另一個(gè)線程還沒有運(yùn)行 //先運(yùn)行的線程很有可能就已經(jīng)連續(xù)獲得兩個(gè)鎖了 Thread.sleep(2000); synchronized (left) { System.out.println("rightLeft end!"); } } } } class Thread0 extends Thread { private DeadLock d1; public Thread0(DeadLock d1) { this.d1 = d1; } @Override public void run() { try { d1.leftRight(); } catch (Exception e) { e.printStackTrace(); } } } class Thread1 extends Thread { private DeadLock d1; public Thread1(DeadLock d1) { this.d1 = d1; } @Override public void run() { try { d1.rightLeft(); } catch (Exception e) { e.printStackTrace(); } } } public class Test { public static void main(String[] args) { DeadLock d1 = new DeadLock(); Thread0 t0 = new Thread0(d1); Thread1 t1 = new Thread1(d1); t0.start(); t1.start(); } }
3.4,線程的明鎖
在Java5種,專門提供了鎖對(duì)象Lock,利用鎖可以方便的實(shí)現(xiàn)資源的封鎖,用來對(duì)競爭資源并發(fā)訪問的控制。
Lock所有加鎖和解鎖的方法都是顯式的。
Lock.lock():獲取鎖
Lock.unlock():釋放鎖
Lock可以構(gòu)建公平鎖和非公平鎖,默認(rèn)是非公平鎖。
Lock與synchronized對(duì)比:
Lock不是Java語言內(nèi)置的,synchronized是Java語言的關(guān)鍵字,因此是內(nèi)置特性。Lock是一個(gè)類,通過這個(gè)類可以實(shí)現(xiàn)同步訪問。
synchronized不需要手動(dòng)釋放鎖,當(dāng)synchronized方法或者synchronized代碼塊執(zhí)行完之后,系統(tǒng)會(huì)自動(dòng)讓線程釋放對(duì)鎖的占用;而Lock則必須要用戶去手動(dòng)釋放鎖,如果沒有主動(dòng)釋放鎖,就有可能導(dǎo)致出現(xiàn)死鎖現(xiàn)象。
3.5,線程的公平鎖和非公平鎖
Java的ReenTrantLock也就是用隊(duì)列實(shí)現(xiàn)的公平鎖和非公平鎖:
在公平的鎖中,如果有另一個(gè)線程持有鎖或者有其他線程在等待隊(duì)列中等待這個(gè)鎖,那么新發(fā)出的請(qǐng)求的線程將被放入到隊(duì)列中。
而非公平鎖上,只有當(dāng)鎖被某個(gè)線程持有時(shí),新發(fā)出請(qǐng)求的線程才會(huì)被放入隊(duì)列中(此時(shí)和公平鎖是一樣的)。所以,它們的差別在于非公平鎖會(huì)有更多的機(jī)會(huì)去搶占鎖。
Java的ReenTrantLock的ReenTrantLock(true),表示公平鎖:
import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; class UserRunn implements Runnable { //長途汽車票是10 private int count = 10; //線程運(yùn)行退出標(biāo)識(shí)位 private boolean flag = true; private Lock lock; public UserRunn(Lock lock) { this.lock = lock; } @Override public void run() { System.out.println(Thread.currentThread().getName() + ",歡迎訂票"); while (flag) { lock.lock(); System.out.println(Thread.currentThread().getName() + ",獲取到了第" + count-- + "票,恭喜你獲取票成功"); if (count <= 1) { flag = false; } lock.unlock(); } System.out.println(Thread.currentThread().getName() + ",謝謝你"); } } /* * 公平鎖 * */ public class Test { public static void main(String[] args) { //true:公平鎖 false:非公平鎖 ReentrantLock lock = new ReentrantLock(true); UserRunn ur = new UserRunn(lock); Thread t1 = new Thread(ur, "北門汽車站"); Thread t2 = new Thread(ur, "南門汽車站"); Thread t3 = new Thread(ur, "東門汽車站"); t1.start(); t2.start(); t3.start(); /* * 獲取鎖的線程順序正是線程啟動(dòng)的順序 * 北門汽車站,歡迎訂票 東門汽車站,歡迎訂票 南門汽車站,歡迎訂票 北門汽車站,獲取到了第10票,恭喜你獲取票成功 東門汽車站,獲取到了第9票,恭喜你獲取票成功 南門汽車站,獲取到了第8票,恭喜你獲取票成功 北門汽車站,獲取到了第7票,恭喜你獲取票成功 東門汽車站,獲取到了第6票,恭喜你獲取票成功 南門汽車站,獲取到了第5票,恭喜你獲取票成功 北門汽車站,獲取到了第4票,恭喜你獲取票成功 東門汽車站,獲取到了第3票,恭喜你獲取票成功 南門汽車站,獲取到了第2票,恭喜你獲取票成功 南門汽車站,謝謝你 北門汽車站,獲取到了第1票,恭喜你獲取票成功 北門汽車站,謝謝你 東門汽車站,獲取到了第0票,恭喜你獲取票成功 東門汽車站,謝謝你 * */ } }
Java的ReenTrantLock的ReenTrantLock(false),表示非公平鎖:
import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; class UserRunn implements Runnable { //長途汽車票是10 private int count = 10; //線程運(yùn)行退出標(biāo)識(shí)位 private boolean flag = true; private Lock lock; public UserRunn(Lock lock) { this.lock = lock; } @Override public void run() { System.out.println(Thread.currentThread().getName() + ",歡迎訂票"); while (flag) { lock.lock(); System.out.println(Thread.currentThread().getName() + ",獲取到了第" + count-- + "票,恭喜你購票成功"); if (count <= 1) { flag = false; } lock.unlock(); } System.out.println(Thread.currentThread().getName() + ",謝謝你"); } } /* * 非公平鎖,對(duì)鎖的獲取是亂序的既有一個(gè)搶占鎖的過程 * */ public class Test { public static void main(String[] args) { //true:公平鎖 false:非公平鎖 ReentrantLock lock = new ReentrantLock(false); UserRunn ur = new UserRunn(lock); Thread t1 = new Thread(ur, "北門汽車站"); Thread t2 = new Thread(ur, "南門汽車站"); Thread t3 = new Thread(ur, "東門汽車站"); t1.start(); t2.start(); t3.start(); /* * 輸出結(jié)果 * 北門汽車站,歡迎訂票 南門汽車站,歡迎訂票 北門汽車站,獲取到了第10票,恭喜你購票成功 東門汽車站,歡迎訂票 北門汽車站,獲取到了第9票,恭喜你購票成功 北門汽車站,獲取到了第8票,恭喜你購票成功 北門汽車站,獲取到了第7票,恭喜你購票成功 北門汽車站,獲取到了第6票,恭喜你購票成功 北門汽車站,獲取到了第5票,恭喜你購票成功 北門汽車站,獲取到了第4票,恭喜你購票成功 北門汽車站,獲取到了第3票,恭喜你購票成功 北門汽車站,獲取到了第2票,恭喜你購票成功 北門汽車站,謝謝你 南門汽車站,獲取到了第1票,恭喜你購票成功 南門汽車站,謝謝你 東門汽車站,獲取到了第0票,恭喜你購票成功 東門汽車站,謝謝你 * */ } }
Java 多線程
版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實(shí)的內(nèi)容,請(qǐng)聯(lián)系我們jiasou666@gmail.com 處理,核實(shí)后本網(wǎng)站將在24小時(shí)內(nèi)刪除侵權(quán)內(nèi)容。
版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實(shí)的內(nèi)容,請(qǐng)聯(lián)系我們jiasou666@gmail.com 處理,核實(shí)后本網(wǎng)站將在24小時(shí)內(nèi)刪除侵權(quán)內(nèi)容。