JAVA執行緒(升級)
阿新 • • 發佈:2019-01-12
同步方法
package com.thread.ticket3;
/**
* 同步程式碼塊中的物件針對多個執行緒必須是同一個,
* 其實這個物件被稱為同步鎖物件
*
* 同步程式碼塊的鎖物件是誰呢?
* 任意物件
*
* 如果一個方法,一進去我們就看到程式碼被同步了,所以,我就想,這個同步能不能加在方法上面呢?
* 能:這個東西被稱為同步方法:就是把同步關鍵字加在同步方法上。
*
* 現在我們在這樣的一個程式碼中
* if(){
* 同步程式碼塊
* }else{
* 同步方法
* private synchronized void show()
* }
*
* 又會出現負票,為什麼會有問題呢?
* 就是同步的程式碼,必須保證鎖物件一致。
* 同步方法的鎖物件是誰呢?
* this物件
* 靜態同步方法的鎖物件是誰呢?
* Ticket.class 位元組碼檔案物件
* 當前類的位元組碼檔案物件
*/
public class TicketDemo {
public static void main(String[] args) {
Ticket t1 = new Ticket();
Thread t = new Thread(t1, "視窗1");
Thread t4 = new Thread(t1, "視窗2");
Thread t2 = new Thread(t1, "視窗3");
Thread t3 = new Thread(t1, "視窗4");
t.start();
t4.start();
t2.start();
t3.start();
}
}
package com.thread.ticket3;
public class Ticket implements Runnable {
private static int tickets = 100;
//private final Object b = new Object();
private int x = 0;
@Override
public void run() {
while (true) {
if (x % 2 == 0) {
synchronized (this) {
if (tickets > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "正在出售第" + (tickets--) + "張票");
}
}
} else {
show();
}
x++;
}
}
//同步方法
private static synchronized void show() {
if (tickets > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "正在出售第" + (tickets--) + "張票");
}
}
}
上面在同步方法上加了 static 又導致了執行緒安全問題
如下圖:
那麼靜態同步方法的物件又是什麼呢??
預設當前類的位元組碼檔案物件,所以要將同步程式碼塊中的物件改為Ticket.class 同步方法和同步程式碼塊中的物件一致。
synchronized (Ticket.class) {
if (tickets > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "正在出售第" + (tickets--) + "張票");
}
}
JDK1.5 的新特性
package com.thread.ticket4;
/**
* jdk1.5的新特性
* 以前同步關鍵字的方式,可以實現同步,並且是我們比較常見的方式,
* 但是我們老是需要去考慮它使用的哪個鎖物件,比較麻煩
* 所以,在JDK5以後,提供了一個新的方式:
* Lock鎖物件
* 這種方式在以前的程式碼中比較少見,但是以後開發中可以這樣中
* vector:執行緒安全,加了synchronizd
* ArrayList:執行緒不安全
* hashTable:執行緒安全
* hashMap :執行緒不安全
*
* @author yuliyang
* @version $Id: TicketDemo.java, v 0.1 2016年12月4日 下午7:28:39 yuliyang Exp $
*/
public class TicketDemo {
public static void main(String[] args) {
Ticket t = new Ticket();
Thread t1 = new Thread(t);
Thread t2 = new Thread(t);
Thread t3 = new Thread(t);
Thread t4 = new Thread(t);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
package com.thread.ticket4;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Ticket implements Runnable {
private int ticket = 100;
//private final ReentrantLock lock = new ReentrantLock();
//定義一個鎖物件
private final Lock lock = new ReentrantLock();
@Override
public void run() {
while (true) {
lock.lock();
try {
Thread.sleep(100);
if (ticket > 0) {
System.out.println(Thread.currentThread().getName() + "---正在出售:" + (ticket--) + "張票");
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
//釋放鎖
lock.unlock();
}
}
}
}
生產者與消費者問題
package com.thread.ticket5;
/**
* 執行緒間的通訊問題:
* 不同種類的執行緒對同一資源的操作問題。
*
* 需求:我有一個學生,我可以對其屬性賦值,我也可以獲取其屬性值,請用執行緒間的通訊的案例來體現
*
* 資源:學生student
* 獲取執行緒:GetThread
* 設定執行緒:SetThread
* 測試類:StudentDemo
*
* 問題一:每個執行緒操作的是自己的那一個學生物件,
* 怎麼解決:讓多個執行緒操作同一個學生物件
* 問題二:因為執行緒的隨機性,一次賦值和獲取值得操作,資料出現了問題
* 怎麼解決:
* 問題三:執行緒操作的東西一般都是比較複雜的操作
* 所以我們加入了迴圈操作。產生了更大的問題。
* 資料產生了混亂
* A:同一資料出現多次:
* CPU的一點點時間就足夠一個執行緒執行很多次。
* B:資料出現了問題:
* 由於CPU每一次的執行點應該是一次原子性的操作。
* 解決執行緒安全問題:
* 分析:真的是執行緒安全問題嗎?
* 1:是多執行緒環境嗎?
* 是
* 2:有共享資料嗎?
* 有:student
* 3:對共享資料有多條語句操作嗎?
* 有:s.name s.age
*
* 如何解決?
* 把出現問題的程式碼用同步程式碼塊包起來。
* 問題4:加入同步方法塊之後還是出現了問題。為什麼?
* A:多種執行緒進行操作的時候,要保證同步,必須是每一種型別的執行緒都必須加同步
* B:即使是給多種型別的執行緒加鎖,也必須是同一把鎖
* 到此解決了執行緒安全問題。
*
* @author yuliyang
* @version $Id: TicketDemo.java, v 0.1 2016年12月4日 下午7:28:39 yuliyang Exp $
*/
public class StudentDemo {
public static void main(String[] args) {
Student student = new Student();
SetThread st = new SetThread(student);
GetThread gt = new GetThread(student);
Thread g = new Thread(gt);
Thread s = new Thread(st);
s.start();
g.start();
}
}
package com.thread.ticket5;
public class Student {
private String name;
private String age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
}
package com.thread.ticket5;
public class SetThread implements Runnable {
private final Student s;
private int x = 0;
public SetThread(Student s) {
this.s = s;
}
@Override
public void run() {
while (true) {
//加同步,加同一把鎖(s)
synchronized (s) {
if (x % 2 == 0) {
s.setName("aaa");
s.setAge("20");
} else {
s.setName("vvv");
s.setAge("10");
}
}
x++;
}
}
}
package com.thread.ticket5;
public class GetThread implements Runnable {
private final Student student;
public GetThread(Student student) {
this.student = student;
}
@Override
public void run() {
while (true) {
//加同步,加同一把鎖
synchronized (student) {
System.out.println(student.getName() + ":" + student.getAge());
}
}
}
}
等待喚醒機制
package com.thread.ticket6;
/**
* 執行緒間的通訊問題:
* 不同種類的執行緒對同一資源的操作問題。
*
* 需求:我有一個學生,我可以對其屬性賦值,我也可以獲取其屬性值,請用執行緒間的通訊的案例來體現
*
* 資源:學生student
* 獲取執行緒:GetThread
* 設定執行緒:SetThread
* 測試類:StudentDemo
*
* 問題一:店鋪生產了包子,客人才可以買,賣完了,店鋪再生產
* 剛才的資料是大片大片的資料,此時需要改為:只有設定了學生的屬性,才能獲
* 取到學生的屬性值,依次的出現,怎麼辦呢?
* 原理:
* 如果我的學生屬性沒有值,應該先賦值,然後才可以使用,
* 如果我的學生屬性有值,應該使用後再賦值。
* 在Student中加入了一個flag標記,false表示沒有值
*
* 為了配合這種操作,java提供了一種機制:等待喚醒機制。
* notify(); //該方法在Object中
* wait(); //該方法在Object中
* 這兩個方法的呼叫,必須是鎖物件(物件監視器)呼叫。
* 為什麼這兩個方法沒有定義到Thread類裡面呢,
* 因為如果放到了Thread類中,就得使用Thread物件去呼叫這兩個方法,
* 但是,同步程式碼塊的鎖物件是任意的。那麼只能用Object。
* 怎麼用?
* 看程式碼例子。
*
*/
public class StudentDemo {
public static void main(String[] args) {
Student student = new Student();
SetThread st = new SetThread(student);
GetThread gt = new GetThread(student);
Thread g = new Thread(gt);
Thread s = new Thread(st);
s.start();
g.start();
}
}
package com.thread.ticket6;
public class SetThread implements Runnable {
private final Student student;
private int x = 0;
public SetThread(Student s) {
this.student = s;
}
@Override
public void run() {
while (true) {
//加同步,加同一把鎖(s)
synchronized (student) {
//如果有值:等待,(店家如果有包子,就等它賣完)
if (student.getFlag()) {
try {
student.wait();
/**
* s執行緒就等待在這裡了。
*/
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//設定值(如果沒有包子,就設定值)
if (x % 2 == 0) {
student.setName("餘麗陽");
student.setAge("20");
} else {
student.setName("簡小慧");
student.setAge("10");
}
x++;
//有值後,修改標記為true(告訴顧客有包子了)
student.setFlag(true);
//喚醒等待程式
student.notify();
/**
* 喚醒等待的執行緒,喚醒其他的執行緒,不代表其他的執行緒就能夠立即執行
*/
}
//可能g執行緒,可能s執行緒
}
}
}
package com.thread.ticket6;
public class GetThread implements Runnable {
private final Student student;
public GetThread(Student student) {
this.student = student;
}
@Override
public void run() {
while (true) {
//加同步,加同一把鎖
synchronized (student) {
//如果沒有值,就等待。(顧客發現如果沒有包子,就等著)
if (!student.getFlag()) {
try {
student.wait();
/**
* g執行緒在這裡等待了,那麼它就會釋放鎖資源。
* 將來,當它再次獲取鎖物件的時候,
* 是從哪裡等待,哪裡醒來
*/
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//如果有值,則輸出(如果有包子,就買了吃)
System.out.println(student.getName() + ":" + student.getAge());
//告訴店家沒有包子了
student.setFlag(false);
//喚醒:(叫店家做包子)
student.notify();
}
//可能g執行緒,可能s執行緒
}
}
}
package com.thread.ticket6;
public class Student {
private String name;
private String age;
private boolean flag = false; //預設表示沒有值
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
public boolean getFlag() {
return flag;
}
public void setFlag(boolean f) {
this.flag = f;
}
}
死鎖
package com.thread.DieLock;
/**
* 舉例:
* 五個哲學家的故事
*
* 面試題:
* 死鎖例子:
* 有兩把鎖 A,B 一個執行緒先使用了A鎖,然後使用B鎖
* 一個執行緒先使用了B鎖,然後使用了A鎖。
*
* @author yuliyang
* @version $Id: DieLockDemo.java, v 0.1 2016年12月4日 下午9:59:10 yuliyang Exp $
*/
public class DieLockDemo {
public static void main(String[] args) {
DieLock d1 = new DieLock(true);
DieLock d2 = new DieLock(false);
d2.start();
d1.start();
}
}
package com.thread.DieLock;
public class DieLock extends Thread {
private final boolean flag;
public DieLock(boolean flag) {
this.flag = flag;
}
@Override
public void run() {
if (flag) {
synchronized (MyLock.objA) {
System.out.println("if objA");
synchronized (MyLock.objB) {
System.out.println("if objB");
}
}
} else {
synchronized (MyLock.objB) {
System.out.println("else objB");
synchronized (MyLock.objA) {
System.out.println("else objA");
}
}
}
}
}
package com.thread.DieLock;
public class MyLock {
public static final Object objA = new Object();
public static final Object objB = new Object();
}