線程池,多線程,線程異步,同步和死鎖,Lock接口


 線程池

  線程池,其實就是一個容納多個線程的容器,其中的線程可以反復使用,省去了頻繁創建線程對象的操作,無需反復創建線程而消耗過多資源。

除了創建和銷毀線程的開銷之外,活動的線程也需要消耗系統資源。線程池主要用來解決線程生命周期開銷問題和資源不足問題。

 使用線程池方式--Runnable接口

通常,線程池都是通過線程池工廠創建,再調用線程池中的方法獲取線程,再通過線程去執行任務方法。

Executors:線程池創建工廠類
    public static ExecutorService newFixedThreadPool(int nThreads):返回線程池對象
ExecutorService:線程池類
    Future<?> submit(Runnable task):獲取線程池中的某一個線程對象,並執行
Future接口:用來記錄線程任務執行完畢后產生的結果。線程池創建與使用

使用線程池中線程對象的步驟:

1.  創建線程池對象

2.  創建Runnable接口子類對象

3.  提交Runnable接口子類對象

4.  關閉線程池

package com.oracle.xiancheng;

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

public class Xiancheng {
    public static void main(String[] args) {
        //創建線程池對象(從線程池工廠中獲得)
        ExecutorService ec=Executors.newFixedThreadPool(2);
        //創建Runnable接口子類實例對象(創建任務對象)
        MyRunnable r = new MyRunnable();
        //    提交Runnable接口子類對象
        //線程池會選一條線程執行提交的任務
        ec.submit(r);
        //注意:submit方法調用結束后,程序並不終止,是因為線程池控制了線程的關閉。
        //又將使用完的線程又歸還到了線程池中
        for(int i=0;i<50;i++){
            System.out.println("main..."+i);
        }
        ec.shutdown();//關閉線程池
    }
}

Runnable接口實現類

package com.oracle.xiancheng;
public class MyRunnable  implements Runnable {

    public void run() {
        for(int i=0;i<50;i++){
            System.out.println("runable..."+i);
        }
    }
}

使用線程池方式—Callable接口

Callable接口:與Runnable接口功能相似,用來指定線程的任務。其中的call()方法,用來返回線程任務執行完畢后的結果,call方法可拋出異常。
ExecutorService:線程池類
    <T> Future<T> submit(Callable<T> task):獲取線程池中的某一個線程對象,並執行線程中的call()方法
Future接口:用來記錄線程任務執行完畢后產生的結果。線程池創建與使用

  使用線程池中線程對象的步驟:

  1. 創建線程池對象

  2. 創建Callable接口子類對象

  3. 提交Callable接口子類對象

  4.  關閉線程池

Callable接口實現類,call方法可拋出異常、返回線程任務執行完畢后的結果

package com.oracle.xiancheng;

import java.util.concurrent.Callable;

//Callable泛型就是你call的返回值類型
public class MyCallable implements Callable<String> {
    public String call() throws Exception {
        
        return "這是call方法";
    }

}

測試類:

package com.oracle.xiancheng;

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

public class demo01 {

    public static void main(String[] args) throws InterruptedException, ExecutionException {
        //獲取線程池
        ExecutorService ec=Executors.newFixedThreadPool(2);
        //創建callable子類
        MyCallable mc=new MyCallable();
        //提交Callable子類
        Future<String> f=ec.submit(mc);
        String str=f.get();
        System.out.println(str);
        //關閉線程池
        ec.shutdown();
    }

}

異步計算0-100的和跟0-200的和

package com.oracle.xiancheng;

import java.util.concurrent.Callable;

public class JiSuan  implements Callable<Integer>{
    private Integer a;
    public JiSuan(Integer a){
        this.a=a;
    }
    public Integer call(){
        int sum=0;
        for(int i=0;i<=a;i++){
            sum +=i;
        }
        System.out.println(sum);
        return sum;
    }
    
}

測試類:

package com.oracle.xiancheng;

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

public class Yibu {
    //異步計算0-100的和跟0-200的和
    public static void main(String[] args) throws InterruptedException, ExecutionException {
        //獲取線程池

        ExecutorService ec=Executors.newFixedThreadPool(2);
        //創建callable子類
        JiSuan js=new JiSuan(100);
        JiSuan js1=new JiSuan(200);
        //提交callable子類
        Future<Integer> f1=ec.submit(js);
        Future<Integer> f2=ec.submit(js1);
        /*//從future中獲取返回值
        System.out.println(f1.get());
        System.out.println(f2.get());*/
        //關閉
        ec.shutdown();
    }
    
    
}

 

 多線程

 線程安全

  電影院要賣票,我們模擬電影院的賣票過程。假設要播放的電影是 “功夫熊貓3”,本次電影的座位共100個(本場電影只能賣100張票)。

我們來模擬電影院的售票窗口,實現多個窗口同時賣 “功夫熊貓3”這場電影票(多個窗口一起賣這100張票)

需要窗口,采用線程對象來模擬;需要票,Runnable接口子類來模擬

模擬票:

public class Ticket implements Runnable {
    private int tickets=100;
   public void run(){
    //模擬賣票
while(true){ if(tickets>0){
          //模擬選座
try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"出售第"+tickets--+"張票"); } } } }

測試類;

package com.oracle.demo01;

public class demo01 {
    public static void main(String[] args) {
        Ticket t=new Ticket();//創建票對象
        Thread t1=new Thread(t);
        Thread t2=new Thread(t);
        Thread t3=new Thread(t);
        t1.start();
        t2.start();
        t3.start();
        
    }
}

 

結果會出現重復的票,還有錯誤的票,0和-1問題

 線程同步(線程安全處理Synchronized)

java中提供了線程同步機制,它能夠解決上述的線程安全問題。

         線程同步的方式有兩種:

 方式1:同步代碼塊

 方式2:同步方法

 同步代碼塊

//同步代碼塊: 在代碼塊聲明上 加上synchronized
synchronized (鎖對象) {
    //可能會產生線程安全問題的代碼
}

  同步代碼塊中的鎖對象可以是任意的對象;但多個線程時,要使用同一個鎖對象才能夠保證線程安全。

使用同步代碼塊,對電影院賣票案例中Ticket類進行如下代碼修改:

模擬出票

public class Ticket implements Runnable {
    private int tickets=100;
    private Object lock = new Object();
    public void run(){
        while(true){
            
            synchronized (lock) {
                if(tickets>0){
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                    
                    System.out.println(Thread.currentThread().getName()+"出售第"+tickets--+"張票");
                }else{
                    break;
                }
            }

            
        }
    }

 同步方法

//同步方法:在方法聲明上加上synchronized
public synchronized void method(){
       //可能會產生線程安全問題的代碼
}

同步方法中的鎖對象是 this

使用同步方法,對電影院賣票案例中Ticket類進行如下代碼修改:

package com.oracle.demo01;

public class Ticket implements Runnable {
    private int tickets=100;
    private Object lock = new Object();
    
    public void run() {
        //模擬賣票
        while(true){
            //同步方法
            method();
        }
    }
    
    //同步方法,鎖對象this
     public synchronized void method(){
            if (tickets> 0) {
                //模擬選坐的操作
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+"出售第"+tickets--+"張票");
            }
      }
    


}    
//靜態同步方法: 在方法聲明上加上static synchronized
public static synchronized void method(){
    //可能會產生線程安全問題的代碼
}

 靜態同步方法中的鎖對象是本類字節碼對象  類名.class

同步鎖:對象鎖,對象監視器

同步是怎么保證安全性?

 因為同步中有鎖,沒有鎖的線程,不能執行,只能等。

StringBuilder比StringBuffer快

原因:StringBuffer有同步關鍵字synchronized,安全性比StringBuilder高,但速度慢

 

  死鎖

  同步鎖使用的弊端:當線程任務中出現了多個同步(多個鎖)時,如果同步中嵌套了其他的同步。這時容易引發一種現象:程序出現無限等待,這種現象我們稱為死鎖。

格式如:

synchronzied(A鎖){
    synchronized(B鎖){
         
  }
}

定義鎖對象類:

package com.oracle.demo01;

public class LockA {
public final static LockA locka=new LockA();
private LockA(){
    
}
}
package com.oracle.demo01;

public class LockB {
    public final static LockB lockb=new LockB();
    private LockB(){
        
    }
}

線程任務類:

package com.oracle.demo01;

public class DeadLock implements Runnable {
    private int i=0;
    
    public void run() {
        
        while(true){
            if(i%2==0){
                //情況一
                synchronized (LockA.locka){
                    System.out.println("IF......lockA");
                    synchronized (LockB.lockb){
                        System.out.println("IF......lockB");
                    }
                }
            }else{
                //情況二
                synchronized (LockB.lockb){
                    System.out.println("else......lockB");
                    synchronized (LockA.locka){
                        System.out.println("else......lockA");
                    }
                }
            }
            i++;
        }
    }
    
}

測試類:

package com.oracle.demo01;

public class TestDEADlock {

    public static void main(String[] args) {
        //創建線程任務類對象
        DeadLock dl=new DeadLock();
            //創建兩個線程
        Thread t1=new Thread(dl);
        Thread t2=new Thread(dl);
           //開啟線程
        t1.start();
        t2.start();
        
    }

}

 

 Lock接口

  使用Lock接口,以及其中的lock()方法和unlock()方法替代同步,對電影院賣票案例中Ticket類進行如下代碼修改:

package com.oracle.demo01;

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

public class NewTicket implements Runnable{
    private int tickets=100;
    //創建Lock鎖對象
    private    Lock ck = new ReentrantLock();
        public void run(){
            while(true){
                ck.lock();
                if(tickets>0){
                    try {
                        Thread.sleep(10);
                        System.out.println(Thread.currentThread().getName()+"出售第"+tickets--+"張票");
    
                    } catch (InterruptedException e) {
                    
                        e.printStackTrace();
                    }finally{
                        ck.unlock();//釋放鎖
                    }
                
                }
            }
        }

}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM