Java多線程編程(3)

5、線程協調

線程協調:wait和notify方法

死鎖發生時的條件:

(1)互斥條件:一個資源每次只能被一個進程使用。

(2)請求與保持條件:一個進程因請求資源而阻塞時,對已獲得的資源保持不放。

(3)不剝奪條件: 進程已獲得的資源,在未使用完之前,不能強行剝奪。

(4)循環等待條件:若干進程之間形成一種頭尾相接的循環等待資源關係。

public class ThreadCoordinate {

    public static void main(String[] args) {
        CoordinateA coordinateA = new CoordinateA();
        Thread threadA = new Thread(coordinateA, "線程1");
        CoordinateB coordinateB = new CoordinateB();
        Thread threadB = new Thread(coordinateB, "線程2");
        threadA.start();
        threadB.start();
    }
}
class CoordinateA implements Runnable {
    @Override
    public void run() {
        synchronized ("A") {
            System.out.println(Thread.currentThread().getName() 
		+ "持有了A鎖,等待B鎖。。。");
            try {
                "A".wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized ("B") {
                System.out.println(Thread.currentThread().getName() 
		+ "持有了A鎖和B鎖");
            }
        }
    }
}
class CoordinateB implements Runnable {
    @Override
    public void run() {
        synchronized ("B") {
            System.out.println(Thread.currentThread().getName() 
		+ "持有了B鎖,等待A鎖。。。");
            synchronized ("A") {
                System.out.println(Thread.currentThread().getName() 
		+ "持有了B鎖和A鎖");
                "A".notify();
            }
        }
    }
}

懶漢式單例模式

懶漢式單例模式 在使用這個對象時,纔去查看這個對象是否創建。如果沒創建就馬上創建;如果已經創建,就返回這個實例。 線程不安全,需要加上同步鎖,影響了程序執行效率。 餓漢式單例模式 在加載這個類的時候,就先創建好一個對象實例,等待調用。 天生線程安全,類加載的時候初始化一次對象,效率比懶漢式高。

public class ThreadSingleton {
    public static void main(String[] args) {
        for (int i = 0; i < 100; i++) {
            HelloRunnable helloRunnable = new HelloRunnable();
            Thread thread = new Thread(helloRunnable);
            thread.start();
        }
    }
}
class Lazybones {
    public Lazybones() {
        System.out.println("對象實例化了");
    }
    private static Lazybones lazybones = null;
    public static Lazybones getLazybones() {
        synchronized ("") {
            if (lazybones == null) {
                lazybones = new Lazybones();
            }
        }
        return lazybones;
    }
}
class HelloRunnable implements Runnable {
    @Override
    public void run() {
        Lazybones.getLazybones();
    }
}

生產者消費者例子

多線程環境下,我們經常需要多個線程的併發和協作。這個時候,就需要了解一個重要的多線程併發協作模型“生產者/消費者模式”。

什麼是生產者?       生產者指的是負責生產數據的模塊(這裏模塊可能是:方法、對象、線程、進程)。

什麼是消費者?       消費者指的是負責處理數據的模塊(這裏模塊可能是:方法、對象、線程、進程)。

什麼是緩衝區?       消費者不能直接使用生產者的數據,它們之間有個“緩衝區”。生產者將生產好的數據放入“緩衝區”,消費者從“緩衝區”拿要處理的數據。

public class Product {

    public String name;

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

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
public class ProductPool {
    public int maxSize = 0;
    public List<Product> productList;
    public ProductPool(int maxSize, List<Product> productList) {
        this.maxSize = maxSize;
        this.productList = productList;
    }
    public synchronized void push(Product product) {
        if (this.productList.size() >= this.maxSize) {
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        this.productList.add(product);
        this.notifyAll();
    }
public synchronized Product pop() {
        if (this.productList.size() <= 0) {
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        Product product = this.productList.remove(0);
        this.notifyAll();
        return product;
    }
}
public class Producer implements Runnable {

    private ProductPool productPool;

    public Producer(ProductPool productPool) {
        this.productPool = productPool;
    }

    @Override
    public void run() {
        int i = 0;
        while (true) {
            String name = i++ + "產品";
            Product product = new Product(name);
            this.productPool.push(product);
            System.out.println("生產了" + name);
        }

    }
}
public class Consumer implements Runnable {

    public ProductPool productPool;

    public Consumer(ProductPool productPool) {
        this.productPool = productPool;
    }

    @Override
    public void run() {
        while (true) {
            Product product = this.productPool.pop();
            System.out.println("消費了" + product.getName() + "產品");
        }
    }
}
public class MainMethod {

    public static void main(String[] args) {
        ProductPool productPool = 
	new ProductPool(10, new LinkedList<Product>());
        Producer producer = new Producer(productPool);
        Thread thread1 = new Thread(producer);
        thread1.start();

        Consumer consumer = new Consumer(productPool);
        Thread thread2 = new Thread(consumer);
        thread2.start();
    }
}

6、高級併發對象

線程定義:實現Callable接口

前面兩種線程定義方式都有這兩個問題:

無法獲取子線程的返回值;

run方法不可以拋出異常。

爲了解決這兩個問題,我們就需要用到Callable這個接口了。

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;

public class HelloCallable implements Callable {
    int i = 0;
    @Override
    public Object call() throws Exception {
        for (int i1 = 0; i1 < 10; i1++) {
            System.out.println(Thread.currentThread().getName() + ":" + i++);
        }
        return i;
    }
    public static void main(String[] args) {
        HelloCallable helloCallable = new HelloCallable();
        for (int i = 0; i < 10; i++) {
            FutureTask futureTask = new FutureTask(helloCallable);
            Thread thread = new Thread(futureTask, "子線程" + i);
            thread.start();
            try {
                System.out.println("子線程" + i + "返回值:" + futureTask.get());
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
        }
    }
}

線程同步:鎖對象

同步代碼依賴於一種簡單的可重入鎖。這種鎖易於使用,但有很多限制。java.util.concurrent.locks軟件包支持更復雜的鎖定習慣用法 。 Lock對象的工作方式非常類似於同步代碼所使用的隱式鎖。與隱式鎖一樣,一次只能有一個線程擁有一個Lock對象。Lock對象還wait/notify通過其關聯的Condition對象支持一種機制 。

import java.util.concurrent.locks.ReentrantLock;
public class ThreadLock implements Runnable {
    public static int ticket = 100;
    ReentrantLock reentrantLock = new ReentrantLock();
    @Override
    public void run() {
        while (ticket > 0) {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            reentrantLock.lock();
            if (ticket <= 0) {
                return;
            }
            System.out.println(Thread.currentThread().getName() + "賣了1張票,剩餘" + --ticket + "張票");
            reentrantLock.unlock();
        }
    }
    public static void main(String[] args) {
        ThreadLock threadLock = new ThreadLock();
        Thread thread1 = new Thread(threadLock, "售票員1");
        thread1.start();
        Thread thread2 = new Thread(threadLock, "售票員2");
        thread2.start();
        Thread thread3 = new Thread(threadLock, "售票員3");
        thread3.start();
    }
}

線程池

Java中創建和銷燬一個線程是比較昂貴的操作,需要系統調用。頻繁創建和銷燬線程會影響系統性能。 使用線程池帶來以下好處: 降低資源的消耗。線程本身是一種資源,創建和銷燬線程會有CPU開銷;創建的線程也會佔用一定的內存。 提高任務執行的響應速度。任務執行時,可以不必等到線程創建完之後再執行。 提高線程的可管理性。線程不能無限制地創建,需要進行統一的分配、調優和監控。

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

public class ThreadPool implements Runnable {
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName());
    }

    public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        executorService.execute(new ThreadPool());
        executorService.execute(new ThreadPool());
        executorService.execute(new ThreadPool());
        executorService.shutdown();
    }
}

併發集合: BlockingQueue

BlockingQueue實現被設計爲主要用於生產者-消費者隊列,如果BlockingQueue是空的,從BlockingQueue取東西的操作將會被阻斷進入等待狀態,直到BlockingQueue進了東西纔會被喚醒。同樣,如果BlockingQueue是滿的,任何試圖往裏存東西的操作也會被阻斷進入等待狀態,直到BlockingQueue裏有空間時纔會被喚醒。 BlockingQueue實現是線程安全的。所有排隊方法都可以使用內部鎖或其他形式的併發控制來原子地實現其效果。

public class Product {

    private String name;

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

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

}
import java.util.concurrent.BlockingQueue;
public class Producer implements Runnable {
    BlockingQueue<Product> blockingQueue;
    public Producer(BlockingQueue<Product> blockingQueue) {
        this.blockingQueue = blockingQueue;
    }
    @Override
    public void run() {
        int i = 0;
        while (true) {
            try {
                Product product = new Product("" + i++);
                blockingQueue.put(product);
                System.out.println("生產產品:" + product.getName());
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
import java.util.concurrent.BlockingQueue;
public class Consumer implements Runnable {
    BlockingQueue<Product> blockingQueue;
    public Consumer(BlockingQueue<Product> blockingQueue) {
        this.blockingQueue = blockingQueue;
    }
    @Override
    public void run() {
        try {
            while (true) {
                Product product = blockingQueue.take();
                System.out.println("消費產品: " + product.getName());
                Thread.sleep(1000);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
import java.util.concurrent.*;

public class MainMethod {

    public static void main(String[] args) {
        BlockingQueue<Product> BlockingQueue = new 	ArrayBlockingQueue<Product>(100);
        Producer producer = new Producer(BlockingQueue);
        Consumer consumer = new Consumer(BlockingQueue);
        new Thread(producer).start();
        new Thread(consumer).start();
    }

}

靜態代理

靜態代理就是在編譯時就確定了代理類與被代理類的關係。

public class StaticProxy {
    public static void main(String[] args) {
        Me me = new Me();
        WeddingCompany weddingCompany = new WeddingCompany(me);
        weddingCompany.marryMethod();
    }
}
interface Marry {
    void marryMethod();
}
class Me implements Marry {
    @Override
    public void marryMethod() {
        System.out.println("哈哈,我要結婚了!!!");
    }
}
class WeddingCompany implements Marry {
    private Marry marrry;
    public WeddingCompany(Marry marrry) {
        this.marrry = marrry;
    }
    @Override
    public void marryMethod() {
        before();
        this.marrry.marryMethod();
        after();
    }
    public void before() {
        System.out.println("結婚之前,佈置現場。");
    }
    public void after() {
        System.out.println("結婚之後,收取費用。");
    }
}

Lamda表達式

Lambda 表達式,也可稱爲閉包,它是推動 Java 8 發佈的最重要新特性。 Lambda 允許把函數作爲一個方法的參數(函數作爲參數傳遞進方法中)。 使用 Lambda 表達式可以使代碼變的更加簡潔緊湊。

public class LambdaExpression {
    // 2、靜態內部類
    static class Love2 implements ILove {
        @Override
        public void lambda() {
            System.out.println("I Love Lambda 2");
        }
    }
    public static void main(String[] args) {
        // 1、外部類
        ILove love = new Love();
        love.lambda();
        // 2、靜態內部類
        ILove love2 = new Love2();
        love2.lambda();
        // 3、局部內部類
        class Love3 implements ILove {
            @Override
            public void lambda() {
                System.out.println("I Love Lambda 3");
            }
        }
        ILove love3 = new Love3();
        love3.lambda();
        // 4、匿名內部類
        ILove love4 = new ILove() {
            @Override
            public void lambda() {
                System.out.println("I Love Lambda 4");
            }
        };
        love4.lambda();
        // 5、Lambda
        ILove love5 = () -> {
            System.out.println("I Love Lambda 5");
        };
        love5.lambda();
    }
}
// 函數式接口
interface ILove {
    void lambda();
}

// 1、實現類
class Love implements ILove {

    @Override
    public void lambda() {
        System.out.println("I Love Lambda 1");
    }
}

ArrayList

ArrayList不是線程安全的,他的add方法沒有synchronized同步鎖控制。

import java.util.ArrayList;
import java.util.List;
public class ArrayListTest {
    public static void main(String[] args) {
        List<String> list = new ArrayList<String>();
        for (int i = 0; i < 10000; i++) {
            new Thread(() -> {
                list.add(Thread.currentThread().getName());
            }).start();
        }
        try {
            Thread.sleep(1000);
            System.out.println(list.size());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
import java.util.ArrayList;
import java.util.List;
public class ArrayListTest2 {
    public static void main(String[] args) {
        List<String> list = new ArrayList<String>();
        for (int i = 0; i < 10000; i++) {
            new Thread(() -> {
                synchronized ("") {
                    list.add(Thread.currentThread().getName());
                }
            }).start();
        }
        try {
            Thread.sleep(1000);
            System.out.println(list.size());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

CopyOnWriteArray

CopyOnWriteArrayList是ArrayList 的一個線程安全的變體,其中所有可變操作(add、set等等)都是通過對底層數組進行一次新的複製來實現的。

import java.util.concurrent.CopyOnWriteArrayList;

public class CopyOnWriteArrayListTest {
    public static void main(String[] args) {
        CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<String>();
        for (int i = 0; i < 10000; i++) {
            new Thread(() -> {
                list.add(Thread.currentThread().getName());
            }).start();
        }
        try {
            Thread.sleep(1000);
            System.out.println(list.size());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章