java 多執行緒-2
阿新 • • 發佈:2020-09-12
### 七、執行緒生命週期
沒錯,執行緒也是有生命週期的。就好像人類有出生、兒童、青年、中年、晚年、死亡一般。下面是執行緒的生命週期圖:
![](https://img2020.cnblogs.com/blog/1947461/202009/1947461-20200912160101785-638323818.png)
### 八、執行緒的安全問題
所謂執行緒不安全【併發問題】,舉個例子來說,如賣票,會出現重票、錯票等現象,這就是執行緒不安全的。
> 並行:多個CPU同時執行多個任務。比如:多個人同時做不同的事
>
> 併發:一個CPU(採用時間片)**同時執行多個任務。比如:秒殺、多個人做同一件事**。
如:
~~~java
/**
* 建立三個視窗買票,共100張票。用Runnable介面實現
*/
public class RunMainRunnable {
public static void main(String[] args) {
Window window = new Window();
// 建立三個執行緒
Thread win1 = new Thread(window);
Thread win2 = new Thread(window);
Thread win3 = new Thread(window);
// 設定執行緒的名字
win1.setName("視窗一:");
win2.setName("視窗二:");
win3.setName("視窗三:");
// 啟動執行緒
win1.start();
win2.start();
win3.start();
}
}
class Window implements Runnable{
private int ticket = 100; // 定義100張票
@Override
public void run() {
while (true) {
if (ticket > 0) {
try {
Thread.sleep(100); // 呼叫此方法,讓效果明顯一點
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"獲取到了第"+ticket+"票");
ticket--;
}else{
break;
}
}
}
}
~~~
**執行結果:**出現了重票
![](https://img2020.cnblogs.com/blog/1947461/202009/1947461-20200912160202334-123978959.png)
**問題分析:**
> 之所以會出現重票、錯票等問題,就是因為出現了執行緒不安全。【假設此時ticket=7】在if判斷中,如當視窗一獲得cpu後,首先判斷ticket>0會真,執行列印語句輸出“視窗一:獲取到了第7票”,此時視窗二獲得cpu【注意:視窗一併未執行“ticket--;”,ticket依然為7】,判斷ticket>0為真,執行列印語句輸出“視窗二:獲取到了第7票”,此時視窗三獲得cpu【注意:視窗二並未執行“ticket--;”,ticket依然為7】,判斷ticket>0為真,執行列印語句輸出“視窗三:獲取到了第7票”。便會出現重票。這在實際生活當中,肯定是不允許的,那我們該如何解決執行緒的安全問題呢?
### 九、同步機制
>在Java中,我們通過同步機制,來解決執行緒的安全問題。
>
>**方式一:同步程式碼塊**
>
>~~~java
>synchronized(同步監視器){
> //需要被同步的程式碼
>
>}
>~~~
>
>說明:
>
>1. 什麼是同步的程式碼?
> - 操作共享資料的程式碼,即為需要被同步的程式碼。
>2. 什麼是共享資料?
> - 多個執行緒共同操作的變數。比如:ticket就是共享資料。
>3. 什麼是同步監視器
> - 俗稱:鎖。任何一個類的物件,都可以充當鎖。
> - **要求:多個執行緒必須要共用同一把鎖。**
>
>**方式二:同步方法**
>
> 如果操作共享資料的程式碼完整的宣告在一個方法中,我們不妨將此方法宣告為同步的。
>
>**總結**:
>
>好處:同步的方式,解決了執行緒的安全問題。
>
>壞處:操作同步程式碼時,只能有一個執行緒參與,其他執行緒等待。相當於是一個單執行緒的過程,效率低。 ---侷限性
eg1:方式一:同步程式碼塊
~~~java
/**
* 建立三個視窗買票,共100張票。用Runnable介面實現
*/
public class RunMainRunnable {
public static void main(String[] args) {
Window window = new Window();
// 建立三個執行緒
Thread win1 = new Thread(window);
Thread win2 = new Thread(window);
Thread win3 = new Thread(window);
// 設定執行緒的名字
win1.setName("視窗一:");
win2.setName("視窗二:");
win3.setName("視窗三:");
// 啟動執行緒
win1.start();
win2.start();
win3.start();
}
}
/**
* 方式一;同步程式碼塊
*/
class Window implements Runnable{
private int ticket = 100; // 定義100張票
private Object object = new Object();
@Override
public void run() {
while (true) {
synchronized (object){ // 同步程式碼塊
if (ticket > 0) {
try {
Thread.sleep(100); // 呼叫此方法,讓效果明顯一點
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"獲取到了第"+ticket+"票");
ticket--;
}else{
break;
}
}
}
}
}
~~~
eg2:方式二:同步方法
~~~java
/**
* 建立三個視窗買票,共100張票。用Runnable介面實現
*/
public class RunMainRunnable {
public static void main(String[] args) {
Window window = new Window();
// 建立三個執行緒
Thread win1 = new Thread(window);
Thread win2 = new Thread(window);
Thread win3 = new Thread(window);
// 設定執行緒的名字
win1.setName("視窗一:");
win2.setName("視窗二:");
win3.setName("視窗三:");
// 啟動執行緒
win1.start();
win2.start();
win3.start();
}
}
/*
方式二;同步方法
* */
class Window implements Runnable{
private int ticket = 100; // 定義100張票
@Override
public void run() {
while (true) {
show();// 呼叫同步方法
if (ticket == 0) {
break; // 用來結束迴圈
}
}
}
// 定義一個私有方法
private synchronized void show() { // 加synchronized關鍵字,使其成為一位同步方法。同步監視器或鎖即為:this
if (ticket > 0) {
try {
Thread.sleep(100); // 呼叫此方法,讓效果明顯一點
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"獲取到了第"+ticket+"票");
ticket--;
}
}