JAVA thread

摘要:JAVA thread

並行性
產生Thread的方法
方法一: 繼承Thread類
1. 子類覆寫父類中的run方法,將執行緒運行動作放在run函數中。
2. 建立子類物件的同時,執行緒也被創建
3. 使用start方法啟動執行緒
 

class Demo extends Thread{
    public void run(){
                for(int x = 0; x < 60; x++){
                        System.out.println("Demo Run " + x);
                }
        }
}

class Test{
    public static void main(String args[]){
                Demo mDemo = new Demo();
                mDemo.start();
               
                for(int x = 0; x < 60; x++){
                        System.out.println("Main Function " + x);
                }
        }
}

 

方法二: 實作Runnable介面
1. 子類覆寫介面中的run方法
2. 通過Thread類創建執行緒,並將實現了 Runnable介面的子類物件作為參數傳遞給 Thread類的構造函數
3. Thread類物件調用start方法開啟執行緒。
思考:為什麼要給Thread類的構造函數傳遞 Runnable的子類物件?
 
 
 
安全問題:
多執行緒下共享資源時,需注意互斥問題。利用同步來解決
 
問題:

class Hello{
    public static void main(String[] args){
        Resource r = new Resource();
        Input in = new Input(r);
        Output out = new Output(r);
        Thread t1 = new Thread(in);
        Thread t2 = new Thread(out);
        t1.start();
        t2.start();
    }
}


class Resource{
    String name;
    int sex;
}
class Input implements Runnable{
    Resource r;
    Input(Resource r){
        this.r = r;
    }
    public void run(){
        int x = 0;
        while(true){
            if(x == 0){
                r.name = "A";
                r.sex = 0;
            }else{
                r.name = "B";
                r.sex = 1;             
            }
            x = (x + 1)%2;
        }
    }
}
class Output implements Runnable{
    Resource r = new Resource();
    Output(Resource r){
        this.r = r;
    }
    public void run(){
        while(true){
            System.out.println(r.name + "..." + r.sex);
            //B...0, B...1, A...0, A...1 問題發生
        }
    }
}

 

解決 加入同步


class Input implements Runnable{
    Resource r;
    Input(Resource r){
        this.r = r;
    }
    public void run(){
        int x = 0;
        while(true){
            synchronized(r){
                if(x == 0){
                    r.name = "A";
                    r.sex = 0;
                }else{
                    r.name = "B";
                    r.sex = 1;             
                }
                x = (x + 1)%2;
            }
        }
    }
}
class Output implements Runnable{
    Resource r;
    Output(Resource r){
        this.r = r;
    }
    public void run(){
        while(true){
            synchronized (r) {
                System.out.println(r.name + "..." + r.sex);
            }
        }
    }
}
synchronized(物件)
{
需要同步的程式片段;
}
 
同步需要兩個或者兩個以上的執行緒,多個執行緒使用的是同一個鎖,未滿足這兩個條件,不能稱其為同步。
 
 
同步函數
public synchronized void Test(){
}
同步函數被static修飾後其共用的鎖就不是this,因為static不可用this
可使用該類別物件,即類別名稱 .class 當鎖
 
喚醒等待機制

class Resource{
    String name;
    int sex;
    boolean flag = false;
}
class Input implements Runnable{
    Resource r;
    Input(Resource r){
        this.r = r;
    }
    public void run(){
        int x = 0;
        while(true){
            synchronized(r){
                if(r.flag)
                    try {
                        r.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                if(x == 0){
                    r.name = "A";
                    r.sex = 0;
                }else{
                    r.name = "B";
                    r.sex = 1;             
                }
                x = (x + 1)%2;
                r.flag = true;
                r.notify();
            }
        }
    }
}
class Output implements Runnable{
    Resource r;
    Output(Resource r){
        this.r = r;
    }
    public void run(){
        while(true){
            synchronized (r) {
                if(!r.flag){
                    try {
                        r.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println(r.name + "..." + r.sex);
                r.flag = false;
                r.notify();
            }
        }
    }
}
wait(), notity(), notifyAll()
都使用在同步中,因為要對持有監視器的執行緒操作,所以要使用在同步中,因為只有同步才具有鎖
 
修改

class Hello{
    public static void main(String[] args){
        Resource r = new Resource();
        new Thread(new Input(r)).start();
        new Thread(new Output(r)).start();
    }
}


class Resource{
    private String name;
    private int sex;
    private boolean flag = false;
   
    public synchronized void set(String name, int sex){
        if(flag){
            try{this.wait();}catch(Exception e){}
        }
        this.name = name;
        this.sex = sex;
        flag = true;
        this.notify();
    }
    public synchronized void out(){
        if(!flag){
            try{this.wait();}catch(Exception e){}
        }
        System.out.println(name + "..." + sex);
        flag = false;
        this.notify();     
    }
}
class Input implements Runnable{
    Resource r;
    Input(Resource r){
        this.r = r;
    }
    public void run(){
        int x = 0;
        while(true){
            if(x == 0){
                r.set("A", 0);
            }else{
                r.set("B", 1);             
            }
            x = (x + 1)%2;
        }
    }
}
class Output implements Runnable{
    Resource r;
    Output(Resource r){
        this.r = r;
    }
    public void run(){
        while(true){
            r.out();
        }
    }
}

 

修正 多生產者及多消費者時,產生的非同步錯誤,使用notifyAll()
while判斷可以讓被喚醒的thread再一次判斷標記,使用notify()容易出現只喚醒本方thread,導致程序中的所以thread都變成等待,因為本方會因flag而卡住,故使用notifyAll()

class Hello{
    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){}
        }
        this.name = name + "--" + count++;
        System.out.println(Thread.currentThread().getName() + "---生產者---" + this.name);
        flag = true;
        this.notifyAll();
    }
    public synchronized void out(){
        while(!flag){
            try{this.wait();}catch(Exception e){}
        }
        System.out.println(Thread.currentThread().getName() + "消費者" + this.name);
        flag = false;
        this.notifyAll();      
    }
}


class Producer implements Runnable{
    private Resource res;
    Producer(Resource res){
        this.res = res;
    }
    public void run(){
        int x = 0;
        while(true){
            res.set("+A+");
        }
    }
}
class Consumer implements Runnable{
    Resource res;
    Consumer(Resource res){
        this.res = res;
    }
    public void run(){
        while(true){
            res.out();
        }
    }
}

 

2. 使用interrupt中斷方法。該方法是結束執行緒的凍結狀態,使執行緒回到運行狀態
setPriority(int num)
setDaemon(boolean b)
join()
自訂執行緒名稱
toString()
 
Lock介面
Lock 實作提供了比使用 synchronized 方法和語句可獲得的更廣泛的鎖定操作
 
使用Lock修改

class Hello{
    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;
    private Lock lock   = new ReentrantLock();
    private Condition condition = lock.newCondition();
    public void set(String name) throws InterruptedException{
        lock.lock();
        try{
            while(flag){
                condition.await();
            }
            this.name = name + "--" + count++;
            System.out.println(Thread.currentThread().getName() + "---生產者---" + this.name);
            flag = true;
            condition.signalAll();
        }finally{
            lock.unlock();//一定要關閉資源
        }
    }
    public void out() throws InterruptedException{
        lock.lock();
        try{
            while(!flag){
                condition.await();
            }
            System.out.println(Thread.currentThread().getName() + "消費者" + this.name);
            flag = false;
            condition.signalAll();
        }finally{
            lock.unlock();
        }
    }
}

class Producer implements Runnable{
    private Resource res;
    Producer(Resource res){
        this.res = res;
    }
    public void run(){
        int x = 0;
        while(true){
            try {
                res.set("+A+");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
class Consumer implements Runnable{
    Resource res;
    Consumer(Resource res){
        this.res = res;
    }
    public void run(){
        while(true){
            try {
                res.out();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

 

控制Thread
當沒有指定的方式讓凍結的tread恢復到運行狀態,這時需要對凍結進行清除
強制讓tread恢復到運行狀態中來,這樣就可以讓thread結束
利用Thread類別提供的interrupt();方法
//stop方法已經過時
//停止thread要使用run方法結束
//需控制循環結構讓run方法結束

class Hello{
    public static void main(String[] args){
        StopThread st = new StopThread();
        Thread t1 = new Thread(st);
        Thread t2 = new Thread(st);
       
        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");
    }
}

class StopThread implements Runnable{
    private boolean flag = true;
    public synchronized void run(){
        while(flag){
            try{
                wait();//wait後 其他thread就可以進入此 synchronized 方法
            }catch(InterruptedException e){
                System.out.println(Thread.currentThread().getName() + ".....Excetpion");
                flag =false;
            }
            System.out.println(Thread.currentThread().getName() + ".....run");
        }
    }
    public void changeFlag(){
        flag = false;
    }
}

 

setDaemon()當主執行緒結束後,也會讓呼叫此方法的執行緒結束
將該執行緒標記為守護執行緒或使用者執行緒

class Hello{
    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");
    }
}

class StopThread implements Runnable{
    private boolean flag = true;
    public synchronized void run(){
        while(flag){
            try{
                wait();
            }catch(InterruptedException e){
                System.out.println(Thread.currentThread().getName() + ".....Excetpion");
                flag =false;
            }
            System.out.println(Thread.currentThread().getName() + ".....run");
        }
    }
    public void changeFlag(){
        flag = false;
    }
}

 

join()

當a執行緒執行到了b執行緒的join()方法時,a就會等待,等b執行緒執行完,a才會執行


class Hello{
    public static void main(String[] args) throws InterruptedException{
        Demo st = new Demo();
        Thread t1 = new Thread(st);
        Thread t2 = new Thread(st);
       
        t1.start();
        t1.join();//t1向cpu搶奪執行權 做完才給主執行緒
        t2.start();
        for(int x = 0; x <5; x++){
            System.out.println("main ..." + x);
        }
        System.out.println("over");
    }
}

class Demo implements Runnable{
    public void run(){
        for(int x = 0; x <5; x++){
            System.out.println(Thread.currentThread().getName() + "..." + x);
        }
    }
}
執行結果:
-----------------------------------------
 
Thread-0...0
Thread-0...1
Thread-0...2
Thread-0...3
Thread-0...4
main ...0
main ...1
main ...2
main ...3
main ...4
over
Thread-1...0
Thread-1...1
Thread-1...2
Thread-1...3
----------------------------------------- 
 
 

//setPriority();、toString();
class Hello{
    public static void main(String[] args) throws InterruptedException{
        Demo st = new Demo();
        Thread t1 = new Thread(st);
        Thread t2 = new Thread(st);
       
        t1.start();
        t1.setPriority(Thread.MAX_PRIORITY);//10
        t2.start();
        for(int x = 0; x <80; x++){
            System.out.println("main ..." + x);
        }
        System.out.println("over");
    }
}

class Demo implements Runnable{
    public void run(){
        for(int x = 0; x <70; x++){
            System.out.println(Thread.currentThread().toString() + "..." + x);.
        }
    }
}

 


//yeild();
class Hello{
    public static void main(String[] args) throws InterruptedException{
        Demo st = new Demo();
        Thread t1 = new Thread(st);
        Thread t2 = new Thread(st);
       
        t1.start();
        t2.start();
        for(int x = 0; x <80; x++){
            System.out.println("main ..." + x);
        }
        System.out.println("over");
    }
}

class Demo implements Runnable{
    public void run(){
        for(int x = 0; x <70; x++){
            System.out.println(Thread.currentThread().toString() + "..." + x);
            Thread.yield();//暫停當前正在執行的執行緒物件,並執行其他執行緒。
        }
    }
}

 

使用封裝方法產生執行緒去執行特定方法,提高並行性


class Hello{
    public static void main(String[] args) throws InterruptedException{
        new Thread(){
            public void run(){
                for(int x = 0; x < 100; x++){
                    System.out.println(Thread.currentThread().getName() + "..." + x);
                }
            }
        }.start();
        for(int x = 0; x < 100; x++){
            System.out.println(Thread.currentThread().getName() + "..." + x);
        }
       
        Runnable r = new Runnable(){
            public void run(){
                for(int x = 0; x < 100; x++){
                    System.out.println(Thread.currentThread().getName() + "..." + x);
                }
            }
        };
        new Thread(r).start();
    }
}