1. 程式人生 > >多執行緒併發庫(一)

多執行緒併發庫(一)

ThreadLocal

是執行緒區域性變數。在多執行緒中,實現每個執行緒中變數的私有性。

例子一

在該例子中,在同一個執行緒中通過呼叫類A和類B的getData()方法獲取的資料是一致的。

public class ThreadLocalTest {
static ThreadLocal<Integer> threadLocal = new ThreadLocal<>();
public static void main(String[] args) {
    final ThreadLocalTest tlt = new ThreadLocalTest();
    for(int i=0;i<3;i++){
        new Thread(new Runnable() {
            @Override
            public void run() {
                int data = new Random().nextInt();
                threadLocal.set(data);
                System.out.println(Thread.currentThread().getName()+"==生成的資料是:"+data);
                tlt.new A().getData();
                tlt.new B().getData();
            }
        }).start();
    }
}
class A{
    public void getData(){
        System.out.println(Thread.currentThread().getName()+"A:"+"取出的資料是:"+threadLocal.get());
    }
}
class B{
    public void getData(){
        System.out.println(Thread.currentThread().getName()+"B:"+"取出的資料是:"+threadLocal.get());
    }
}

** }
結果:**

Thread-1==生成的資料是:-229198565
Thread-2==生成的資料是:-1892334218
Thread-0==生成的資料是:-1126013147
Thread-0A:取出的資料是:-1126013147
Thread-1A:取出的資料是:-229198565
Thread-2A:取出的資料是:-1892334218
Thread-1B:取出的資料是:-229198565
Thread-2B:取出的資料是:-1892334218
Thread-0B:取出的資料是:-1126013147

例子二

實現每個執行緒只擁有一個例項物件的寫法。

public class ThreadLocalForObjTest {
static ThreadLocal<Student> threadLocal = new ThreadLocal<>();
public static void main(String[] args) {
    final ThreadLocalForObjTest tlt = new ThreadLocalForObjTest();
    for(int i=0;i<3;i++){
        new Thread(new Runnable() {
            @Override
            public void run() {
                int data = new Random().nextInt(150);
                Student s = Student.getInstance();
                s.setAge(data);
                s.setName("lzl"+data);
                threadLocal.set(s);
                System.out.println(Thread.currentThread().getName()+"==生成的資料是:"+data);
                tlt.new A().getData();
                tlt.new B().getData();
            }
        }).start();
    }
}
class A{
    public void getData(){
        System.out.println(Thread.currentThread().getName()+"A:"+"取出的資料是:"+threadLocal.get().toString());
    }
}
class B{
    public void getData(){
        System.out.println(Thread.currentThread().getName()+"B:"+"取出的資料是:"+threadLocal.get().toString());
    }
}
}
class Student{
private String name;
private int age;
private static ThreadLocal<Student> map = new ThreadLocal<Student>();
public static Student getInstance(){
    Student instance = map.get();
    if(instance ==null){
        instance = new Student();
        map.set(instance);
        System.out.println("======顯示次數========");
    }
    return instance;
}
public String getName() {
    return name;
}
public void setName(String name) {
    this.name = name;
}
public int getAge() {
    return age;
}
public void setAge(int age) {
    this.age = age;
}
@Override
public String toString() {
    return "Student [name=" + name + ", age=" + age + "]";
}
}

結果
三個執行緒,建立了三個例項物件。並且保證單個執行緒中的資料是私有的。

======顯示次數========
======顯示次數========
======顯示次數========
Thread-0==生成的資料是:81
Thread-1==生成的資料是:67
Thread-2==生成的資料是:81
Thread-2A:取出的資料是:Student [name=lzl81, age=81]
Thread-0A:取出的資料是:Student [name=lzl81, age=81]
Thread-1A:取出的資料是:Student [name=lzl67, age=67]
Thread-1B:取出的資料是:Student [name=lzl67, age=67]
Thread-0B:取出的資料是:Student [name=lzl81, age=81]
Thread-2B:取出的資料是:Student [name=lzl81, age=81]

多執行緒中共享資料

  1. 如果程式碼塊執行的邏輯相同,可以在一個繼承Runnable的類中實現資料的共享。
    兩個執行緒之間共享了count資料

    public class MultiThreadShareData {
    public static void main(String[] args) {
        MyRunnable mr = new MyRunnable();
        new Thread(mr).start();
        new Thread(mr).start();
    }
    static class MyRunnable implements Runnable{
        private int count=5;
        @Override
        public void run() {
            while(true){
                System.out.println("執行緒名:"+Thread.currentThread().getName()+" count="+count);
                if(count<1){
                    break;
                }
                dec();
            }
    
        }
        private synchronized int dec(){
            count--;
            return count;
        }
    }
    }
    

結果:

執行緒名:Thread-0 count=5
執行緒名:Thread-1 count=5
執行緒名:Thread-0 count=4
執行緒名:Thread-1 count=3
執行緒名:Thread-1 count=1
執行緒名:Thread-0 count=2
執行緒名:Thread-1 count=0
  1. 如果程式碼塊不同,就將資料封裝到同一個物件中,將這個物件逐一傳遞給每個Runnable物件。
    兩個執行緒之間共享了data資料

    public class MultiThreadShareData2 {
    public static void main(String[] args) {
        new MultiThreadShareData2().init();
    }
    private void init(){
        new Thread(new DecRunnable(this)).start();
        new Thread(new IncRunnable(this)).start();
    }
    int data=10;
    private synchronized void dec(){
    data--;
        System.out.println(Thread.currentThread().getName()+" data Dec="+data);
    }
    private synchronized void inc(){
        data++;
        System.out.println(Thread.currentThread().getName()+" data Inc="+data);
    }
    static class DecRunnable implements Runnable{
        private MultiThreadShareData2 data;
        public DecRunnable(MultiThreadShareData2 data2){
            this.data = data2;
        }
        @Override
        public void run() {
            while(true){
                data.dec();
            }
        }
    }
    static class IncRunnable implements Runnable{
        private MultiThreadShareData2 data;
        public IncRunnable(MultiThreadShareData2 data2){
            this.data = data2;
        }
        @Override
        public void run() {
            while(true){
                data.inc();
            }
        }
    }
    }
    

Executors的應用

  1. 建立固定大小的執行緒池

    ExecutorService fixedThreadPools = Executors.newFixedThreadPool(5);

  2. 建立快取執行緒池

    ExecutorService threadPools = (ExecutorService) 
                Executors.newCachedThreadPool();
    
  3. 建立單一執行緒池(保證記憶體中一直有一條執行緒)

    ExecutorService sigleThreadPools = Executors.newSingleThreadExecutor();

  4. 執行緒池啟動定時器

    ScheduledExecutorService threadPools = (ScheduledExecutorService) Executors.newScheduledThreadPool(3); //建立一個計時器的執行緒
        threadPools.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                System.out.println("hhh");
            }
        }, 2, 2, TimeUnit.SECONDS);
    }
    

    例子:

    public class ExecutorsTest {
    public static void main(String[] args) {
        ExecutorService threadPools = (ExecutorService) 
                Executors.newCachedThreadPool();//建立快取執行緒池。執行緒的大小沒有固定
        ExecutorService fixedThreadPools = Executors.newFixedThreadPool(5);//建立固定大小的執行緒池
        ExecutorService sigleThreadPools = Executors.newSingleThreadExecutor();//建立
        for(int i=0;i<10;i++){
            final int task = i;
            fixedThreadPools.execute(new Runnable() {
                @Override
                public void run() {
                    for(int i=0;i<10;i++){
                        System.out.println("執行緒名:"+Thread.currentThread().getName()+" 迴圈了"+i+"次,第"+task+"任務");
                    }
                }
            });
        }
        System.out.println("迴圈結束...");
        new ExecutorsTest().hh();
    }
    

Callable和Future

  1. 通過Executors的submit方法獲取執行緒中返回的結果。

    public class CallableAndFuture {
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newSingleThreadExecutor();
        Callable<String> callable = new Callable<String>() {
            @Override
            public String call() throws Exception {
                Thread.sleep(2000);
                return "Hello Thread!";
            }
        };
        Future<String> future = executorService.submit(callable);
        System.out.println("等待結果...");
        try {
            System.out.println("結果是:"+future.get(1,TimeUnit.SECONDS)); //1s後沒有拿到結果,就拋錯!
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    }
    
  2. CompletionService提交多組資料

    private void futureTest2(){
        ExecutorService executorService = Executors.newFixedThreadPool(5);
        CompletionService<Integer> completionService = new ExecutorCompletionService(executorService);
        for(int i=0;i<10;i++){
            final int task=i;
            completionService.submit(new Callable<Integer>() {
                @Override
                public Integer call() throws Exception {
                    Thread.sleep(new Random().nextInt(5000));
                    return task;
                }
            });
        }
        for(int i=0;i<10;i++){
            try {
                System.out.println("結果是:"+completionService.take().get());
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
        }
    }