java多執行緒--練習題總結
目錄
練題1:編寫程式實現,子執行緒迴圈3次,接著主執行緒迴圈5次,接著再子執行緒迴圈3次,主執行緒迴圈5次,如此反覆,迴圈3次.
練習題2:設計四個執行緒,其中兩個執行緒每次對變數i加1,另外兩個執行緒每次對i減1.
練習題3:自己編寫程式碼,實現生產者-消費者模型功能.內容自由發揮,只需要表達思想.
練習題4:現在有T1、T2、T3三個執行緒,你怎樣保證T2在T1執行完後執行,T3在T2執行完後執行?
練題1:編寫程式實現,子執行緒迴圈3次,接著主執行緒迴圈5次,接著再子執行緒迴圈3次,主執行緒迴圈5次,如此反覆,迴圈3次.
第一種實現方式:使用synchronized關鍵字
package com.lianxi; public class ThreadMain2 { public static void main(String[] args) { final ThreadFunction2 f2 = new ThreadFunction2(); // 子執行緒迴圈3次 new Thread(new Runnable(){ public void run(){ for(int i=0;i<3;i++){ f2.subFunction(); } } }).start(); // 主執行緒迴圈3次 for(int i=0;i<3;i++){ f2.mainFunction(); } } }
package com.lianxi; // 編寫功能類,實現子執行緒和主執行緒的功能 public class ThreadFunction2 { private boolean flag = false; // 主執行緒要實現的功能 public synchronized void mainFunction(){ while(!flag){ try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } for(int i=0;i<5;i++){ System.out.println("mainFunction"+i); } this.notify(); flag = false; } // 子執行緒要實現的功能 public synchronized void subFunction(){ while(flag){ try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } for(int i=0;i<3;i++){ System.out.println("subFunction"+i); } this.notify(); flag = true; } }
輸出結果:
subFunction0
subFunction1
subFunction2
mainFunction0
mainFunction1
mainFunction2
mainFunction3
mainFunction4
subFunction0
subFunction1
subFunction2
mainFunction0
mainFunction1
mainFunction2
mainFunction3
mainFunction4
subFunction0
subFunction1
subFunction2
mainFunction0
mainFunction1
mainFunction2
mainFunction3
mainFunction4
第二種實現方式:使用 lock 鎖和 Condition 介面
package com.lianxi;
public class ThreadMain {
public static void main(String[] args) {
final ThreadFunction f = new ThreadFunction();
// 子執行緒迴圈3次
new Thread(new Runnable(){
public void run(){
for(int i=0;i<3;i++){
f.subFunction();
}
}
}).start();
// 主執行緒迴圈3次
for(int i=0;i<3;i++){
f.mainFunction();
}
}
}
package com.lianxi;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
// 編寫功能類,實現子執行緒和主執行緒的功能
public class ThreadFunction extends Thread{
private boolean flag = false;
Lock lock = new ReentrantLock();
Condition con = lock.newCondition();
// 主執行緒要實現的功能
public void mainFunction(){
System.out.println("1.主執行緒開始"+" -- flag="+flag);
lock.lock();
try{
while(!flag){
try {
System.out.println("2.主執行緒等待"+" -- flag="+flag);
con.await(); // 使當前執行緒加入 await() 等待佇列中,並釋放當鎖,當其他執行緒呼叫signal()會重新請求鎖。與Object.wait()類似。
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("7.主執行緒開始迴圈5次"+" -- flag="+flag);
for(int i=0;i<5;i++){
System.out.println("mainFunction"+i+" -- flag="+flag);
}
flag = false;
System.out.println("8.喚醒子執行緒"+" -- flag="+flag);
con.signal(); // 喚醒一個在 await()等待佇列中的執行緒。與Object.notify()相似
}finally{
lock.unlock();
}
}
// 子執行緒要實現的功能
public void subFunction(){
System.out.println("3.子執行緒開始"+" -- flag="+flag);
lock.lock();
try{
while(flag){
try {
System.out.println("6.子執行緒等待"+" -- flag="+flag);
con.await(); // 使當前執行緒加入 await() 等待佇列中,並釋放當鎖,當其他執行緒呼叫signal()會重新請求鎖。與Object.wait()類似。
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("4.子執行緒開始迴圈3次"+" -- flag="+flag);
for(int i=0;i<3;i++){
System.out.println("subFunction"+i+" -- flag="+flag);
}
flag = true;
System.out.println("5.喚醒主執行緒"+" -- flag="+flag);
con.signal(); // 喚醒一個在 await()等待佇列中的執行緒。與Object.notify()相似
}finally{
lock.unlock();
}
}
}
輸出結果:
1.主執行緒開始 -- flag=false
2.主執行緒等待 -- flag=false
3.子執行緒開始 -- flag=false
4.子執行緒開始迴圈3次 -- flag=false
subFunction0 -- flag=false
subFunction1 -- flag=false
subFunction2 -- flag=false
5.喚醒主執行緒 -- flag=true
3.子執行緒開始 -- flag=true
7.主執行緒開始迴圈5次 -- flag=true
mainFunction0 -- flag=true
mainFunction1 -- flag=true
mainFunction2 -- flag=true
mainFunction3 -- flag=true
mainFunction4 -- flag=true
8.喚醒子執行緒 -- flag=false
1.主執行緒開始 -- flag=false
2.主執行緒等待 -- flag=false
4.子執行緒開始迴圈3次 -- flag=false
subFunction0 -- flag=false
subFunction1 -- flag=false
subFunction2 -- flag=false
5.喚醒主執行緒 -- flag=true
3.子執行緒開始 -- flag=true
6.子執行緒等待 -- flag=true
7.主執行緒開始迴圈5次 -- flag=true
mainFunction0 -- flag=true
mainFunction1 -- flag=true
mainFunction2 -- flag=true
mainFunction3 -- flag=true
mainFunction4 -- flag=true
8.喚醒子執行緒 -- flag=false
1.主執行緒開始 -- flag=false
2.主執行緒等待 -- flag=false
4.子執行緒開始迴圈3次 -- flag=false
subFunction0 -- flag=false
subFunction1 -- flag=false
subFunction2 -- flag=false
5.喚醒主執行緒 -- flag=true
7.主執行緒開始迴圈5次 -- flag=true
mainFunction0 -- flag=true
mainFunction1 -- flag=true
mainFunction2 -- flag=true
mainFunction3 -- flag=true
mainFunction4 -- flag=true
8.喚醒子執行緒 -- flag=false
Lock和synchronized的選擇
總結來說,Lock和synchronized有以下幾點不同:
1)Lock是一個介面,而synchronized是Java中的關鍵字,synchronized是內建的語言實現;
2)synchronized在發生異常時,會自動釋放執行緒佔有的鎖,因此不會導致死鎖現象發生;而Lock在發生異常時,如果沒有主動通過unLock()去釋放鎖,則很可能造成死鎖現象,因此使用Lock時需要在finally塊中釋放鎖;
3)Lock可以讓等待鎖的執行緒響應中斷,而synchronized卻不行,使用synchronized時,等待的執行緒會一直等待下去,不能夠響應中斷;
4)通過Lock可以知道有沒有成功獲取鎖,而synchronized卻無法辦到。
5)Lock可以提高多個執行緒進行讀操作的效率。
在效能上來說,如果競爭資源不激烈,兩者的效能是差不多的,而當競爭資源非常激烈時(即有大量執行緒同時競爭),此時Lock的效能要遠遠優於synchronized。所以說,在具體使用時要根據適當情況選擇。
練習題2:設計四個執行緒,其中兩個執行緒每次對變數i加1,另外兩個執行緒每次對i減1.
package com.lianxi;
// 面試題2:設計四個執行緒,其中兩個執行緒每次對變數i加1,另外兩個執行緒每次對i減1.
public class ThreadTest {
public static void main(String[] args){
final ThreadTestFunction f = new ThreadTestFunction();
Thread t1 = new Thread(new Runnable(){
public void run(){
f.add();
}
});
Thread t2 = new Thread(new Runnable(){
public void run(){
f.add();
}
});
Thread t3 = new Thread(new Runnable(){
public void run(){
f.sub();;
}
});
Thread t4 = new Thread(new Runnable(){
public void run(){
f.sub();
}
});
t1.start();
t2.start();
t3.start();
t4.start();
}
}
package com.lianxi;
public class ThreadTestFunction {
private int i=0;
public synchronized void add(){
i++;
System.out.println(Thread.currentThread().getName()+"add:i="+i);
}
public synchronized void sub(){
i--;
System.out.println(Thread.currentThread().getName()+"sub:i="+i);
}
}
輸出結果:
Thread-0add:i=1
Thread-3sub:i=0
Thread-2sub:i=-1
Thread-1add:i=0
練習題3:自己編寫程式碼,實現生產者-消費者模型功能.內容自由發揮,只需要表達思想.
package com.lianxi;
public class ThreadProductTest {
/**
* 面試題3:自己編寫程式碼,實現生產者-消費者模型功能.內容自由發揮,只需要表達思想.
* 程式碼中,自定義一個學生類,有name和age屬性,屬於共享物件,
* 生產者負責為studnet物件賦值,消費者負責打印出student物件的name和age的值,
* 當生產者賦值完以後通知消費者來列印,消費者列印完以後,通知生產者重新設定.
*/
public static void main(String[] args) {
final ThreadStudent s = new ThreadStudent();
// 模擬生產者執行緒類
Thread inputThread = new Thread(new Runnable(){
public void run(){
int num = 10;
while(num>0){
if(num % 2 == 0){
s.set("劉德華", 56);
}else{
s.set("倉木麻衣", 36);
}
num--;
}
}
},"生產者");
// 模擬消費者執行緒類
Thread outputThread = new Thread(new Runnable(){
public void run(){
int num = 10;
while(num>0){
s.get();
num--;
}
}
},"消費者");
inputThread.start();
outputThread.start();
}
}
package com.lianxi;
//學生實體類作為共享資源
public class ThreadStudent {
public String name; // 姓名
public int age; // 年齡
public boolean flag = false; // 標記變數,判斷當前學生物件是否已建立賦值好
//生產者的功能 ,為studnet物件賦值
public synchronized void set(String name,int age){
if(flag){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.name = name;
this.age = age;
System.out.println(Thread.currentThread().getName()+" student:name="+name+",age="+age+" -- flag="+flag);
this.flag = true;
this.notify(); // 喚醒 消費者執行緒
}
// 消費者的功能,列印sutdent物件的內容
public synchronized void get(){
if(!flag){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName()+" student:name="+name+",age="+age+" -- flag="+flag);
this.flag = false;
this.notify(); // 喚醒 生產者執行緒
}
}
輸出結果:
生產者 student:name=劉德華,age=56 -- flag=false
消費者 student:name=劉德華,age=56 -- flag=true
生產者 student:name=倉木麻衣,age=36 -- flag=false
消費者 student:name=倉木麻衣,age=36 -- flag=true
生產者 student:name=劉德華,age=56 -- flag=false
消費者 student:name=劉德華,age=56 -- flag=true
生產者 student:name=倉木麻衣,age=36 -- flag=false
消費者 student:name=倉木麻衣,age=36 -- flag=true
生產者 student:name=劉德華,age=56 -- flag=false
消費者 student:name=劉德華,age=56 -- flag=true
生產者 student:name=倉木麻衣,age=36 -- flag=false
消費者 student:name=倉木麻衣,age=36 -- flag=true
生產者 student:name=劉德華,age=56 -- flag=false
消費者 student:name=劉德華,age=56 -- flag=true
生產者 student:name=倉木麻衣,age=36 -- flag=false
消費者 student:name=倉木麻衣,age=36 -- flag=true
生產者 student:name=劉德華,age=56 -- flag=false
消費者 student:name=劉德華,age=56 -- flag=true
生產者 student:name=倉木麻衣,age=36 -- flag=false
消費者 student:name=倉木麻衣,age=36 -- flag=true
練習題4:現在有T1、T2、T3三個執行緒,你怎樣保證T2在T1執行完後執行,T3在T2執行完後執行?
package com.lianxi;
import java.util.Date;
public class ThreadSortTest {
/**
* 現在有T1、T2、T3三個執行緒,你怎樣保證T2在T1執行完後執行,T3在T2執行完後執行?
*/
public static void main(String[] args) {
Thread t1 = new Thread(new Runnable(){
public void run(){
System.out.println(Thread.currentThread().getName()+" -- "+System.currentTimeMillis());
}
},"t1");
Thread t2 = new Thread(new Runnable(){
public void run(){
System.out.println(Thread.currentThread().getName()+" -- "+System.currentTimeMillis());
}
},"t2");
Thread t3 = new Thread(new Runnable(){
public void run(){
System.out.println(Thread.currentThread().getName()+" -- "+System.currentTimeMillis());
}
},"t3");
t1.start();
try {
t1.join(); // t1.join()需要等t1.start()執行之後執行才有效果
} catch (InterruptedException e) {
e.printStackTrace();
}
t2.start();
try {
t2.join(); // t2.join()需要等t2.start()執行之後執行才有效果
} catch (InterruptedException e) {
e.printStackTrace();
}
t3.start();
}
}
輸出結果:
t1 -- 1540456352889
t2 -- 1540456352889
t3 -- 1540456352890
轉載請註明出處:https://blog.csdn.net/yuzongtao/article/details/83378538