1. 程式人生 > >java多執行緒一 基本實現方法、消費者生產者佇列、死鎖

java多執行緒一 基本實現方法、消費者生產者佇列、死鎖

1.基本概念圖

這裡寫圖片描述
四個狀態、start 代表被建立、run表示正在執行、阻塞(在幹別的事情去了,可以把資源空出來給別人用)、死亡。
核心思想是提高cpu的使用率,你幹別的事去了我來利用cpu啊,難點是同步,同時訪問資料,一個廁所只有一個坑,倆個人一起上是不文明的。
參考:http://blog.csdn.net/zjwcdd/article/details/51517096

2.基本使用方法

倆種常用使用方法,一個是整合Thread類,一種是實現Runnable介面,因為java有單繼承的限制,而介面Runnable可以重複實現多次,還可以利用執行緒池,推薦介面實現方法:

1.繼承方法

class JavaThread extends Thread{
    private String name;
    public JavaThread(String name) {
        this.name=name;
    }
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println(name + "執行  :  " + i);
            try {
                sleep((int) Math.random() * 10
); } catch (InterruptedException e) { e.printStackTrace(); } } } } class Main { public static void main(String[] args) { JavaThread mTh1=new JavaThread("A"); JavaThread mTh2=new JavaThread("B"); mTh1.start(); mTh2.start(); } }

輸出

B執行  :  0
A執行  :  0#可以發現A也可以搶cpu用
B執行  :  1
A執行  :  1
B執行  :  2
A執行  :  2
B執行  :  3
A執行  :  3
B執行  :  4
A執行  :  4

2.實現Runable介面

class JavaThread implements Runnable{
    private String name;

    public JavaThread(String name) {
        this.name=name;
    }

    @Override//表示實現介面的時候過載了
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println(name + "執行  :  " + i);
            try {
                Thread.sleep((int) Math.random() * 10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

    }

}
class Main {

    public static void main(String[] args) {
        new Thread(new JavaThread("C")).start();
        new Thread(new JavaThread("D")).start();
    }

}

輸出

C執行  :  0
D執行  :  0#可以發現D也在搶C的資源
C執行  :  1
D執行  :  1
C執行  :  2
D執行  :  2
C執行  :  3
D執行  :  3
C執行  :  4
D執行  :  4

另外提一點join,主程序做完了想關閉程式,你新建的子程序沒幹完事也也也也也也直接退出的話會出問題的,join是主程序等你一起退出,這個第一次用的時候坑了我好久。
參考:java多執行緒吐血整理http://blog.csdn.net/evankaka/article/details/44153709

3.生產者消費者模式

優點:利用很多個生產者(執行緒)去生產東西,利用很多個消費者(也是執行緒)去消費生產的東西,好處是利用多執行緒來處理,省時間。注意 生產者生產東西的總數有個限制,超過就暫時不生產,等消費者消費完,不然會通貨膨脹。消費者有東西就消費,沒東西就等待。
難點:生產者和消費者之間的資料的同步的問題(一個檔案只能同時讓一個人在修改,一個廁所只能一個人同時在用)
模式圖:
這裡寫圖片描述
參考:https://www.cnblogs.com/chentingk/p/6497107.html
實現也有很多種方式:
可以參考這個:
生產者/消費者問題的多種Java實現方式http://blog.csdn.net/monkey_d_meng/article/details/6251879
這裡舉出另外一種:



import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;

/**
 * 多執行緒模擬實現生產者/消費者模型
 *
 * @author 林計欽
 * @version 1.0 2013-7-25 下午05:23:11
 */
public class ProducerConsumer {
    /**
     *
     * 定義裝蘋果的籃子
     *
     */
    public class Basket {
        // 籃子,能夠容納3個蘋果
        BlockingQueue<String> basket = new LinkedBlockingQueue<String>(3);

        // 生產蘋果,放入籃子
        public void produce() throws InterruptedException {
            // put方法放入一個蘋果,若basket滿了,等到basket有位置
            basket.put("An apple");
        }

        // 消費蘋果,從籃子中取走
        public String consume() throws InterruptedException {
            // take方法取出一個蘋果,若basket為空,等到basket有蘋果為止(獲取並移除此佇列的頭部)
            return basket.take();
        }
    }

    // 定義蘋果生產者
    class Producer implements Runnable {
        private String instance;
        private Basket basket;

        public Producer(String instance, Basket basket) {
            this.instance = instance;
            this.basket = basket;
        }

        public void run() {
            try {
                while (true) {
                    // 生產蘋果
                    System.out.println("生產者準備生產蘋果:" + instance);
                    basket.produce();
                    System.out.println("!生產者生產蘋果完畢:" + instance);
                    // 休眠300ms
                    Thread.sleep(300);
                }
            } catch (InterruptedException ex) {
                System.out.println("Producer Interrupted");
            }
        }
    }

    // 定義蘋果消費者
    class Consumer implements Runnable {
        private String instance;
        private Basket basket;

        public Consumer(String instance, Basket basket) {
            this.instance = instance;
            this.basket = basket;
        }

        public void run() {
            try {
                while (true) {
                    // 消費蘋果
                    System.out.println("消費者準備消費蘋果:" + instance);
                    System.out.println(basket.consume());
                    System.out.println("!消費者消費蘋果完畢:" + instance);
                    // 休眠1000ms
                    Thread.sleep(1000);
                }
            } catch (InterruptedException ex) {
                System.out.println("Consumer Interrupted");
            }
        }
    }

    public static void main(String[] args) {
        ProducerConsumer test = new ProducerConsumer();

        // 建立一個裝蘋果的籃子
        Basket basket = test.new Basket();

        ExecutorService service = Executors.newCachedThreadPool();
        Producer producer = test.new Producer("生產者001", basket);
        Producer producer2 = test.new Producer("生產者002", basket);
        Consumer consumer = test.new Consumer("消費者001", basket);
        service.submit(producer);
        service.submit(producer2);
        service.submit(consumer);
        // 程式執行5s後,所有任務停止
//        try {
//            Thread.sleep(1000 * 5);
//        } catch (InterruptedException e) {
//            e.printStackTrace();
//        }
//        service.shutdownNow();
    }

}

輸出:

生產者準備生產蘋果:生產者002
消費者準備消費蘋果:消費者001
生產者準備生產蘋果:生產者001
!生產者生產蘋果完畢:生產者002
!生產者生產蘋果完畢:生產者001
An apple
!消費者消費蘋果完畢:消費者001
生產者準備生產蘋果:生產者001
!生產者生產蘋果完畢:生產者001

4.死鎖

鎖,同步是解決多執行緒下保證只有一個執行緒在處理這個檔案的基本方式,我鎖住了,別人就不能訪問了,直到我不需要了,你才能訪問它,上面中的執行緒安全的佇列就是利用這個原理實現的,死鎖就是A在等B某個訪問結束,B也在等A某個訪問結束,倆個都在等,一直等,這樣就出現問題了。
總感覺會出現這個問題是因為計算機太傻比了,訪問的時候給檔案加個鎖,誰來我給誰,或者讓它等不行嗎?不可以嗎?可能有另外的開銷?為啥對執行緒上鎖?是不是我理解錯了。
這裡寫圖片描述
上圖中,就是倆個人都在等,都不服輸,就會出現死鎖,程式碼如下:

import java.util.Date;

public class LockTest {
    public static String obj1 = "obj1";
    public static String obj2 = "obj2";
    public static void main(String[] args) {
        LockA la = new LockA();
        new Thread(la).start();
        LockB lb = new LockB();
        new Thread(lb).start();
    }
}
class LockA implements Runnable{
    public void run() {
        try {
            System.out.println(new Date().toString() + " LockA 開始執行");
            while(true){
                synchronized (LockTest.obj1) {
                    System.out.println(new Date().toString() + " LockA 鎖住 obj1");
                    Thread.sleep(3000); // 此處等待是給B能鎖住機會
                    synchronized (LockTest.obj2) {
                        System.out.println(new Date().toString() + " LockA 鎖住 obj2");
                        Thread.sleep(60 * 1000); // 為測試,佔用了就不放
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
class LockB implements Runnable{
    public void run() {
        try {
            System.out.println(new Date().toString() + " LockB 開始執行");
            while(true){
                synchronized (LockTest.obj2) {
                    System.out.println(new Date().toString() + " LockB 鎖住 obj2");
                    Thread.sleep(3000); // 此處等待是給A能鎖住機會
                    synchronized (LockTest.obj1) {
                        System.out.println(new Date().toString() + " LockB 鎖住 obj1");
                        Thread.sleep(60 * 1000); // 為測試,佔用了就不放
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

輸出就是A鎖住了,B鎖住了,然後就不動了:

Wed Jan 10 19:36:44 GMT+08:00 2018 LockA 開始執行
Wed Jan 10 19:36:44 GMT+08:00 2018 LockA 鎖住 obj1
Wed Jan 10 19:36:44 GMT+08:00 2018 LockB 開始執行
Wed Jan 10 19:36:44 GMT+08:00 2018 LockB 鎖住 obj2
後面就沒有了

如何避免死鎖呢,別一個鎖著又去鎖另外一個

import java.util.Date;

public class UnLock {
    public static String obj1 = "obj1";
    public static String obj2 = "obj2";
    public static void main(String[] args) {
        LockA la = new LockA(obj1,obj2);
        new Thread(la).start();
        LockB lb = new LockB(obj1,obj2);
        new Thread(lb).start();
    }
}
class LockA implements Runnable{
    private Object obj1;
    private Object obj2;
    public LockA(Object o1,Object o2){
        this.obj1 = o1;
        this.obj2 = o2;
    }
    public void run() {
        String name = Thread.currentThread().getName();
        System.out.println(name + " acquiring lock on " + obj1);
        synchronized (obj1) {
            System.out.println(name + " acquired lock on " + obj1);
//            work();
        }
        System.out.println(name + " released lock on " + obj1);
        System.out.println(name + " acquiring lock on " + obj2);
        synchronized (obj2) {
            System.out.println(name + " acquired lock on " + obj2);
//            work();
        }
        System.out.println(name + " released lock on " + obj2);

        System.out.println(name + " finished execution.");
    }
}
class LockB implements Runnable{
    private Object obj1;
    private Object obj2;
    public LockB(Object o1,Object o2){
        this.obj1 = o1;
        this.obj2 = o2;
    }
    public void run() {
        String name = Thread.currentThread().getName();
        System.out.println(name + " acquiring lock on " + obj2);
        synchronized (obj2) {
            System.out.println(name + " acquired lock on " + obj2);
//            work();
        }
        System.out.println(name + " released lock on " + obj2);
        System.out.println(name + " acquiring lock on " + obj1);
        synchronized (obj1) {
            System.out.println(name + " acquired lock on " + obj1);
        }
        System.out.println(name + " released lock on " + obj1);

        System.out.println(name + " finished execution.");
    }
}
Thread-0 acquiring lock on obj1
Thread-1 acquiring lock on obj2
Thread-0 acquired lock on obj1
Thread-1 acquired lock on obj2
Thread-0 released lock on obj1
Thread-1 released lock on obj2
Thread-0 acquiring lock on obj2
Thread-1 acquiring lock on obj1
Thread-0 acquired lock on obj2
Thread-1 acquired lock on obj1
Thread-0 released lock on obj2
Thread-1 released lock on obj1
Thread-1 finished execution.
Thread-0 finished execution.