java 執行緒間通訊
阿新 • • 發佈:2018-12-18
1.使用join,執行緒同步
Object object; public static void TestJoin(){ object= new Object(); final Thread one =new Thread(new Runnable() { @Override public void run() { printNumber("one"); } }); Thread two=new Thread(new Runnable() { @Override public void run() { Log.d("threadName","two開始等待one"); try { one.join();//join方法的意思是,放棄當前執行緒的執行,並返回呼叫join方法的執行緒,並直到呼叫join方法的執行緒執行完畢,才到當前執行緒的執行。 } catch (InterruptedException e) { e.printStackTrace(); } printNumber("two"); } }); one.start(); two.start();
private static void printNumber(String threadName) { int i = 0; while (i++ < 3) { synchronized (object){ try { Thread.sleep(100); Log.d("threadName", threadName + "數字:" + i); } catch (InterruptedException e) { e.printStackTrace(); } } }
結果:
two開始等待one
one數字:1
one數字:2
one數字:3
two數字:1
two數字:2
two數字:3
2.使用synchronized wait notify,執行緒同步
private static void printNumberOne(String threadName) { int i = 0; while (i++ < 3) { //synchronized同步程式碼塊,synchronized無法知道執行緒有沒有成功獲取到鎖 // synchronized不論是加在方法上還是物件上,它取得的鎖都是物件,不需要使用者手動去釋放鎖 //synchronizedd的作用域分為物件級別和類級別: //類級別:每個類都對應一把鎖,如果將類的靜態成員函式宣告為 synchronized,將對類的所有例項物件起作用 //物件級別:可以防止一個物件裡的幾個執行緒同時訪問synchronized修飾的方法,但是如果一個執行緒訪問了synchronized修飾 的方法,其它執行緒不能同時訪問此物件內的任意一個synchronized修飾的方法,但是類的不同勢例項物件勢可以的。 synchronized (object){ try { Thread.sleep(100); Log.d("threadName", threadName + "數字:" + i); //wait()呼叫wait方法的執行緒,會釋放物件的鎖,並將當前執行緒放置到物件的等待佇列,只有等待另外執行緒的通知或者中斷,才會返回 //wait(long)超時等待一段時間,如果沒有通知就超時返回 if(i==1) object.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } } private static void printNumberTwo(String threadName) { int i=0; while (i++ < 3) { synchronized (object) { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } Log.d("threadName", threadName + "數字:" + i); if(i==2){ //通知一個物件上等待的執行緒,等待的執行緒進入阻塞狀態,從等待佇列移動到同步佇列,等待cpu的排程重新獲得鎖,過得鎖後,該執行緒從wait方法返回 object.notify(); } } } } Object object; public static void ThreadOne(){ object= new Object(); final Thread one =new Thread(new Runnable() { @Override public void run() { printNumberOne("one"); } }); Thread two=new Thread(new Runnable() { @Override public void run() { printNumberTwo("two"); } }); one.start(); two.start(); }
結果:
one數字:1 two數字:1 two數字:2 two數字:3 one數字:2 one數字:3
3.使用lock,condition,執行緒同步
//ReentrantLock可重入鎖,有以下幾種方法:
//void lock 無條件的鎖
//void lockInterruptibly throws InterruptedException 可中斷的鎖 如果獲取了鎖,則立即返回,如果沒有獲取到鎖,則當前執行緒則進入等待狀態,直到獲取到鎖或者被別的執行緒中斷。如果使用synchronized,如果執行緒沒有獲取到鎖,則會一直等待下去。
// boolean tryLock() 嘗試獲取鎖,如果獲取到鎖,返回true,如果被其它執行緒持有,返回false,不會等待
//boolean tryLock(long timeout,TimeUnit unit) 如果獲取到鎖,返回true,別的執行緒持有,有等待時間,在等待時間內獲取到鎖,返回true,反之則返回false
//(lock是一個介面,必須手動釋放鎖,可重入,可中斷,可判斷,可(非)公平,適用於大量同步,
// synchronized是Java的關鍵字,執行完或者發生異常,都會主動釋放鎖,可重入,不可中斷,非公平,使用於少量同步)
final Lock lock = new ReentrantLock();
//對於同一的鎖,我們可以建立多個Condition,進行不同的操作,也可以喚醒或讓具體執行緒等待,這是Object的notify()方法做不到的
final Condition inputCondation = lock.newCondition();
final Condition removeCondation = lock.newCondition();
List<Integer> list = new ArrayList<Integer>();
int number = 0;
void InputNum() {
//加鎖
lock.lock();
try {
list.add(number++);
Log.d("ThreadName","inputsize="+list.size());
//相當於Object中的notify()方法
removeCondation.signal();
//相當於Object中的wait()方法
inputCondation.await();
list.add(number++);
Log.d("ThreadName","inputsize="+list.size());
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
//加了鎖,必須要手動釋放鎖,要不然在異常情況下,lock不會主動釋放鎖,會造成死鎖現象
lock.unlock();
}
}
void RemoveNum() {
lock.lock();
try {
removeCondation.await();
list.remove(list.size()-1);
Log.d("ThreadName","removesize="+list.size());
inputCondation.signal();
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
final Thread one =new Thread(new Runnable() {
@Override
public void run() {
InputNum();
}
});
Thread two=new Thread(new Runnable() {
@Override
public void run() {
RemoveNum();
}
});
two.start();
one.start();
結果:
inputsize=1 removesize=0 inputsize=1
4.使用AtomicInteger,執行緒同步
private AtomicInteger numCount=new AtomicInteger(0);
void Increment(){
for(int i=0;i<100;i++){
numCount.getAndIncrement();
}
Log.d("ThreadName","number++="+numCount);
}
new Thread(new Runnable() {
@Override
public void run() {
Increment();
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
Increment();
}
}).start();
結果:
ThreadName: number++=100
ThreadName: number++=200
5.使用volatile,執行緒同步
//使用volatile,執行緒修改了volatile修飾的值,會被立即強制重新整理到主記憶體中,讓所有執行緒可見,如果某個執行緒在修改變數的值,其它執行緒對於變數的快取會無效
//volatile能保證可見性,必不能保證原子性,所有對於volatile修飾的變數。不能含有變量表達式的操作,比如num=num++,這部書原子操作
volatile Boolean isTure=false;
void changStation(){
isTure=!isTure;
Log.d("ThreadName","修改了值");
}
void getStation(){
if(isTure){
Log.d("ThreadName","獲取到修改了的值");
return;
}
Log.d("ThreadName","沒獲取到修改了的值");
}
new Thread(new Runnable() {
@Override
public void run() {
changStation();
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
getStation();
}
}).start();
結果:
ThreadName: 修改了值
ThreadName: 獲取到修改了的值