多個線程訪問共享對象和數據的方式:
1.如果每個線程執行的代碼相同,可以使用同一個Runnable對象,這個Runnable對象中有那個共享數據,例如,賣票系統就可以這麼做。
2.如果每個線程執行的代碼不同,這是需要用不同的Runnable對象,有如下兩種方式來實現這些Runnable對象之間的數據共享:
2.1 將共享數據封裝在另外一個對象中,然後將這個對象逐一傳遞給各個Runnable對象。每個線程對共享數據的操作方法也分配到那個對象上去完成,這樣容易實現針對該數據進行的各個操作的互斥和通信(如生產者消費者的實現方式);
2.2 將這些Runnable對象作爲某一個類中的內部類,共享數據作爲這個外部類中的成員變量,每個線程對共享數據的操作方法也分配給外部類,以便實現對共享數據進行的各個操作的互斥和通信,作爲內部類的各個Runnable對象調用外部類的這些方法;
2.3 上面兩種方式的結合:將共享數據封裝在另外一個對象中,每個線程對共享數據的操作方法也分配到那個對象上去完成,對象作爲這個外部類的成員變量或方法中的局部變量,每個線程的Runnable對象作爲外部類中的成員內部類或局部內部類。
總之:要同步互斥的幾段代碼最好是分別放在幾個獨立的方法中,這些方法再放到同一個類中,這樣比較容易實現它們之間的同步互斥和通信。
1. 問題1
面試問題:子線程循環10次,接着主線程循環30次,接着又回到子線程循環10次,接着再回到主線程循環30次,如此循環20次,請寫出程序。
分析:
1)有兩個線程一次交替執行,則需要一個共享的flag變量來判斷執行次序,則需要互斥訪問和等待喚醒機制;
2)兩個線程的運行任務分別爲循環10次和循環30次,線程任務應封裝到線程的run()方法中;
3)子線程和主線程交替執行,循環20次,則需要一個外層for循環;
具體實現代碼如下(採用上述2.1的實現方式):
public class Question1 {
public static void main(String[] args){
final Bussiness bussiness=new Bussiness();
Thread t1=new Thread(new Runnable(){
public void run() {
for(int i=1;i<=20;i++){
bussiness.subRun(i);
}
}
} );
t1.start();
for(int i=1;i<=20;i++){
bussiness.mainRun(i);
}
}
}
class Bussiness{
//共享數據封裝在另外一個對象中
private static boolean flag=true;
//對共享數據的操作方法也分配到這個對象上
public synchronized void subRun(int i) {
while(!flag){//使用while防止僞喚醒,使用if在多生產多消費的情況下會有問題
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
for (int j = 1; j <= 10; j++) {
System.out.println("sub thread sequence of " + j + "; loop of " + i);
}
flag = false;
this.notify();
}
public synchronized void mainRun(int i) {
while (flag){
try {
this.wait();
} catch (InterruptedException e1) {
e1.printStackTrace();
}
}
for (int j = 1; j <= 30; j++) {
System.out.println("main main mian main thread sequence of "+ j + "; loop of " + i);
}
flag = true;
this.notify();
}
}
經驗:要用到共同數據(包括同步鎖)或共同算法的若干方法應該歸在同一個類身上,這種設計體現了高類聚和程序健壯性。
2.問題2
面試問題:設計4個線程,其中兩個線程每次對j增加1,另外兩個線程對j每次減少1,寫出程序。
分析:共享數據j,對這個共享數據操作的兩個方法:增加和減少。
具體實現代碼如下(採用上述2.2的實現方式):
public class Question2 {
//共享數據作爲外部類的成員變量
private int j;
@Test
public void test(){
IncRunnable incR=new IncRunnable();
DecRunnable decR=new DecRunnable();
Thread t1=new Thread(incR);
Thread t2=new Thread(incR);
Thread t3=new Thread(decR);
Thread t4=new Thread(decR);
t1.start();
t2.start();
t3.start();
t4.start();
}
//對共享數據的操作方法也分配給外部類,以便實現對共享數據的各個操作的互斥和通信
private synchronized void inc(){
j++;
System.out.println(Thread.currentThread().getName()+" inc: "+ j);
}
private synchronized void dec(){
j--;
System.out.println(Thread.currentThread().getName()+" dec: "+ j);
}
//將Runnable對象作爲外部類Question2的內部類
class IncRunnable implements Runnable{
@Override
public void run() {
for(int i=0;i<10;i++)
//作爲內部類的Runnable對象調用外部類操作數據的方法inc()
inc();
}
}
class DecRunnable implements Runnable{
@Override
public void run() {
for(int i=0;i<10;i++)
dec();
}
}
}