最近忙著備考大學四年最後的兩科,昨天剛考完大學所有的考試,但是大學專業是機械,但是自己熱衷於IT行業,想往IT行業走,希望畢業後能成功進入到IT行業,只希望畢業能找到一份Java開發工程師的工作,這樣我才能專研和學習到更高深的技術.希望在畢業前能學到一點Java的精髓,讓我可以成功進入到IT行業,加 ...
最近忙著備考大學四年最後的兩科,昨天剛考完大學所有的考試,但是大學專業是機械,但是自己熱衷於IT行業,想往IT行業走,希望畢業後能成功進入到IT行業,只希望畢業能找到一份Java開發工程師的工作,這樣我才能專研和學習到更高深的技術.希望在畢業前能學到一點Java的精髓,讓我可以成功進入到IT行業,加油~
線程問題:
1 package demosummary.threadsafety;
2
3 public class Ticket implements Runnable{
4 //定義票數
5 private int ticket = 100;
6 /**
7 * 執行賣票方法
8 */
9 @Override
10 public void run() {
11 //賣票視窗永久開啟
12 while (true) {
13 //判斷票是否賣完
14 if (ticket > 0) {
15 try {
16 //三秒賣一張票
17 Thread.sleep(3000);
18 } catch (InterruptedException e) {
19 e.printStackTrace();
20 }
21 String name = Thread.currentThread().getName();
22 System.out.println(name+"正在賣:"+ticket--);
23 }
24 }
25 }
26 }
27
28
29
30
31 package demosummary.threadsafety;
32
33 public class TicketTest {
34 public static void main(String[] args) {
35 //創建線程任務對象
36 Ticket ticket = new Ticket();
37 //創建三個視窗對象
38 Thread t1 = new Thread(ticket, "視窗1");
39 Thread t2 = new Thread(ticket, "視窗2");
40 Thread t3 = new Thread(ticket, "視窗3");
41 //啟用視窗來賣票
42 t1.start();
43 t2.start();
44 t3.start();
45 }
46 /**
47 * 執行結果
48 * 視窗2正在賣:79
49 * 視窗3正在賣:79
50 * 視窗1正在賣:-1
51 * 出現重覆賣同一張票和負票,在現實中是不允許出現的
52 */
53 }
註意:執行結果出現兩種問題
1.出現不同視窗賣同一張票,把票賣了兩次
2.出現賣不存在的票
註意:這兩種問題稱為線程不安全
線程安全問題都是由全局變數及靜態變數引起的。若每個線程中對全局變數、靜態變數只有讀操作,而無寫 操作,一般來說,這個全局變數是線程安全的;若有多個線程同時執行寫操作,一般都需要考慮線程同步, 否則的話就可能影響線程安全
線程同步
當我們使用多個線程訪問同一資源的時候,且多個線程中對資源有寫的操作,就容易出現線程安全問題。要解決上述多線程併發訪問一個資源的安全性問題:也就是解決重覆票與不存在票問題,Java中提供了同步機制 (synchronized)來解決
方法:
1. 同步代碼塊
2. 同步方法
3. 鎖機制
同步代碼塊
同步代碼塊: synchronized 關鍵字可以用於方法中的某個區塊中,表示只對這個區塊的資源實行互斥訪問
格式:
synchronized(同步鎖){
需要同步操作的代碼
}
同步鎖:對象的同步鎖只是一個概念,可以想象為在對象上標記了一個鎖
1. 鎖對象 可以是任意類型。
2. 多個線程對象 要使用同一把鎖
同步鎖是誰?
對於非static方法,同步鎖就是this。
對於static方法,我們使用當前方法所在類的位元組碼對象(類名.class)。
註意:在任何時候,最多允許一個線程擁有同步鎖,誰拿到鎖就進入代碼塊,其他的線程只能在外等著 (BLOCKED)
1 package demosummary.threadsafety;
2
3 public class Ticket02 implements Runnable {
4 //定義票數
5 private int ticket = 100;
6
7 Object lock = new Object();
8 /**
9 * 執行賣票方法
10 */
11 @Override
12
13 public void run() {
14 //賣票視窗永久開啟
15 while (true) {
16 synchronized (lock) {
17 //判斷票是否賣完
18 if (ticket > 0) {
19 try {
20 //三秒賣一張票
21 Thread.sleep(3000);
22 } catch (InterruptedException e) {
23 e.printStackTrace();
24 }
25 String name = Thread.currentThread().getName();
26 System.out.println(name + "正在賣:" + ticket--);
27 }
28 }
29 }
30 }
31 }
32
33 package demosummary.threadsafety;
34
35 public class TicketTest02 {
36 public static void main(String[] args) {
37 //創建線程任務對象
38 Ticket02 ticket = new Ticket02();
39 //創建三個視窗對象
40 Thread t1 = new Thread(ticket, "視窗1");
41 Thread t2 = new Thread(ticket, "視窗2");
42 Thread t3 = new Thread(ticket, "視窗3");
43 //啟用視窗來賣票
44 t1.start();
45 t2.start();
46 t3.start();
47 }
48 }
使用了同步代碼塊可以解決線程安全問題
同步方法
同步方法:使用synchronized修飾的方法,就叫做同步方法,保證A線程執行該方法的時候,其他線程只能在方法外等著。
格式:
public synchronized void method(){
可能會產生線程安全問題的代碼
}
1 package demosummary.threadsafety;
2
3 public class Ticket03 implements Runnable {
4 private int ticket = 100;
5
6 /**
7 * 執行賣票方法
8 */
9 @Override
10 public void run() {
11 //賣票視窗永久開啟
12 while (true) {
13 sellTicket();
14 }
15 }
16
17 private synchronized void sellTicket() {
18 //判斷票是否賣完
19 if (ticket > 0) {
20 try {
21 //三秒賣一張票
22 Thread.sleep(3000);
23 } catch (InterruptedException e) {
24 e.printStackTrace();
25 }
26 //獲取線程名稱
27 String name = Thread.currentThread().getName();
28 System.out.println(name + "正在賣:" + ticket--);
29 }
30 }
31 }
32
33
34
35 package demosummary.threadsafety;
36
37 public class TicketTest03 {
38 public static void main(String[] args) {
39 //創建線程任務對象
40 Ticket03 ticket = new Ticket03();
41 //創建三個視窗對象
42 Thread t1 = new Thread(ticket, "視窗1");
43 Thread t2 = new Thread(ticket, "視窗2");
44 Thread t3 = new Thread(ticket, "視窗3");
45 //啟用視窗來賣票
46 t1.start();
47 t2.start();
48 t3.start();
49 }
50 }
Lock鎖
java.util.concurrent.locks.Lock 機制提供了比synchronized代碼塊和synchronized方法更廣泛的鎖定操作, 同步代碼塊/同步方法具有的功能Lock都有,除此之外更強大,更體現面向對象
Lock鎖也稱同步鎖,加鎖與釋放鎖方法化了,如下
public void lock() :加同步鎖。
public void unlock() :釋放同步
1 package demosummary.threadsafety;
2
3 import java.util.concurrent.locks.Lock;
4 import java.util.concurrent.locks.ReentrantLock;
5
6 public class Ticket04 implements Runnable {
7 //定義票數
8 private int ticket = 100;
9
10 //創建鎖對象
11 Lock lock = new ReentrantLock();
12 /**
13 * 執行賣票方法
14 */
15 @Override
16 public void run() {
17 //賣票視窗永久開啟
18 while (true) {
19 //給線程加鎖
20 lock.lock();
21 //判斷是否郵票可賣
22 if (ticket > 0) {
23 try {
24 Thread.sleep(1000);
25 } catch (InterruptedException e) {
26 e.printStackTrace();
27 }
28 //獲取線程當前名稱
29 String name = Thread.currentThread().getName();
30 System.out.println(name+"正在賣"+ticket--);
31 }
32 //釋放線程鎖
33 lock.unlock();
34 }
35 }
36 }
37
38 package demosummary.threadsafety;
39
40 public class TicketTest04 {
41 public static void main(String[] args) {
42 //創建線程任務對象
43 Ticket04 ticket = new Ticket04();
44 //創建三個視窗對象
45 Thread t1 = new Thread(ticket, "視窗1");
46 Thread t2 = new Thread(ticket, "視窗2");
47 Thread t3 = new Thread(ticket, "視窗3");
48 //啟用視窗來賣票
49 t1.start();
50 t2.start();
51 t3.start();
52 }
53 }