java併發編程之再學習

java併發基礎

Callable

首先我們來理一理Future和Runable的關係:

public class FutureTask<V> implements RunnableFuture<V>

public interface RunnableFuture<V> extends Runnable, Future<V> {       
    void run(); 
}

也就是說FutureTask 間接的 實現了Runable

而利用FutureTask:我們就有了線程進行,取消、判斷線程運行狀態、獲取結果等功能。實現如下:

public interface Future<V> {       

		boolean cancel(boolean mayInterruptIfRunning);       

		boolean isCancelled();       

		boolean isDone();       

		V get();       

		V get(long timeout, TimeUnit unit); 

}

FutureTask 間接的 實現了Runable : 故我們調用 的 Callable 中的call方法,其實是在Runable裏面的run()方法中執行的;

public void run() {
    ......
        try {
            Callable<V> c = callable;
            if (c != null && state == NEW) {
                V result;
                boolean ran;
                try {
                    result = c.call();
                    ran = true;
                } catch (Throwable ex) {
                    ......
                }
                if (ran)
                    set(result);
            }
        } finally {
            ......
        }
}

接下來是HelloCallabe的簡單操作:

public class HelloThread{
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        CallableTest callableTest = new CallableTest();
        FutureTask<Integer> ft = new FutureTask<Integer>(callableTest);

        Thread thread = new Thread(ft);
        thread.start();
        System.out.println(ft.get());
    }
}

class CallableTest implements Callable<Integer>{
    @Override
    public Integer call() throws Exception {
        return 123;
    }
}

線程的停止stop()

想要線程停止,我們最好在內部調用使其停止,簡單實現如下:

結合Thred.sleep()的一個倒計時功能;

具體還是stop()方法

public class ThreadStop {
    public static void main(String[] args) throws InterruptedException {
        Test test = new Test();
        new Thread(test).start();
        for(int i = 0; i < 10; i++){
            Thread.sleep(1000);
            System.out.println(Thread.currentThread().getName() + i);
        }
        test.stop();
    }
}
class Test implements Runnable{
    boolean flag = true;
    @Override
    public void run() {
        while(flag){
            try {
                Thread.sleep(1000);
                Date date = new Date();
                System.out.println(new SimpleDateFormat("HH:mm:ss").format(date));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    public void stop(){
        this.flag = false;
    }
}

線程的禮讓yield()

對靜態方法 Thread.yield() 的調用聲明瞭當前線程已經完成了生命週期中最重要的部分,可以切換給其它線程來執行。該方法只是對線程調度器的一個建議,而且也只是建議具有相同優先級的其它線程可以運行。

public class Yield {
    public static void main(String[] args) {
        new Thread(new R(),"a").start();
        new Thread(new R(),"b").start();
    }
}

class R implements Runnable{

    @Override
    public void run() {
        System.out.println("+++++++++開始了++++++++++++" + Thread.currentThread().getName());
        Thread.yield();
        System.out.println("+++++++++結束++++++++++++" + Thread.currentThread().getName());
    }
}

線程的強制插入Join()

public class Join {
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(new R1());
        thread.start();
        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName() + i);
            if(i == 20)thread.join();
        }
    }
}
class R1 implements Runnable{

    @Override
    public void run() {
        int i = 100;
        while(i-- > 0){
            System.out.println("+++++++++開始了++++++++++++" + Thread.currentThread().getName());
            System.out.println("+++++++++結束++++++++++++" + Thread.currentThread().getName());
        }
    }
}

使用state觀察線程狀態:

Therad.getState();
System.out.println(state);

線程設置優先級setPriority()

public class Priority {
    public static void main(String[] args) {
        Thread t1 = new Thread(new R3(),"t1");
        Thread t2 = new Thread(new R3(),"t2");
        Thread t3 = new Thread(new R3(),"t3");
        Thread t4 = new Thread(new R3(),"t4");
        Thread t5 = new Thread(new R3(),"t5");
        Thread t6 = new Thread(new R3(),"t6");
        t1.setPriority(1);
        t1.start();
        t2.setPriority(2);
        t2.start();
        t3.setPriority(3);
        t3.start();
        t4.setPriority(5);
        t4.start();
        t5.setPriority(7);
        t5.start();
        t6.setPriority(10);
        t6.start();
    }
}
class R3 implements Runnable{

    @Override
    public void run() {
        try {
            Thread.sleep(10);
            System.out.println(Thread.currentThread().getName() + "正在運行 " + Thread.currentThread().getPriority());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }
}

守護線程deamon

  • 線程分爲用戶線程守護線程
  • 虛擬機必須確保用戶線程執行完畢
  • 虛擬機不用等待守護線程執行完畢
  • 如,操作日誌,監控內存,垃圾回收等

synchronized

  • 鎖方法
  • 索同步代碼塊(對象)

Lock

  • Lock只能鎖代碼塊,但是其性能更好
public class HelloTecket {
    public static void main(String[] args) throws InterruptedException {
        Tic tic = new Tic();
        new Thread(tic,"t1").start();
        new Thread(tic,"t2").start();
        new Thread(tic,"t3").start();


    }
}

class Tic implements Runnable{
    static int ticket = 10;
    static boolean flag = true;
    private final ReentrantLock lock = new ReentrantLock();
    @Override
    public void run() {
        while(flag){
            try{
                lock.lock();
                if(ticket > 0){
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("售出了第" + ticket-- + " 張票" + Thread.currentThread().getName());
                }else{
                    stop();
                }
            }finally {
                lock.unlock();
            }
        }
    }
    private static void stop(){
        flag = false;
    }
}

多線程生產者消費者簡單模型

public class Pro {
    public static void main(String[] args) throws InterruptedException {
        Food food = new Food();
        new Thread(new Producer(food)).start();
        new Thread(new Customer(food)).start();
    }

}

enum F{
    APPLE("蘋果"),CHEERY("櫻桃"),MANGO("芒果"),WATERMELON("西瓜");
    private String name;

    private F(String name){
        this.name = name;
    }
}

class Food{
    private String[] Fruit = new String[12];
    int count = 0;
    private Random random = new Random();
    public synchronized void add() throws InterruptedException {
        if(count >= 10) {
            System.out.println("緩衝區已滿++++++無法生產");
            super.wait();
        }
        else{
            Thread.sleep(100);
            int rand = random.nextInt(4);
            switch (rand){
                case 0: Fruit[++count] = F.APPLE.toString();break;
                case 1: Fruit[++count] = F.CHEERY.toString();break;
                case 2: Fruit[++count] = F.MANGO.toString();break;
                case 3: Fruit[++count] = F.WATERMELON.toString();break;
            }
            System.out.println("已生成第" + count + " 個水果" + Fruit[count]);
            super.notifyAll();
        }
    }

    public synchronized String pop() throws InterruptedException {
        if(count <= 0){
            super.wait();
            return "沒有食物 : " + count;
        }else{
            Thread.sleep(100);
            super.notifyAll();
            int temp = count;
            String fruit = Fruit[count--];
            return "得到食物 : " + fruit + "他是第 " + temp + "個";
        }
    }

    void test(){
        for (String s : Fruit) {
            System.out.println(s);
        }
    }
}

class Producer implements Runnable{
    private Food food;
    public Producer (Food food){
        this.food = food;
    }
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            try {
                food.add();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

    }
}


class Customer implements Runnable{
    private Food food;
    public Customer(Food food){
        this.food = food;
    }
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            try {
                System.out.println(food.pop());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

模擬數字的加減,使Num保持在0,1中

public class MathThread {
    public static void main(String[] args) {
        Resource re = new Resource();
        Add add = new Add(re);
        Sub sub = new Sub(re);
        new Thread(add, "加法線程--A").start();
        new Thread(add, "加法線程--B").start();

        new Thread(sub, "減法線程---A").start();
        new Thread(sub, "減法線程---B").start();
    }
}
class Resource {
    private volatile int num = 0;
    private boolean flag = true;
    public synchronized void add() throws Exception{
        while(this.flag == false){
            super.wait();
        }
        Thread.sleep(10);
        this.num++;
        System.out.println(Thread.currentThread().getName() + " " + this.num);

        this.flag = false;
        super.notify();
    }

    public synchronized void sub() throws Exception{
        while(this.flag == true){
            super.wait();
        }
        Thread.sleep(10);
        this.num--;
        System.out.println(Thread.currentThread().getName() + " " + this.num);

        this.flag = true;
        super.notify();
    }
}

class Add implements Runnable{
    private Resource resource;
    public Add(Resource resource){
        this.resource = resource;
    }
    @Override
    public void run() {
        for (int i = 0; i < 30; i++) {
            try {
                this.resource.add();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

    }
}


class Sub implements Runnable{
    private Resource resource;
    public Sub(Resource resource){
        this.resource = resource;
    }
    @Override
    public void run() {
        for (int i = 0; i < 30; i++) {
            try {
                this.resource.sub();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

    }
}

被喚醒的線程,應該被繼續檢測,所以永遠在while循環而不是if語句中使用wait!

volatile

  • 主要定義在屬性上的,表示此屬性爲直接操作,不進行副本處理

普通變量操作過程:

  1. 獲取原有變量的副本
  2. 對副本進行操作
  3. 將操作後的結果,保存到原始空間中

加上volatile的變量,表示直接操作原始變量,節約了拷貝副本的時間。

併發深入理解

synchronized

實現原理:保證方法或者代碼塊在運行時,同一時刻只有一個方法可以進入到臨界區

使用方式:(重點)

  1. 定義在普通方法上: 表示進入同步代碼前,要獲得當前實例的鎖。
  2. 靜態同步方法: 表示是當前的class類。
  3. 同步方法塊: 表示鎖的是 括號裏的對象。

synchronized作用於靜態方法

public class TestTread {

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

        /**
         * 在普通方法上加鎖,鎖的是同一個實例
         * 故操作同一個實例,有效。
         */
        new Thread(()->test.method1()).start();
        new Thread(()->test.method2()).start();


        /**
         * 不同的實例,失效。
         */
//        new Thread(()->new syncTest().method1()).start();
//        new Thread(()->new syncTest().method2()).start();
    }
}
class syncTest{
    public synchronized void method1() {
        System.out.println("Method 1 start");
        try {
            System.out.println("Method 1 execute");
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("Method 1 end");
    }
    public synchronized void method2() {
        System.out.println("Method 2 start");
        try {
            System.out.println("Method 2 execute");
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("Method 2 end");
    }
}

synchronized作用於靜態方法

public class StaticThread{
    //共享資源
    static int i = 0;

    /**
     * synchronized 修飾實例方法
     */
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(new St(),"s1");
        Thread t2 = new Thread(new St(),"s2");
        t1.start();
        t2.start();
        t1.join(); //加上join的使 主線程進入等待池,需要等待t1,t2執行完畢,方便後面的St.i 靜態變量的輸出
        t2.join();
        Thread.sleep(2000);
        System.out.println(St.i);
    }
}
class St implements Runnable {
    static int i = 0;
    public static synchronized void increase(){
        try {
            Thread.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        i++;
    }

    @Override
    public void run() {
        for (int j = 0; j < 100; j++) {
            System.out.println(Thread.currentThread().getName() + " " +  i);
            increase();
        }
    }
}

synchronized作用於同步代碼塊

public class BlockThread implements Runnable {
    static BlockThread instance=new BlockThread();
    static int i=0;
    @Override
    public void run() {
        //省略其他耗時操作....
        //使用同步代碼塊對變量i進行同步操作,鎖對象爲instance
        synchronized(instance){  // 可修改爲 this 或者 當前類的 class
            for(int j=0;j<10000;j++){
                i++;
            }
        }
    }
    public static void main(String[] args) throws InterruptedException {
        Thread t1=new Thread(instance);
        Thread t2=new Thread(instance);
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        System.out.println(i);
    }
}

join()方法

大部分看起來好像是:t.join()方法會使所有其它線程都暫停並等待t的執行完畢。

經過上面 :synchronized作用於靜態方法 代碼看到結果。

t1.start();
t2.start();
t1.join(); 
t2.join();

上述順序中,t1,t2交替運行。

而若改成:

t1.start();
t1.join(); 
t2.start();
t2.join();

則是 t1先執行完畢,t2再執行完畢。

join()源代碼:

/**
     * Waits for this thread to die.
     *
     * <p> An invocation of this method behaves in exactly the same
     * way as the invocation
     *
     * <blockquote>
     * {@linkplain #join(long) join}{@code (0)}
     * </blockquote>
     *
     * @throws  InterruptedException
     *          if any thread has interrupted the current thread. The
     *          <i>interrupted status</i> of the current thread is
     *          cleared when this exception is thrown.
     */
public final void join() throws InterruptedException {
    join(0);            //join()相當於調用了join(0)
}

public final synchronized void join(long millis) throws InterruptedException {
    long base = System.currentTimeMillis();
    long now = 0;

    if (millis < 0) {
        throw new IllegalArgumentException("timeout value is negative");
    }

    if (millis == 0) {
        while (isAlive()) {
            wait(0);           //join(0)等同於wait(0),即wait無限時間直到被notify
        }
    } else {
        while (isAlive()) {
            long delay = millis - now;
            if (delay <= 0) {
                break;
            }
            wait(delay);
            now = System.currentTimeMillis() - base;
        }
    }
}

借鑑其他博客得知:在哪個線程中調用 x.join() 則該線程掛起。

上述代碼在 主線程中調用t1.join(),故主線程掛起,等待t1執行完畢,才繼續執行下面代碼,所以若是這樣:

t1.start();
t2.start();
t1.join(); 
t2.join();

則t1,t2交互進行。

參考博文:https://blog.csdn.net/zjy15203167987/article/details/82531772

notify()notifyAll()

鎖池和等待池:

  • 鎖池:假設線程A已經擁有了某個對象(注意:不是類)的鎖,而其它的線程想要調用這個對象的某個synchronized方法(或者synchronized塊),由於這些線程在進入對象的synchronized方法之前必須先獲得該對象的鎖的擁有權,但是該對象的鎖目前正被線程A擁有,所以這些線程就進入了該對象的鎖池中。

  • 等待池:假設一個線程A調用了某個對象的wait()方法,線程A就會釋放該對象的鎖後,進入到了該對象的等待池中

鎖池:沒有得到鎖的線程,進入鎖池,等待釋放後,可以去競爭鎖。

等待池:被調用wait()進入等待池,不能去競爭鎖,必須先被喚醒,才能去競爭鎖。

notify喚醒一個等待的線程;notifyAll喚醒所有等待的線程。

參考博文:https://blog.csdn.net/djzhao/article/details/79410229

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