線程基礎和多線程相關


基本線程類

1. 繼承Thread類

public class MyThread extends Thread {  
  public void run() {  
   System.out.println("MyThread.run()");  
  }  
}  
MyThread myThread1 = new MyThread();  
MyThread myThread2 = new MyThread();  
myThread1.start();  
myThread2.start();

Thread類本質上是實現(xiàn)Runnable接口的一個實例,代表一個線程的實例哥放。啟動線程的唯一方法是通過Thread類的start()實例方法亚皂。start()是一個native方法滓窍,它將啟動一個新線程,并執(zhí)行run()方法醒颖。這種方式實現(xiàn)多線程很簡單朱躺,通過自己的類直接extend Thread刁赖,并復寫run()方法,就可以啟動新線程并執(zhí)行自己定義的run()方法长搀。 **多個線程之間無法共享線程類的實例變量 **

2.實現(xiàn)Runnable接口

public class MyThread extends OtherClass implements Runnable {  
  public void run() {  
   System.out.println("MyThread.run()");  
  }  
} 
MyThread myThread = new MyThread();  
Thread thread = new Thread(myThread);  
thread.start(); 

3.實現(xiàn)Callable接口通過FutureTask包裝器來創(chuàng)建線程

Callable接口提供了一個call()方法可以作為線程執(zhí)行體,這個方法具有返回值鸡典,還可以聲明拋出異常

實現(xiàn)Callable接口通過FutureTask包裝器來創(chuàng)建Thread線程future模式 并發(fā)模式的一種, 可以有二種形式, 無阻賽和阻塞,分別是isDoneget其中future 對象用來存放該線程的返回值以及狀態(tài)

//Callable接口
public interface Callable<V>   { 
  V call() throws Exception;  
} 

public class SomeCallable<V> extends OtherClass implements Callable<V> {

    @Override
    public V call() throws Exception {
        // TODO Auto-generated method stub
        return null;
    }
}
Callable<V> oneCallable = new SomeCallable<V>();   
//由Callable<Integer>創(chuàng)建一個FutureTask<Integer>對象:   
FutureTask<V> oneTask = new FutureTask<V>(oneCallable);   
//注釋:FutureTask<Integer>是一個包裝器源请,它通過接受Callable<Integer>來創(chuàng)建,
//它同時實現(xiàn)了Future和Runnable接口彻况。 
  //由FutureTask<Integer>創(chuàng)建一個Thread對象:   
Thread oneThread = new Thread(oneTask);   
oneThread.start();   //至此谁尸,一個線程就創(chuàng)建完成了

4.使用ExccutorServiceCallable纽甘,Future實現(xiàn)有返回結果的線程

ExecutorService pool = Executors.newFixedThreadPool(taskSize); 
   for (int i = 0; i < taskSize; i++) {  
    Callable c = new MyCallable();  
    // 執(zhí)行任務并獲取Future對象  
    Future f = pool.submit(c); 
    list.add(f);  
   }  
    // 關閉線程池  
    pool.shutdown();  
    //從Future對象上獲取任務的返回值
    f.get()     
   // 獲取所有并發(fā)任務的運行結果  
//實現(xiàn)Callable接口
class MyCallable implements Callable<Object> {  
public Object call() throws Exception {  
   return null;  
}  
  • ExecutoreService提供了submit()方法良蛮,傳遞一個Callable,或Runnable悍赢,返回Future决瞳。如果Executor后臺線程池還沒有完成Callable的計算,這調用返回Future對象的get()方法左权,會阻塞直到計算完成
ExecutorService e = Executors.newFixedThreadPool(taskSize); //新建線程池
 //submit方法有多重參數(shù)版本皮胡,及支持callable也能夠支持runnable接口類型.
Future future = e.submit(new myCallable());
future.isDone() //return true,false 無阻塞
future.get() // return 返回值,阻塞直到該線程運行結束

總結 :Runnable赏迟,Callable接口與Thread類的對比

  • 采用實現(xiàn)Runnable屡贺,Callable接口優(yōu)缺點:
    1,接口可以多繼承锌杀,繼承了Runnable接口還能繼承其他接口
    2甩栈,適合多個相同線程來處理同一份資源的情況,
    3糕再,缺點是量没,編程稍微復雜,訪問當前線程必須使用Thread.currentThread()
  • 采用繼承Thread類優(yōu)缺點:
    1亿鲜,編寫簡單允蜈,訪問當前線程可直接用this
    2冤吨,缺點是,不能再繼承其他類
    綜上饶套,建議采用實現(xiàn)Runnable接口的方法來創(chuàng)建和啟動線程

synchronized關鍵字

synchronized關鍵字用于修飾方法和代碼塊漩蟆,以實現(xiàn)同步,當多個線程在執(zhí)行被synchronized修飾的代碼妓蛮,以排隊的方式進行處理怠李。當一個線程調用被修飾的代碼時,先判斷有沒有被上鎖蛤克,如果上鎖就說明有其他線程在調用捺癞,必須等待其他線程結束調用后才能執(zhí)行這段代碼,synchronized可以在任意對象以及方法上加鎖构挤,加鎖的這段代碼被稱為“互斥區(qū)”或者“臨界區(qū)”

synchronized關鍵字加到static靜態(tài)方法是給Class類上鎖

synchronized關鍵字加到非static靜態(tài)方法上是給對象上鎖

關于并行,并發(fā)和同步的概念

  • 并行:
    • 多個CPU實例或者多臺機器同時執(zhí)行一段處理邏輯,是真正的同時.
  • 并發(fā)
    • 通過CPU調度算法,讓用戶看上去同時執(zhí)行,實際上CPU操作層面不是真正的同時,并發(fā)往往場景中有公共資源,那么針對這個公用的資源往往產(chǎn)生瓶頸,我們會用TPS或者QPS來反應這個系統(tǒng)的處理能力
  • 同步
    • Java中的同步指的是通過人為的控制和調度,保證共享資源的多線程訪問成為線程安全,.來確保結果的準確,如上面的

線程安全

  • 指在并發(fā)的情況下,改代碼經(jīng)過多線程的使用,線程的調度順序不影響任何結果,這個時候使用多線程,我們只需要關注系統(tǒng)功能的內(nèi)存,Cpu是不是夠用即可,反過來,線程不安全就意味著線程的調度順序會響應最終結果
    1. 多個線程同時操作一個全局變量是不安全的髓介,使用自旋鎖并不是絕對的安全(因為單寫多讀)。
    2. 在多個線程進行讀寫操作時筋现,仍然能夠保證數(shù)據(jù)的正確 唐础。使用互斥鎖可以實現(xiàn),但是消耗性能
    3. 所有更新UI的操作都在主線程上執(zhí)行

線程的狀態(tài)

  • 新建狀態(tài):使用new 關鍵字和Thread類或其子類建立一個線程對象后矾飞,該線程對象就處于新建狀態(tài)一膨。它保持這個狀態(tài)直到程序 start()這個線程。

  • 就緒狀態(tài):當線程對象調用了start()方法之后洒沦,該線程就進入就緒狀態(tài)豹绪。就緒狀態(tài)的線程處于就緒隊列中,要等待JVM里線程調度器的調度申眼。

  • 阻塞(Blocked) : 對Running狀態(tài)的線程加同步鎖synchronized使其今如(Lock Blocked Pool) 同步鎖被釋放今如可運行狀態(tài),

  • 等待(Waiting) :線程可以主動調用obj.wait或者Thread.sleep或者 join 今入,Waiting是等待另一個線程完成某一個操作,如join等待另一個完成執(zhí)行.

  • waiting 和 Blocked狀態(tài), Blocked也是一種等待,等待的是monitor 但是waiting等待是notify()方法

每個對象都有的機制

  • monitor :Java中 每個對象都有一個監(jiān)視器,來監(jiān)視并發(fā)代碼的重入,在非多線程編碼時改監(jiān)視器不發(fā)揮作用,反之如果在``synchronized`范圍內(nèi),監(jiān)視器發(fā)揮作用.
  • wait/notify 必須存在于synchronized塊中, 并且這三個關鍵字針對的是同一個監(jiān)視器,意味著wait之后,其他線程可以進入同步代碼塊
  • synchronized 單獨使用 在多線程環(huán)境下瞒津,synchronized塊中的方法獲取了lock實例的monitor,如果實例相同豺型,那么只有一個線程能執(zhí)行該塊內(nèi)容

優(yōu)雅地停止線程

  • interrupt阻塞中斷和非阻塞中斷

  • interrupt()方法并不會立即執(zhí)行中斷操作仲智,這個方法只會給線程設置一個為true的中斷標志。設置之后姻氨,則根據(jù)線程當前的狀態(tài)進行不同的后續(xù)操作钓辆。

  • 線程的當前狀態(tài)處于非阻塞狀態(tài),那么僅僅是線程的中斷標志被修改為true而已(2)如果線程的當前狀態(tài)處于阻塞狀態(tài)肴焊,那么在將中斷標志設置為true后前联,如果是 wait、sleep以及join 三個方法引起的阻塞娶眷,那么會將線程的中斷標志重新設置為false似嗤,并拋出一個InterruptedException,這樣受阻線程就得以退出阻塞的狀態(tài)届宠。

    public class TestThread1 {
    
        public static void main(String[] args) {
            MyRunnable1 myRunnable=new MyRunnable1();
            Thread thread=new Thread(myRunnable,"子線程");
            thread.start();
            try{
                //主線程休眠
                Thread.sleep(3000);
                //調用中斷烁落,true
               thread.interrupt();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    
    class MyRunnable1 implements Runnable{
    
        @Override
        public void run() {
          int i=0;
          while(true){
              System.out.println(Thread.currentThread().getName()+"循環(huán)第"+ ++i+"次");
              try{
                //判斷線程的中斷情況
                    boolean interruptStatus=Thread.currentThread().isInterrupted();
                    System.out.println(Thread.currentThread().getName()+"循環(huán)第"+ ++i+"次"+interruptStatus);
                    Thread.sleep(1000);
                    //非阻塞中斷 只是設置標記位true
                  //非阻塞中斷 只是設置標記位true
                  if(interruptStatus){
                  //如果中斷為true則退出
                      break;
                  }
                 
              } catch (InterruptedException e) {
                  
                  // 一個線程在運行狀態(tài)中乘粒,其中斷標志被設置為true之后,一旦線程調用了
                  // wait伤塌、join灯萍、sleep方法中的一種,立馬拋出一個InterruptedException每聪,且中斷標志被程序會自動清除旦棉,重新設置為false
                  System.out.println("阻塞中斷"+Thread.currentThread().isInterrupted());//顯示false并拋異常
                  return;//不想返回還可繼續(xù)寫代碼
              }
          }
        }
     
    }
    
    子線程循環(huán)第1次false
    子線程循環(huán)第2次false
    子線程循環(huán)第3次false
    阻塞中斷false
    
  • Stop()停止

    • 由于不安全,已經(jīng)不使用了药薯,因為stop會解除由線程獲取的所有鎖定绑洛,當在一個線程對象上調用stop()方法時,這個線程對象所運行的線程就會立即停止童本,假如一個線程正在執(zhí)行:synchronized void { x = 3; y = 4;}由于方法是同步的真屯, 多個線程訪問時總能保證x,y被同時賦值,而如果一個線程正在執(zhí)行到x = 3;時巾陕,被調用了 stop()方法讨跟,即使在同步塊中,它也會馬上stop了鄙煤,這樣就產(chǎn)生了不完整的殘廢數(shù)據(jù)。
  • 設置標記位停止

public class TestThread2_1 {
    public static void main(String[] args) throws InterruptedException {
        MyThreads my = new MyThreads();
        new Thread(my, "線程A").start();
        Thread.sleep(10000);
        //設置標記位
        //my.setFlag(false);

        //stop方法
        new Thread(my, "線程A").stop();
        System.out.println("代碼結束");

    }
}

class MyThreads implements Runnable {
    private boolean flag = true;

    @Override
    public void run() {
        int i = 1;
        while (flag) {
            try {
                Thread.sleep(1000);
                System.out.println("第" + i + "次執(zhí)行茶袒,線程名稱為:" + Thread.currentThread().getName());
                i++;
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    public void setFlag(boolean flag) {
        this.flag = flag;
    }
}

多線程控制類

ThreadLocal(關于這個類后續(xù)會推出關于這個類的專題)

  • 用處: 保存線程的獨立變量, 對一個下線程類使用ThreadLocal維護變量時,ThreadLocal為每個使用該變量的線程單獨提供了獨立的變量,所以每個線程都可以獨立地改變自己的副本,而不會影響其他線程所對應的副本,長用于用戶登錄控制,如記錄session信息

  • 常用方法

    方法 作用
    initValue() 副本創(chuàng)建的方法
    get() 得到副本
    set() 設置副本
  • 實例

    • ThreadLocal能實現(xiàn)為不同的線程保存變量的原理是梯刚,它內(nèi)部有個Entry,保存<線程名,變量值>,不同的線程對應著不同的value,就能加以區(qū)分了
    public class ThreadLocalDemo {
      //銀行對象薪寓,有錢亡资,有存款和取款兩個操作
      static class Bank{
          ThreadLocal<Float> threadLocal = new ThreadLocal<Float>() {
              protected Float initialValue() {
                  return 0.0f;
              }
              
          };
          //存款
          public Float get() {
              return threadLocal.get();
          }
          //取款
          public void set(Float money) {
              threadLocal.set(threadLocal.get()+money);
          }
      }
      
      
      //轉賬對象,從銀行中取錢然后轉賬向叉,然后保存到賬戶
      static class Transfer implements Runnable{
          private Bank bank;
          public Transfer(Bank bank){
              this.bank = bank;
          }
          
          @Override
          public void run() {
              // TODO Auto-generated method stub
              for(int i = 0;i<10;i++) {
                  bank.set(10.0f);
                  System.out.println(Thread.currentThread().getName()+"賬戶余額"+bank.get());
              }
          }
      }
    }
    //main 中 模擬操作
          Bank bank = new Bank();
          Transfer transfer = new Transfer(bank);
          Thread t1 =new Thread(transfer,"客戶1");
          Thread t2 =new Thread(transfer,"客戶2");
          t1.start();
          t2.start();
    

Lock類

  • Lock類實際上是一個接口锥腻,我們在實例化的時候實際上是實例化實現(xiàn)了該接口的類
Lock lock = new ReentrantLock();
  • 作用 :通過Lock對象lock,用lock.lock來加鎖 用lock.unlock來釋放鎖母谎。在兩者中間放置需要同步處理的代碼瘦黑。

    • 可重入鎖(ReentrantLock): 線程請求它已經(jīng)擁有的鎖時不會阻塞,可以進入它已經(jīng)擁有的同步代碼塊奇唤,但是它擁有了多少次幸斥,就要解鎖多少次才能釋放鎖
    • 讀寫鎖(ReadWriteLock) :可以同時讀,但是讀的時候不能寫咬扇,也不能同時寫甲葬,寫的時候不能讀
    public class MyConditionService {
        private Lock lock = new ReentrantLock();//ReentrantLock是個重入鎖
        public void testMethod(){
            lock.lock();//加鎖
            for (int i = 0 ;i < 5;i++){
                System.out.println("ThreadName = " + Thread.currentThread().getName() + (" " + (i + 1)));
            }
            lock.unlock();//解鎖
        }
    }
    
  • 其他作用

    • 實現(xiàn)鎖的公平
    • 獲取當前線程調用lock的次數(shù),也就是獲取當前線程鎖定的個數(shù)
    • 獲取等待鎖的線程數(shù)
    • 查詢指定的線程是否等待獲取此鎖定
    • 查詢是否有線程等待獲取此鎖
    • 查詢當前線程是否持有鎖定
    • 判斷一個鎖是不是被線程持有
    • 加鎖時如果中斷則不加鎖懈贺,進入異常處理
    • 嘗試加鎖经窖,如果該鎖未被其他線程持有的情況下成功
  • Condition類

  • Condition是Java提供了來實現(xiàn)等待/通知的類坡垫,Condition類還提供比wait/notify更豐富的功能,Condition對象是由lock對象所創(chuàng)建的画侣。但是同一個鎖可以創(chuàng)建多個Condition的對象冰悠,即創(chuàng)建多個對象監(jiān)視器。這樣的好處就是可以指定喚醒線程棉钧。notify喚醒的線程是隨機喚醒一個屿脐。

  • condition對象通過lock.newCondition()來創(chuàng)建,用condition.await()來實現(xiàn)讓線程等待宪卿,使得線程進入阻塞的诵。用condition.signal()來實現(xiàn)喚醒線程。喚醒的線程是用同一個conditon對象調用await()方法而進入阻塞佑钾。并且和wait/notify一樣西疤,await()signal()也是在同步代碼區(qū)內(nèi)執(zhí)行。
    對于等待/通知機制休溶,簡化而言代赁,就是等待一個條件,當條件不滿足時兽掰,就進入等待芭碍,等條件滿足時,就通知等待的線程開始執(zhí)行孽尽。為了實現(xiàn)這種功能窖壕,需要進行wait的代碼部分與需要進行通知的代碼部分必須放在同一個對象監(jiān)視器里面。執(zhí)行才能實現(xiàn)多個阻塞的線程同步執(zhí)行代碼杉女,等待與通知的線程也是同步進行瞻讽。對于wait/notify而言,對象監(jiān)視器與等待條件結合在一起 即synchronized(對象)利用該對象去調用wait以及notify熏挎。但是對于``Condition類速勇,是對象監(jiān)視器與條件分開,Lock類來實現(xiàn)對象監(jiān)視器坎拐,condition`對象來負責條件烦磁,去調用await以及signal。

  • 示例代碼:

    public class ConditionWaitNotifyService {
    
        private Lock lock = new ReentrantLock();
    
        public Condition condition = lock.newCondition();
    
    
        public void await(){
            try{
                lock.lock();
                System.out.println("await的時間為 " + System.currentTimeMillis());
                condition.await();
                System.out.println("await結束的時間" + System.currentTimeMillis());
    
            }catch (Exception e){
                e.printStackTrace();
            }finally {
                lock.unlock();
            }
        }
    
    
        public void signal(){
            try{
                lock.lock();
                System.out.println("sign的時間為" + System.currentTimeMillis());
                condition.signal();
            }finally {
                lock.unlock();
            }
        }
    
        public static void main(String[] args) throws InterruptedException {
    
            ConditionWaitNotifyService service = new ConditionWaitNotifyService();
            new Thread(service::await).start();
            Thread.sleep(1000 * 3);
            service.signal();
            Thread.sleep(1000);
    
        }
    }
    
    • 其他作用:和wait類提供了一個最長等待時間廉白,awaitUntil(Date deadline)在到達指定時間之后个初,線程會自動喚醒。但是無論是await或者awaitUntil猴蹂,當線程中斷時院溺,進行阻塞的線程會產(chǎn)生中斷異常。Java提供了一個awaitUninterruptibly的方法磅轻,使即使線程中斷時珍逸,進行阻塞的線程也不會產(chǎn)生中斷異常逐虚。
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市谆膳,隨后出現(xiàn)的幾起案子叭爱,更是在濱河造成了極大的恐慌,老刑警劉巖漱病,帶你破解...
    沈念sama閱讀 218,204評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件买雾,死亡現(xiàn)場離奇詭異,居然都是意外死亡杨帽,警方通過查閱死者的電腦和手機漓穿,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,091評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來注盈,“玉大人晃危,你說我怎么就攤上這事±峡停” “怎么了僚饭?”我有些...
    開封第一講書人閱讀 164,548評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長胧砰。 經(jīng)常有香客問我鳍鸵,道長,這世上最難降的妖魔是什么尉间? 我笑而不...
    開封第一講書人閱讀 58,657評論 1 293
  • 正文 為了忘掉前任权纤,我火速辦了婚禮,結果婚禮上乌妒,老公的妹妹穿的比我還像新娘。我一直安慰自己外邓,他們只是感情好撤蚊,可當我...
    茶點故事閱讀 67,689評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著损话,像睡著了一般侦啸。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上丧枪,一...
    開封第一講書人閱讀 51,554評論 1 305
  • 那天光涂,我揣著相機與錄音,去河邊找鬼拧烦。 笑死忘闻,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的恋博。 我是一名探鬼主播齐佳,決...
    沈念sama閱讀 40,302評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼私恬,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了炼吴?” 一聲冷哼從身側響起本鸣,我...
    開封第一講書人閱讀 39,216評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎硅蹦,沒想到半個月后荣德,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,661評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡童芹,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,851評論 3 336
  • 正文 我和宋清朗相戀三年涮瞻,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片辐脖。...
    茶點故事閱讀 39,977評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡饲宛,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出嗜价,到底是詐尸還是另有隱情艇抠,我是刑警寧澤,帶...
    沈念sama閱讀 35,697評論 5 347
  • 正文 年R本政府宣布久锥,位于F島的核電站家淤,受9級特大地震影響,放射性物質發(fā)生泄漏瑟由。R本人自食惡果不足惜絮重,卻給世界環(huán)境...
    茶點故事閱讀 41,306評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望歹苦。 院中可真熱鬧青伤,春花似錦、人聲如沸殴瘦。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,898評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽蚪腋。三九已至丰歌,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間屉凯,已是汗流浹背立帖。 一陣腳步聲響...
    開封第一講書人閱讀 33,019評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留悠砚,地道東北人晓勇。 一個月前我還...
    沈念sama閱讀 48,138評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親宵蕉。 傳聞我的和親對象是個殘疾皇子酝静,可洞房花燭夜當晚...
    茶點故事閱讀 44,927評論 2 355

推薦閱讀更多精彩內(nèi)容