1. 程式人生 > >Java學習(十一)

Java學習(十一)

nwr 定義 多線程操作 常用 yield 安全 cin try 過程

Java在設計之初就已經考慮到了線程的問題,因此Java可以有多種方式調用線程。

1.通過繼承線程類的方式調用線程。通過對函數public void run(){……}進行覆蓋來實現相關的程序

2.通過調用接口Runnable來調用線程。首先定義一個類,在類中定義一個函數為public void run(){……}用於存放相關程序。

3.通過Lambda表達式的匿名類來開啟新的線程。

具體代碼如下:

 1 package helloWorld;
 2 
 3 class AndyThread1 extends Thread {
 4     public void run()
 5     {
6 for(int i=0;i<20;i++) 7 { 8 System.out.print(i+" "); 9 try 10 { 11 Thread.sleep(1000); 12 }catch(Exception e) 13 { 14 e.printStackTrace(); 15 } 16 } 17 System.out.println();
18 } 19 20 } 21 22 class AndyThread2 implements Runnable 23 { 24 public void run() 25 { 26 for(int i=20;i<30;i++) 27 { 28 System.out.print(i+" "); 29 try 30 { 31 Thread.sleep(1000); 32 }catch(Exception e) 33 {
34 e.printStackTrace(); 35 } 36 } 37 System.out.println(); 38 } 39 }
 1                 AndyThread1 at1=new AndyThread1();
 2         AndyThread2 at2=new AndyThread2();
 3         Thread thread=new Thread(at2);
 4         new Thread(()->{
 5             for(int i=-10;i<0;i++)
 6             {
 7                 System.out.print(i+" ");
 8                 try{
 9                     Thread.sleep(1000);
10                 }catch(Exception e)
11                 {
12                     e.printStackTrace();
13                 }
14                 }
15             System.out.println();
16             }).start();
17             at1.start();
18             thread.start();
19 顯示結果為:
20 0 -10 20 1 -9 21 2 22 -8 -7 23 3 -6 4 24 25 -5 5 6 -4 26 27 -3 7 28 -2 8 -1 29 9 10 11 12 13 14 15 16 17 18 19 (結果不唯一,但形式一致)

實現Runnable接口比繼承Thread類所具有的優勢:

1):適合多個相同的程序代碼的線程去處理同一個資源

2):可以避免java中的單繼承的限制

3):增加程序的健壯性,代碼可以被多個線程共享,代碼和數據獨立

4):線程池只能放入實現Runable或callable類線程,不能直接放入繼承Thread的類

線程的狀態有:start、join、yield、run、notify

線程具有優先級,分別為MIN_PRIORITY(1)、NORMAL_PRIORITY(5)、MAX_PRIORITY(10)

線程中存在守護線程,可以利用函數setDeamon()將某個線程設置為守護線程。主線程退出後守護線程會自動中斷,否則守護線程會一直存在。

線程的調用過程有時會出現不同步的情況。因此,對於需要數據必須同步的情況,如生產消費者情況,則可以調用Java中鎖的概念,也稱為monitor。下面對其進行簡述。

鎖的概念是Java為了解決多個線程處理數據不同步的情況下提出來的,使用關鍵字為synchronize。可以在語句段前使用synchronize(this)或者synchronized(other)來聲明。如果整個函數都需要同步,那麽在函數前可以直接使用synchronized。在多個線程中,只有獲得鎖的線程才可以執行程序,否則就需要等待。用戶也可以設置線程的解鎖,可以使用wait()函數,在釋放鎖前利用notify()或者notifyAll()函數來通知其他線程可以獲取鎖了,其他線程的wait()函數自動解除。線程中還有比較常用的函數,如獲取線程狀態的函數getState()和getName()、getClass(),分別表示線程的狀態、獲取線程的名字、獲取線程所在的類名。

以下是生產者消費者代碼。在一個數組中指定數組長度為length,隨機產生的數字個數不能超過此數組長度範圍,逐個取出的數字也不能超過此數組的範圍。

 1 class ProductorConsumer extends Thread
 2 {
 3     private static int[] arr; 
 4     private static int index;
 5     public ProductorConsumer()
 6     {
 7         index=0;
 8         ProductorConsumer.arr=new int[5];//默認數組長度為5
 9     }
10     public ProductorConsumer(int length)
11     {
12         index=0;
13         ProductorConsumer.arr=new int[length];
14     }
15     public ProductorConsumer(int[] arr)
16     {
17         index=arr.length-1;
18         ProductorConsumer.arr=new int[arr.length];//自定義數組
19         ProductorConsumer.arr=arr;        
20     }
21     
22     synchronized public void productor() throws InterruptedException
23     {
24         if(index<arr.length)
25         {
26             arr[index]=(int) (10*Math.random());
27             System.out.printf("productor: arr[%d] = %d\n",index,arr[index]);
28             index++;
29             this.notifyAll();
30         }
31         else
32         {
33             System.out.println("productor: wait");
34             this.wait();                
35         }
36     }
37     
38     synchronized public void consumer() throws InterruptedException
39     {
40         if(index>=0)
41         {
42             index--;
43             System.out.printf("consumer: arr[%d] = %d\n",index,arr[index]);
44             this.notifyAll();
45         }
46         else
47         {
48             System.out.println("consumer: wait");
49             this.wait();
50         }
51     }
52 }
        ProductorConsumer pc;
        for (int i = 0; i < 10; i++) {
            pc = new ProductorConsumer();
            pc.start();
            try {
                pc.productor();
                pc.consumer();
            } catch (InterruptedException ite) {
                ite.printStackTrace();
            }
        }
結果為:
productor: arr[0] = 6
consumer: arr[0] = 6
productor: arr[0] = 5
consumer: arr[0] = 5
productor: arr[0] = 8
consumer: arr[0] = 8
productor: arr[0] = 3
consumer: arr[0] = 3
productor: arr[0] = 8
consumer: arr[0] = 8
productor: arr[0] = 1
consumer: arr[0] = 1
productor: arr[0] = 7
consumer: arr[0] = 7
productor: arr[0] = 4
consumer: arr[0] = 4
productor: arr[0] = 7
consumer: arr[0] = 7
productor: arr[0] = 7
consumer: arr[0] = 7

在Java.util.包中有一個類名為java.util.concurrent.atomic.AtomicInteger,可以很安全的解決多線程同時訪問造成的安全問題。直譯名為原子整數,即此整數不會同時被兩個線程訪問。

 1 class AtomicInte {
 2     static AtomicInteger ai = new AtomicInteger(0);
 3     static int n;
 4     static int num;
 5 
 6     public AtomicInte() {
 7         AtomicInte.n = 0;
 8         AtomicInte.num = 100;
 9     }
10 
11     public AtomicInte(int n, int num) {
12         AtomicInte.n = n;
13         AtomicInte.num = num;
14     }
15 
16     public void test() throws InterruptedException {
17         Thread[] thread = new Thread[num];
18         for (int i = 0; i < num; i++) {
19             thread[i] = new Thread() {
20                 public void run() {
21                     n++;
22                     ai.getAndIncrement();
23                 }
24             };
25         }
26         for (int i = 0; i < num; i++) {
27             thread[i].start();
28             Thread.sleep(1);//不加sleep會出錯,即同時開啟多個線程,中間沒有間斷,導致有些線程並未開啟
29         }
30     }
31 
32     public void print() {
33         System.out.println(n + " " + ai);
34     }
35 }
1         AtomicInte ai = new AtomicInte();
2         try {
3             ai.test();
4         } catch (InterruptedException ite) {
5             ite.printStackTrace();
6         }
7         ai.print();

Java.util.concurrent包中還有一些方便的類用於防止多線程同時操作所產生的錯誤。如CopyOnWriteArrayList、CopyOnWriteSet,適於很少寫入而讀取很頻繁的對象;ConcurrentHashMap中的putlfAbsent()、remove()、replace();ArrayBlockingQueue等

以下為ArrayBlockingQueue的實例:

 1 class Productor extends Thread
 2 {
 3     BlockingQueue<Integer> queue;
 4     public Productor(){}
 5     public Productor(BlockingQueue<Integer> queue)
 6     {
 7         this.queue=queue;
 8     }
 9     
10     public void run()
11     {
12         int temp=0;
13         try
14         {
15             for(int i=0;i<10;i++)
16             {
17                 temp=(int)(20*Math.random());
18                 queue.put(temp);
19                 Thread.sleep(10);
20                 System.out.println("Productor: "+temp);
21             }
22         }catch(InterruptedException ite)
23         {
24             ite.printStackTrace();
25         }
26     }
27 }
28 
29 class Customer extends Thread
30 {
31     BlockingQueue<Integer> queue;
32     public Customer(){}
33     public Customer(BlockingQueue<Integer> queue)
34     {
35         this.queue=queue;
36     }
37     
38     public void run()
39     {
40         try
41         {
42             for(int i=0;i<10;i++)
43             {
44                 Integer integer=queue.take();
45                 System.out.println("Consumer: "+integer);
46             }
47         }catch(InterruptedException ite)
48         {
49             ite.printStackTrace();
50         }
51     }
52 }
1         BlockingQueue<Integer> queue=new ArrayBlockingQueue<Integer>(3);
2         new Thread(new Productor(queue)).start();
3         new Thread(new Customer(queue)).start();;

線程池相關的類:ExecutorService接口、ThreadPoolExecutor類、Executor類

線程池的創建可以用ExecutorService pool=executors.newCachedThreadPool();

具體可見示例:

 1 class ThreadPool implements Runnable
 2 {
 3     int state;
 4     public ThreadPool() {
 5         state = 0;
 6     }
 7 
 8     public ThreadPool(int state) {
 9         this.state = state;
10     }
11 
12     public void run() {
13         for (int i = 0; i < 5; i++)
14             if (state == 0)
15                 System.out.println("Good morning, Andy");
16             else if (state == 1)
17                 System.out.println("Good afternoon, Andy");
18             else if (state == 2)
19                 System.out.println("Good evening, Andy");
20             else if (state == 3)
21                 System.out.println("Good night, Andy");
22             else
23                 System.out.println("Good bye, Andy");
24     }
25 }

 1         ExecutorService pool=Executors.newCachedThreadPool();
 2         ThreadPool tp1=new ThreadPool(0);
 3         ThreadPool tp2=new ThreadPool(1);
 4         ThreadPool tp3=new ThreadPool(2);
 5         ThreadPool tp4=new ThreadPool(3);
 6         ThreadPool tp5=new ThreadPool(4);
 7         pool.execute(tp1);
 8         pool.execute(tp2);
 9         pool.execute(tp3);
10         pool.execute(tp4);
11         pool.execute(tp5);
12 結果為:
13 Good morning, Andy
14 Good morning, Andy
15 Good morning, Andy
16 Good afternoon, Andy
17 Good afternoon, Andy
18 Good afternoon, Andy
19 Good afternoon, Andy
20 Good afternoon, Andy
21 Good bye, Andy
22 Good bye, Andy
23 Good bye, Andy
24 Good evening, Andy
25 Good evening, Andy
26 Good evening, Andy
27 Good evening, Andy
28 Good evening, Andy
29 Good morning, Andy
30 Good morning, Andy
31 Good night, Andy
32 Good night, Andy
33 Good night, Andy
34 Good night, Andy
35 Good night, Andy
36 Good bye, Andy
37 Good bye, Andy

Java中為了使多線程操作更加安全,除了隱式鎖外還有顯式鎖。顯式鎖在java.util.concurrent.locks中,含有Lock接口、ReentrantLock類:lock()、tryLock()、unlock();含有ReadWriteLock接口、ReentrantReadWriteLock類:writeLock().lock()、readLock().lock()

Java學習(十一)