線程間通訊:其實就是多個線程在操作同一個資源,但是操作的動作不同。
class Res {
private String name;
private String sex;
private boolean flag = false;
public synchronized void set(String name,String sex)
{
if(r.flag)
try{r.wait();}catch(Exception e){}
this.name = name;
this.sex = sex;
flag = true;
this.notify();
}
public synchronized void out()
{
if(r.flag)
try{r.wait();}catch(Exception e){}
System.out.println(name+"........"+sex);
flag = false;
this.notify();
}
}
class Input implements Runnable {
private Res r ;
Input(Res r) {
this.r = r;
}
public void run() {
int x = 0;
while(true) {
if(x==0)
r.set("mike","man");
else
r.set("麗麗","女女女女女");
x = (x+1)%2;
}
}
}
class Output implements Runnable {
private Res r ;
Output(Res r) {
this.r = r;
}
public void run() {
while(true) {
r.out();
}
}
}
class InputOutputDemo {
public static void main(String[] args) {
Res r = new Res();
new Thread(new Input(r)).start();
new Thread(new Output(r)).start();
}
}
wait(); notify(); notifyAll();都使用在同步中,因為要對持有監視器(鎖)的線程操作。所以要使用在同步中,因為只有同步才具有鎖。
為什麼這些操作線程的方法要定義Object類中呢?因為這些方法在操作同步中線程時,都必須要標識它們所操作線程只有的鎖,只有同一個鎖上的被等待線程,可以
被同一個鎖上notify喚醒。不可以對不同鎖中的線程進行喚醒。也就是說,等待和喚醒必須是同一個鎖。而鎖可以是任意對象,所以可以被任意對象調用的方法定
義Object類中。
。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。
生產者和消費者的例子
對於多個生產者和消費者。為什麼要定義while判斷標記。原因:讓被喚醒的線程再一次判斷標記。
為什麼定義notifyAll,因為需要喚醒對方線程。因為只用notify,容易出現只喚醒本方線程的情況。導致程式中的所有線程都等待。
class ProducerConsumerDemo {
public static void main(String[] args) {
Resource r = new Resource();
Producer pro = new Producer(r);
Consumer con = new Consumer(r);
Thread t1 = new Thread(pro);
Thread t2 = new Thread(pro);
Thread t3 = new Thread(con);
Thread t4 = new Thread(con);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
class Resource {
private String name;
private int count = 1;
private boolean flag = false;
public synchronized void set(String name) {
while(flag)
try{this.wait();}catch(Exception e){}
//t1(放棄資格) t2(擷取資格)
this.name = name+""+count++;
System.out.println(Thread.currentThread().getName()+"...生產者.."+this.name);
flag = true;
condition_con.signal(); //通過指定就可以避免喚醒的時候吧己方喚醒,這也就不用signalALL(); 和notifyAll();(已經被替代)
}
finally {
lock.unlock();//釋放鎖的動作一定要執行。
}
}
// t3 t4
public void out()throws InterruptedException {
lock.lock();
try {
while(!flag)
condition_con.await(); 1.5新特性:通過指定就可以針對值讓己方等待,代替了wait();
System.out.println(Thread.currentThread().getName()+"...消費者........."+this.name);
flag = false;
condition_pro.signal(); //通過指定就可以避免喚醒的時候吧己方喚醒,這也就不用signalALL(); 和notifyAll();(已經被替代)
}
finally {
lock.unlock();
}
}
}
class Producer implements Runnable {
private Resource res;
Producer(Resource res) {
this.res = res;
}
public void run() {
while(true) {
try {
res.set("+商品+");
} catch (InterruptedException e) { }
}
}
}
class Consumer implements Runnable {
private Resource res;
Consumer(Resource res) {
this.res = res;
}
public void run() {
while(true) {
try {
res.out();
} catch (InterruptedException e) { }
}
}
}
JDK1.5 中提供了多線程升級解決方案。將同步Synchronized替換成現實Lock操作。將Object中的wait,notify notifyAll,替換了Condition對象。該對象可
以Lock鎖 進行擷取。該樣本中,實現了本方只喚醒對方操作。
停止線程:
如何停止線程?:只有一種,run方法結束。開啟多線程運行,運行代碼通常是迴圈結構。只要控制住迴圈,就可以讓run方法結束,也就是線程結束。
特殊情況:當線程處於了凍結狀態。就不會讀取到標記。那麼線程就不會結束。
當沒有指定的方式讓凍結的線程恢複到運行狀態是,這時需要對凍結進行清除。強制讓線程恢複到運行狀態中來。這樣就可以操作標記讓線程結束。Thread
類提供該方法 interrupt();
class StopThread implements Runnable {
private boolean flag =true;
public synchronizede void run() {
while(flag) { //將flag置為false就可以控制迴圈了
try{
wait();
}
catch(InterruppedException e)
{
System.out.println(Thread.currentThread().getName()+"....Exception");
flag = false; //代替了下面的changeFlag();
}
System.out.println(Thread.currentThread().getName()+"....run");
}
}
public void changeFlag() {
flag = false;
}
}
class StopThreadDemo {
public static void main(String[] args) {
StopThread st = new StopThread();
Thread t1 = new Thread(st);
Thread t2 = new Thread(st);
t1.setDaemon(true); //守護線程,要線上程前調用,只要前台線程一結束,守護線程就自動結束了,藍色字型就去掉了沒用了
t2.setDaemon(true);
t1.start();
t2.start();
int num = 0;
while(true) {
if(num++ == 60) {
st.changeFlag();
//t1.interrupt(); //對凍結進行清除。強制讓線程恢複到運行狀態中來
//t2.interrupt();
break;
}
System.out.println(Thread.currentThread().getName()+"......."+num);
}
System.out.println("over");
}
}
。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。
join: 當A線程執行到了B線程的.join()方法時,A就會等待。等B線程都執行完,A才會執行。join可以用來臨時加入線程執行。
class Demo implements Runnable {
public void run() {
for(int x=0; x<70; x++) {
System.out.println(Thread.currentThread().toString()+"....."+x);
Thread.yield();
}
}
}
class JoinDemo {
public static void main(String[] args) throws Exception {
Demo d = new Demo();
Thread t1 = new Thread(d);
Thread t2 = new Thread(d);
t1.start();
//t1.setPriority(Thread.MAX_PRIORITY);
t2.start();
//t1.join();
for(int x=0; x<80; x++) {
//System.out.println("main....."+x);
}
System.out.println("over");
}
}
。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。
class MyThread extends Thread{
public void run(){
try {
Thread.currentThread().sleep(3000);
} catch (InterruptedException e) { }
System.out.println("MyThread running");
}
}
public class ThreadTest{
public static void main(String argv[]) {
MyThread t = new MyThread();
t.run();
t.start();
System.out.println("Thread Test");
}
}
程式碼分析過程:
MyThread t = new MyThread(); 建立了一個線程。
t.run();
調用MyThread對象的run方法。這是只有一個線程在運行就是主線程。當主線程執行到了run方法中的sleep(3000);時。這是主線程處於凍結狀態。程式並沒有任何
執行。當3秒過後,主線程列印了 MyThread running。 run方法執行結束。
t.start();
開啟了t線程。有兩種可能情況。第一種,主線程在只執行了t.start()後,還具有執行權,繼續往下執行,列印了Thread Test。主線程結束。t線程擷取執行權,調用
自己的run方法。然後執行的sleep(3000);凍結3秒。3秒後,列印MyThread running t線程結束,整個程式結束。
第二種情況:主線程執行到t.start();開啟了t線程,t線程就直接擷取到了執行權。就調用自己的run方法。指定到sleep(3000).t線程凍結3秒,這是t線程就是釋放了
執行權。那麼主線程開始執行列印了Thread Test,主線程結束。等到3秒後,t線程列印MyThread running ,然後t線程結束。程式結束。