(java)多執行緒(二)
阿新 • • 發佈:2018-12-22
【同步程式碼塊】-- 解決執行緒安全問題
/* * 同步程式碼塊解決執行緒安全問題 * 同步程式碼塊公式: * synchronized(任意物件){ * 執行緒要操作的共享資料 * } * 任意物件又稱:同步鎖 、物件監視器 obj * 作用:保證同步安全性,沒有鎖的執行緒不能執行同步程式碼塊,只能等 * 原理:執行緒遇到同步程式碼塊時,判斷程式碼塊的同步鎖還有沒有 * 1. 若有鎖,獲取鎖,進入同步程式碼塊執行程式 執行完畢後 出了程式碼塊 執行緒再將鎖還回去 * 2. 若沒有鎖,執行緒只能等待,不能進入同步程式碼塊 */
/* * 同步程式碼塊解決執行緒安全問題 * 同步程式碼塊公式: * synchronized(任意物件){ * 執行緒要操作的共享資料 * } */ public class Tickets implements Runnable{ // 定義出售票的數目 private int tickets = 100; private Object obj = new Object(); public void run(){ while(true){ // 為了保證共享資料的安全,加入同步程式碼塊 synchronized (obj) { // 對票數進行判斷,大於0可以出售,進行變數--操作 if(tickets >0){ try{ Thread.sleep(100); // 此處執行緒休眠後,會出現執行緒安全問題。售票數會出現負數 }catch(Exception ex){} System.out.println(Thread.currentThread().getName()+"出售第"+tickets--); } } } } } /* * 多執行緒售票案例:多執行緒併發訪問同一資料資源 * 本場電影只有100張票,使用多執行緒模擬視窗同時賣票 * 即三個執行緒,出售一個票資源 */ public class ThreadDemo1 { public static void main(String[] args) { // 建立Runnable介面實現類物件 Tickets t = new Tickets(); // 建立3個Thread類物件,傳遞Runnable介面實現類 Thread t0 = new Thread(t); Thread t1 = new Thread(t); Thread t2 = new Thread(t); t0.start();t1.start();t2.start(); } }
【同步方法】
/* * 採用同步方法形式,解決執行緒的安全問題 好處:程式碼簡潔 * 步驟: * 1. 將執行緒共享資料和同步,抽取到一個方法中去 * 2. 在方法宣告上加上同步關鍵字 synchronized() * 問題: * 同步方法中的物件鎖,是本類引用物件this * 如果方法是靜態的,物件鎖是 本類.class (Tickets2.class) */ public class Tickets2 implements Runnable{ // 定義出售票的數目 private int tickets = 100; public void run(){ while(true){ payTicket(); } } public synchronized void payTicket(){ // 對票數進行判斷,大於0可以出售,進行變數--操作 if(tickets >0){ try{ Thread.sleep(100); // 此處執行緒休眠後,會出現執行緒安全問題。售票數會出現負數 }catch(Exception ex){} System.out.println(Thread.currentThread().getName()+"出售第"+tickets--); } } }
【使用Lock介面】
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/*
* 使用Lock介面,解決執行緒安全問題
* Lock介面方法:
* 1. lock()獲取鎖
* 2. unlock()釋放鎖
* 介面實現類ReentrantLock();
*/
public class TicketsLock1 implements Runnable {
private int tickets = 100;
// 建立Lock介面的實現類物件 ,宣告在成員變數位置
private Lock l = new ReentrantLock();
@Override
public void run() {
while(true){
// 呼叫Lock介面方法lock()獲取鎖
l.lock();
if(tickets >0){
try{
Thread.sleep(10);
System.out.println(Thread.currentThread().getName()+"出售第"+tickets--);
}catch(Exception e){
}finally{
// 呼叫方法unlock() 釋放鎖
l.unlock();
}
}
}
}
}
【死鎖】
// 類LockA 物件充當鎖
public class LockA {
// 私有構造方法,類外不能new
private LockA(){}
// 一個靜態成員變數 是自己的物件 外界可以通過類名呼叫靜態變數,拿到這個物件
public static final LockA locka = new LockA();
}
//類LockB 物件充當鎖
public class LockB {
// 私有構造方法,類外不能new
private LockB(){}
// 一個靜態成員變數 是自己的物件 外界可以通過類名呼叫靜態變數,拿到這個物件
public static final LockB lockb = new LockB();
}
// Runnable 介面實現類
// 類LockA、LockB 建立物件鎖
public class DeadLock1 implements Runnable {
private int i = 0;
public void run(){
while(true){
if(i % 2 == 0){
// 先進A同步,再進B同步
synchronized (LockA.locka) {
System.out.println("if...Locka");
synchronized (LockB.lockb) {
System.out.println("if...Lockb");
}
}
}else{
// 先進B同步,再進A同步
synchronized (LockB.lockb) {
System.out.println("else...Lockb");
synchronized (LockA.locka) {
System.out.println("else...Locka");
}
}
}
i++;
}
}
}
/*
* 執行緒的死鎖程式碼實現:
* LockA、LockB類,構造方法私有了不能new,需要通過類名呼叫靜態方法建立物件
*/
public class ThreadDemo1 {
public static void main(String[] args) {
//
DeadLock1 dead = new DeadLock1();
Thread t0 = new Thread(dead);
Thread t1 = new Thread(dead);
t0.start();
t1.start();
/*
if...Locka
if...Lockb
else...Lockb
if...Locka
執行後,程式進入死鎖
*/
}
}
【等待與喚醒機制】
執行緒之間的通訊:多個執行緒在處理同一個資源,處理的任務卻不相同。而通過一定的手段使各個執行緒能夠有效的利用資源,這種手段即 ---- 等待喚醒機制。
wait():等待。將正執行的執行緒釋放其執行資格和執行權,並存儲到執行緒池中。
notify():喚醒。喚醒執行緒池中被wait()的執行緒。一次喚醒一個,而且是任一的。
notifyAll():喚醒全部。可以將執行緒池中的所有wait()執行緒都喚醒。
/*
* 輸入執行緒:對Resource資源物件中的成員變數進行賦值
* 張三 男 wang w 進行輪流賦值
*/
public class InpputDemo1 implements Runnable {
private Resource r ;
public InpputDemo1(Resource r){
this.r = r;
}
public void run(){
int i =0;
while(true){
synchronized (r) {
// 若flag為true,等待
if(r.flag == true){
try{r.wait();}catch(Exception e){}
}
if(i%2 == 0){
r.name = "張三";
r.sex = "男";
}else{
r.name = "wang";
r.sex = "w";
}
// 喚醒輸出執行緒,標記改為true
r.flag = true;
r.notify();
}
i++;
}
}
}
/*
* 輸出執行緒:對Resource資源物件中的成員變數name sex 進行輸出
*/
public class OutputDemo1 implements Runnable{
private Resource r ;
public OutputDemo1(Resource r){
this.r = r;
}
public void run(){
while(true){
synchronized (r) {
// 若標記flag 為false,等待
if(!r.flag){
try{r.wait();}catch(Exception e){}
}
System.out.println(r.name+" "+r.sex);
// 將flag改為false,喚醒輸入執行緒
r.flag = false;
r.notify();
}
}
}
}
/*
* 定義資源類Resource
* 成員變數name sex
*/
public class Resource {
public String name;
public String sex;
// 標記:flag為true 說明:賦值完成 ;flag為false 說明:獲取值完成
// 執行緒輸入:需不需賦值看flag狀態。若flag為true,等待; 若flag為false,進行賦值,然後將flag改為true
// 執行緒輸出:需不需要輸出看flag狀態。若flag為true,進行輸出,然後將flag改為false; 若flag為false,等待
public boolean flag = false;
}
/*
* 使用等待與喚醒完成以下任務:
* 輸入執行緒向Resource中輸入name,sex
* 輸出執行緒從Resource中輸出name,sex
*
* 1. 若InpputDemo1發現Resource中沒有資料時,開始輸入, 輸入完成喚醒notify() OutputDemo1來輸出;若發現有資料就wait()
* 2. 若OutputDemo1發現Resource中沒有資料時,就wait();當發現有資料時,就輸出,輸出完成後喚醒notify() InpputDemo1來輸入資料
*/
public class ThreadTestDemo1 {
public static void main(String[] args) {
Resource r = new Resource();
//建立 Runnable介面實現類物件
InpputDemo1 in = new InpputDemo1(r);
OutputDemo1 out = new OutputDemo1(r);
Thread tin = new Thread(in);
Thread tout = new Thread(out);
tin.start();
tout.start();
/*
張三 男
wang w
張三 男
wang w
張三 男
wang w
張三 男
wang w
*/
}
}