Java 線程管理和同步方法彙總

1.Semaphore信號量,所有線程構造的時候共用同一個信號量,在進入同步代碼快之前,先獲得信號量.示例代碼:

package com.cn.sychonized.concurrent;

import java.util.concurrent.Semaphore;

public class SeDemo {

    public static void main(String[] args) {
        /** 允許3個線程同時訪問 */
        Semaphore semaphore = new Semaphore(3);

        Person p1 = new Person(semaphore, "A");
        p1.start();
        Person p2 = new Person(semaphore, "B");
        p2.start();
        Person p3 = new Person(semaphore, "C");
        p3.start();
    }

}

class Person extends Thread {

    private Semaphore semaphore;

    public Person(Semaphore semaphore, String name) {
        setName(name);
        this.semaphore = semaphore;
    }

    public void run() {
        System.out.println(getName() + " is waiting ....");
        try {
            /** 獲取信號量(獲取運行許可證) */
            semaphore.acquire();
            System.out.println(getName() + " is servicing...");
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(getName() + " is done!");
        /** 使用完之後釋放,以便其他線程能獲取到 */
        semaphore.release();
    }

}

執行結果:

B is waiting ....
C is waiting ....
B is servicing...
C is servicing...
A is waiting ....
A is servicing...
B is done!
A is done!
C is done!

2.synchronized,已經用的不要不要了,鎖方法,鎖代碼塊,同一時刻只能有一個線程進入synchronized代碼塊

    public synchronized void run() {
        System.out.println(getName() + " is waiting ....");
        try {
            System.out.println(getName() + " is servicing...");
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(getName() + " is done!");
    }
    public synchronized void run() {
        System.out.println(getName() + " is waiting ....");
        try {
            System.out.println(getName() + " is servicing...");
            synchronized (semaphore) {
                Thread.sleep(1000);

            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(getName() + " is done!");
    }

3.CountDownLatch,一個同步輔助類,在完成一組正在其他線程中執行的操作之前,它允許一個或多個線程一直等待。
主要方法
public CountDownLatch(int count);
public void countDown();
public void await() throws InterruptedException

構造方法參數指定了計數的次數
countDown方法,當前線程調用此方法,則計數減一
await方法,調用此方法會一直阻塞當前線程,直到計時器的值爲0

public class CountDownLatchDemo {
    final static SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    public static void main(String[] args) throws InterruptedException {
        CountDownLatch latch=new CountDownLatch(2);//兩個工人的協作
        Worker worker1=new Worker("zhang san", 5000, latch);
        Worker worker2=new Worker("li si", 8000, latch);
        worker1.start();//
        worker2.start();//
        latch.await();//等待所有工人完成工作
        System.out.println("all work done at "+sdf.format(new Date()));
    }


    static class Worker extends Thread{
        String workerName; 
        int workTime;
        CountDownLatch latch;
        public Worker(String workerName ,int workTime ,CountDownLatch latch){
             this.workerName=workerName;
             this.workTime=workTime;
             this.latch=latch;
        }
        public void run(){
            System.out.println("Worker "+workerName+" do work begin at "+sdf.format(new Date()));
            doWork();//工作了
            System.out.println("Worker "+workerName+" do work complete at "+sdf.format(new Date()));
            latch.countDown();//工人完成工作,計數器減一

        }

        private void doWork(){
            try {
                Thread.sleep(workTime);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }


}
Worker zhang san do work begin at 2016-03-13 12:14:11
Worker li si do work begin at 2016-03-13 12:14:11
Worker zhang san do work complete at 2016-03-13 12:14:16
Worker li si do work complete at 2016-03-13 12:14:19
all work done at 2016-03-13 12:14:19

CountDownLatch也可以實現當所有任務都創建好以後,統一執行。

4.CyclicBarrier等待所有線程完成工作統一回調,它跟CountDownLatch實現的功能差不多,具體看實例代碼

package com.cn.thread;

import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;

public class MainDemo {
    public static void main(String[] args) {
        CyclicBarrier cyclicBarrier = new CyclicBarrier(3, new Runnable() {

            @Override
            public void run() {
                System.out.println("Now All Thread is Complete ");
            }
        });

        new MyThread(cyclicBarrier, "A").start();
        new MyThread(cyclicBarrier, "B").start();
        new MyThread(cyclicBarrier, "C").start();
    }
}

class MyThread extends Thread {
    CyclicBarrier cyclicBarrier;

    public MyThread(CyclicBarrier cyclicBarrier, String name) {
        setName(name);
        this.cyclicBarrier = cyclicBarrier;
    }

    public void run() {
        System.out.println(getName() + " is Started");
        try {
            sleep(1000);
            cyclicBarrier.await();
        } catch (InterruptedException | BrokenBarrierException e) {
            e.printStackTrace();
        }
    };
}

執行結果:

B is Started
A is Started
C is Started
Now All Thread is Complete 

如上所示,所有線程執行完成之後統一回調到CyclicBarrier的Runnable裏面。然後在此處做線程執行完之後的處理。

4.Exchanger交換器:Exchanger可以在兩個線程之間交換數據,只能是2個線程,他不支持更多的線程之間互換數據。

當線程A調用Exchange對象的exchange()方法後,他會陷入阻塞狀態,直到線程B也調用了exchange()方法,然後以線程安全的方式交換數據,之後線程A和B繼續運行

package com.cn.thread;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.Exchanger;

public class ThreadLocalTest {  

    public static void main(String[] args) {  
        Exchanger<List<Integer>> exchanger = new Exchanger<>();  
        new Consumer(exchanger).start();  
        new Producer(exchanger).start();  
    }  

}  

class Producer extends Thread {  
    List<Integer> list = new ArrayList<>();  
    Exchanger<List<Integer>> exchanger = null;  
    public Producer(Exchanger<List<Integer>> exchanger) {  
        super();  
        this.exchanger = exchanger;  
    }  
    @Override  
    public void run() {  
        Random rand = new Random();  
        for(int i=0; i<10; i++) {  
            list.clear();  
            list.add(rand.nextInt(10000));  
            list.add(rand.nextInt(10000));  
            list.add(rand.nextInt(10000));  
            list.add(rand.nextInt(10000));  
            list.add(rand.nextInt(10000));  

            try {  
                list = exchanger.exchange(list);  

            } catch (InterruptedException e) {  
                // TODO Auto-generated catch block  
                e.printStackTrace();  
            }  
        }  
    }  
}  

class Consumer extends Thread {  
    List<Integer> list = new ArrayList<>();  
    Exchanger<List<Integer>> exchanger = null;  
    public Consumer(Exchanger<List<Integer>> exchanger) {  
        super();  
        this.exchanger = exchanger;  
    }  
    @Override  
    public void run() {  
        for(int i=0; i<10; i++) {  
            try {  
                list = exchanger.exchange(list);  
            } catch (InterruptedException e) {  
                // TODO Auto-generated catch block  
                e.printStackTrace();  
            }  
            System.out.print(list.get(0)+", ");  
            System.out.print(list.get(1)+", ");  
            System.out.print(list.get(2)+", ");  
            System.out.print(list.get(3)+", ");  
            System.out.println(list.get(4)+", ");  
        }  
    }  
}  

執行結果:

1, 2, 3, 4, 5, 
1, 2, 3, 4, 5, 
1, 2, 3, 4, 5, 
1, 2, 3, 4, 5, 
1, 2, 3, 4, 5, 
1, 2, 3, 4, 5, 
1, 2, 3, 4, 5, 
1, 2, 3, 4, 5, 
1, 2, 3, 4, 5, 
1, 2, 3, 4, 5, 

Producer產生的值在Consumer中打印出來了,Consumer先運行到list = exchanger.exchange(list); 然後等待Producer運行到list = exchanger.exchange(list),然後兩者數據交換,繼續往下執行。

5.Phaser
Java 7的併發包中推出了Phaser,其功能跟CyclicBarrier和CountDownLatch有些重疊,但是提供了更靈活的用法,例如支持動態調整註冊任務的數量等。下面我們介紹一下幾個使用場景,看例子大家有明白了。

場景一:我們希望控制多個線程的啓動時機:例如在併發相關的單元測試中,有時需要控制線程的啓動時機,以期獲得最大程度的併發,通常我們會使用CountDownLatch,以下是使用Phaser的版本。

package com.cn.thread;
import java.util.concurrent.Phaser;

public class PhaserTest1 {

    public static void main(String args[]) {
        //
        final int count = 5;
        final Phaser phaser = new Phaser(count);
        for(int i = 0; i < count; i++) {
            System.out.println("starting thread, id: " + i);
            final Thread thread = new Thread(new Task(i, phaser));
            thread.start();
        }
    }

    public static class Task implements Runnable {
        //
        private final int id;
        private final Phaser phaser;

        public Task(int id, Phaser phaser) {
            this.id = id;
            this.phaser = phaser;
        }

        @Override
        public void run() {
            phaser.arriveAndAwaitAdvance();
            System.out.println("in Task.run(), phase: " + phaser.getPhase() + ", id: " + this.id);
        }
    }
}

以上例子中,由於線程是在一個循環中start,因此start的時機有一定的間隔。本例中這些線程實際開始工作的時機是在所有的線程都調用了phaser.arriveAndAwaitAdvance()之後。本例的意思的是等待所有線程都就緒以後,然後再開始統一運行。執行結果如下:

starting thread, id: 0
starting thread, id: 1
starting thread, id: 2
starting thread, id: 3
starting thread, id: 4
in Task.run(), phase: 1, id: 0
in Task.run(), phase: 1, id: 2
in Task.run(), phase: 1, id: 1
in Task.run(), phase: 1, id: 3
in Task.run(), phase: 1, id: 4

等待所有的任務創建好之後,纔開始執行run方法裏面的內容,達到真正的同步。
如果沒有phaser.arriveAndAwaitAdvance();控制,執行結果如下:

starting thread, id: 0
starting thread, id: 1
in Task.run(), phase: 0, id: 0
starting thread, id: 2
starting thread, id: 3
in Task.run(), phase: 0, id: 1
starting thread, id: 4
in Task.run(), phase: 0, id: 2
in Task.run(), phase: 0, id: 3
in Task.run(), phase: 0, id: 4

這樣,某些線程已經先於其他線程執行了。

場景二:
有些時候我們希望只有在某些外部條件滿足時,才真正開始任務的執行,例如:

package com.cn.thread;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.concurrent.Phaser;

public class PhaserTest2 {

    public static void main(String args[]) throws Exception {
        //
        final Phaser phaser = new Phaser(1);
        for(int i = 0; i < 5; i++) {
            phaser.register();
            System.out.println("starting thread, id: " + i);
            final Thread thread = new Thread(new Task(i, phaser));
            thread.start();
        }

        //
        System.out.println("Press ENTER to continue");
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        reader.readLine();
        phaser.arriveAndDeregister();
    }

    public static class Task implements Runnable {
        //
        private final int id;
        private final Phaser phaser;

        public Task(int id, Phaser phaser) {
            this.id = id;
            this.phaser = phaser;
        }

        @Override
        public void run() {
            phaser.arriveAndAwaitAdvance();
            System.out.println("in Task.run(), phase: " + phaser.getPhase() + ", id: " + this.id);
        }
    }
}

所有線程都創建準備好了,等待你發命令,我們就開始跑,這個時候可以初始化一些必要條件然後讓他們跑起來,輸出結果如下:

starting thread, id: 0
starting thread, id: 1
starting thread, id: 2
starting thread, id: 3
starting thread, id: 4
Press ENTER to continue

in Task.run(), phase: 1, id: 3
in Task.run(), phase: 1, id: 1
in Task.run(), phase: 1, id: 0
in Task.run(), phase: 1, id: 4
in Task.run(), phase: 1, id: 2

場景三:
在Phaser類中,我們在每個線程中,每個線程進行完一個階段完成後都會等待其他線程完成後再一起進行,當所有線程都完成了一個任務的時候,會調用Phaser的onAdvance方法,如果我們想在每個階段,所有線程都完成他們的階段工作後做點啥事的話,那就得繼承Phaser類來重寫OnAdvance這個方法來實現我們的目的,下面我們用一個例子來說明,例子就是模擬多個學生考試,考試分爲三個階段,每個階段完成後,都會等待所有的學生完成,同時我們希望在每一個階段,所有的學生完成一個階段的任務後打印出幾句話,下面看代碼。

package com.cn.thread;

import java.util.concurrent.Phaser;

public class PhaserTest3 {

    public static void main(String args[]) throws Exception {
        //
        final int count = 5;
        final int phaseToTerminate = 3;
        final Phaser phaser = new Phaser(count) {
            @Override
            protected boolean onAdvance(int phase, int registeredParties) {
                System.out.println("====== " + phase + " ======");
                return phase >= phaseToTerminate || registeredParties == 0;
            }
        };

        //
        for(int i = 0; i < count; i++) {
            System.out.println("starting thread, id: " + i);
            final Thread thread = new Thread(new Task(i, phaser));
            thread.start();
        }
    }

    public static class Task implements Runnable {
        //
        private final int id;
        private final Phaser phaser;

        public Task(int id, Phaser phaser) {
            this.id = id;
            this.phaser = phaser;
        }

        @Override
        public void run() {
            do {
                try {
                    Thread.sleep(500);
                } catch(InterruptedException e) {
                    // NOP
                }
                System.out.println("in Task.run(), phase: " + phaser.getPhase() + ", id: " + this.id);
                phaser.arriveAndAwaitAdvance();
            } while(!phaser.isTerminated());
        }
    }
}

先查看執行結果:

starting thread, id: 0
starting thread, id: 1
starting thread, id: 2
starting thread, id: 3
starting thread, id: 4
in Task.run(), phase: 0, id: 3
in Task.run(), phase: 0, id: 1
in Task.run(), phase: 0, id: 0
in Task.run(), phase: 0, id: 4
in Task.run(), phase: 0, id: 2
====== 0 ======
in Task.run(), phase: 1, id: 2
in Task.run(), phase: 1, id: 4
in Task.run(), phase: 1, id: 0
in Task.run(), phase: 1, id: 1
in Task.run(), phase: 1, id: 3
====== 1 ======
in Task.run(), phase: 2, id: 1
in Task.run(), phase: 2, id: 4
in Task.run(), phase: 2, id: 3
in Task.run(), phase: 2, id: 2
in Task.run(), phase: 2, id: 0
====== 2 ======
in Task.run(), phase: 3, id: 4
in Task.run(), phase: 3, id: 0
in Task.run(), phase: 3, id: 3
in Task.run(), phase: 3, id: 1
in Task.run(), phase: 3, id: 2
====== 3 ======

線程非常有序的一個輪迴一個輪迴的執行,當onAdvance返回false,則認爲phaser終止了,while循環結束.

場景四:
在場景三的例子中,主線程在其它工作線程結束之前已經終止。如果希望主線程等待這些工作線程結束,除了使用Thread.join()之外,也可以嘗試以下的方式:

package com.cn.thread;

import java.util.concurrent.Phaser;

public class PhaserTest4 {

    public static void main(String args[]) throws Exception {
        //
        final int count = 5;
        final int phaseToTerminate = 3;
        final Phaser phaser = new Phaser(count) {
            @Override
            protected boolean onAdvance(int phase, int registeredParties) {
                System.out.println("====== " + phase + " ======");
                return phase == phaseToTerminate || registeredParties == 0;
            }
        };

        //
        for(int i = 0; i < count; i++) {
            System.out.println("starting thread, id: " + i);
            final Thread thread = new Thread(new Task(i, phaser));
            thread.start();
        }

        //
        phaser.register();
        while (!phaser.isTerminated()) {
            phaser.arriveAndAwaitAdvance();
        }
        System.out.println("done");
    }

    public static class Task implements Runnable {
        //
        private final int id;
        private final Phaser phaser;

        public Task(int id, Phaser phaser) {
            this.id = id;
            this.phaser = phaser;
        }

        @Override
        public void run() {
            while(!phaser.isTerminated()) {
                try {
                    Thread.sleep(500);
                } catch(InterruptedException e) {
                    // NOP
                }
                System.out.println("in Task.run(), phase: " + phaser.getPhase() + ", id: " + this.id);
                phaser.arriveAndAwaitAdvance();
            }
        }
    }
}

執行結果如下:

starting thread, id: 0
starting thread, id: 1
starting thread, id: 2
starting thread, id: 3
starting thread, id: 4
in Task.run(), phase: 0, id: 0
in Task.run(), phase: 0, id: 3
in Task.run(), phase: 0, id: 1
in Task.run(), phase: 0, id: 2
in Task.run(), phase: 0, id: 4
====== 0 ======
in Task.run(), phase: 1, id: 2
in Task.run(), phase: 1, id: 0
in Task.run(), phase: 1, id: 1
in Task.run(), phase: 1, id: 3
in Task.run(), phase: 1, id: 4
====== 1 ======
in Task.run(), phase: 2, id: 3
in Task.run(), phase: 2, id: 0
in Task.run(), phase: 2, id: 2
in Task.run(), phase: 2, id: 1
in Task.run(), phase: 2, id: 4
====== 2 ======
in Task.run(), phase: 3, id: 0
in Task.run(), phase: 3, id: 2
in Task.run(), phase: 3, id: 1
in Task.run(), phase: 3, id: 3
in Task.run(), phase: 3, id: 4
====== 3 ======
done

場景五:
爲了不讓一個Phaser關聯太多的任務,我們可以採用分而治之的方法,讓多個Phaser來組處理這些任務,如下例子:

package com.cn.thread;

import java.util.concurrent.Phaser;

public class PhaserTest6 {
    //
    private static final int TASKS_PER_PHASER = 4;

    public static void main(String args[]) throws Exception {
        //
        final int phaseToTerminate = 3;
        final Phaser phaser = new Phaser() {
            @Override
            protected boolean onAdvance(int phase, int registeredParties) {
                System.out.println("====== " + phase + " ======");
                return phase == phaseToTerminate || registeredParties == 0;
            }
        };

        //
        final Task tasks[] = new Task[10];
        build(tasks, 0, tasks.length, phaser);
        for (int i = 0; i < tasks.length; i++) {
            System.out.println("starting thread, id: " + i);
            final Thread thread = new Thread(tasks[i]);
            thread.start();
        }
    }

    public static void build(Task[] tasks, int lo, int hi, Phaser ph) {
        /**限制每個Phaser只能跑TASKS_PER_PHASER個任務,否則創建新的Phaser關聯任務*/
        if (hi - lo > TASKS_PER_PHASER) {
            for (int i = lo; i < hi; i += TASKS_PER_PHASER) {
                int j = Math.min(i + TASKS_PER_PHASER, hi);
                build(tasks, i, j, new Phaser(ph));
            }
        } else {
            for (int i = lo; i < hi; ++i)
                tasks[i] = new Task(i, ph);
        }
    }

    public static class Task implements Runnable {
        //
        private final int id;
        private final Phaser phaser;

        public Task(int id, Phaser phaser) {
            this.id = id;
            this.phaser = phaser;
            this.phaser.register();
        }

        @Override
        public void run() {
            while (!phaser.isTerminated()) {
                try {
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                    // NOP
                }
                System.out.println("in Task.run(), phase: " + phaser.getPhase()    + ", id: " + this.id);
                phaser.arriveAndAwaitAdvance();
            }
        }
    }
}

需要注意的是,TASKS_PER_PHASER的值取決於具體的Task實現。對於Task執行時間很短的場景(也就是競爭相對激烈),可以考慮使用較小的TASKS_PER_PHASER值,例如4。反之可以適當增大TASKS_PER_PHASER。

5.執行器管理線程
最常見的線程池

package com.cn.thread;

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

public class ExcutorTest {
     public static void main(String[] args) {
         ExecutorService exes = Executors.newCachedThreadPool();
         for(int i=0; i<5; ++i)
             exes.execute(new MThread());
         exes.shutdown(); 
    }
}

class MThread implements Runnable{
    private static int num = 0;
    @Override
    public void run() {
        while(true){
            synchronized(MThread.class){
                ++num;
                try{
                    Thread.sleep(500);
                } catch(Exception e){
                    System.out.println(e.toString());
                }
                System.out.println(Thread.currentThread().getName() + " " + num);
            }
        }
    }
}

線程池的幾種創建模式:
1、newFixedThreadPool創建一個指定工作線程數量的線程池。每當提交一個任務就創建一個工作線程,如果工作線程數量達到線程池初始的最大數,則將提交的任務存入到池隊列中。
2、newCachedThreadPool創建一個可緩存的線程池。這種類型的線程池特點是:
1).工作線程的創建數量幾乎沒有限制(其實也有限制的,數目爲Interger. MAX_VALUE), 這樣可靈活的往線程池中添加線程。
2).如果長時間沒有往線程池中提交任務,即如果工作線程空閒了指定的時間(默認爲1分鐘),則該工作線程將自動終止。終止後,如果你又提交了新的任務,則線程池重新創建一個工作線程。
3、newSingleThreadExecutor創建一個單線程化的Executor,即只創建唯一的工作者線程來執行任務,如果這個線程異常結束,會有另一個取代它,保證順序執行。單工作線程最大的特點是可保證順序地執行各個任務,並且在任意給定的時間不會有多個線程是活動的 。
4、newScheduleThreadPool創建一個定長的線程池,而且支持定時的以及週期性的任務執行,類似於Timer。(這種線程池原理暫還沒完全瞭解透徹)

6.實現Callable接口:
Callable接口類似於Runnable,從名字就可以看出來了,但是Runnable不會返回結果,並且無法拋出返回結果的異常,而Callable功能更強大一些,被線程執行後,可以返回值,這個返回值可以被Future拿到,也就是說,Future可以拿到異步執行任務的返回值,下面來看一個簡單的例子:

package com.cn.thread;

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

public class CallableTest {
    public static void main(String[] args) {
        Callable<String> callable = new Callable<String>() {
            public String call() throws Exception {
                return "Hello World!";
            }
        };
        FutureTask<String> future = new FutureTask<String>(callable);
        new Thread(future).start();
        try {
            System.out.println(future.get());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
}

執行結果:

Hello World!

FutureTask實現了兩個接口,Runnable和Future,所以它既可以作爲Runnable被線程執行,又可以作爲Future得到Callable的返回值,那麼這個組合的使用有什麼好處呢?假設有一個很耗時的返回值需要計算,並且這個返回值不是立刻需要的話,那麼就可以使用這個組合,用另一個線程去計算返回值,而當前線程在使用這個返回值之前可以做其它的操作,等到需要這個返回值時,再通過Future得到。

5.同步鎖和原子操作
兩種方法都能保證同步

package com.cn.sychonized.concurrent;

import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class LDemo {

    public static void main(String[] args) {
        new MT().start();
        new MT().start();
        new MT().start();
        new MT().start();
    }

}

class Data {

    static int i = 0;
    static Lock lock = new ReentrantLock();
    static AtomicInteger ai = new AtomicInteger(0);

    static void operate() {
        System.out.println(ai.incrementAndGet());
//        lock.lock();
//        i++;
//        System.out.println(i);
//        lock.unlock();
    }

}

class MT extends Thread {
    public void run() {
        while (true) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            Data.operate();
        }
    }
}

執行結果:

1
3
2
4
5
7
6
8
9
10
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章