黑馬程序員---Java中的線程

                                                 關於Java中的線程
                 --------- android培訓java培訓、java學習型技術博客、期待與您交流! ------------

1. 進程
  進程:正在運行的程序,所佔有內存空間
  程序存儲在硬盤,運行時期到了內存中
  線程:是一個大程序中的子程序
  CPU真正執行的是線程,子程序對於CPU來講獨立執行路徑,路徑就是線程
//======================================================
2. Java中創建線程
  任何都是對象,線程也是對象,對象的描述類
  java.lang.Thread類
  創建線程的第一種方式,繼承Thread類,重寫run方法
  創建Thread子類對象
  調用子類中的方法start()開啓線程
   void start()
          使該線程開始執行;Java 虛擬機調用該線程的 run 方法。
  JVM 本身是單線程的程序,還是多線程的程序
  一個線程運行我們寫的程序,一個線程不定時收取垃圾,JVM幫助你調用Windows中的功能
  爲什麼繼承Thread類,入夥,繼承Thread類,子類就是一個線程了
  爲什麼重寫run方法 Thread中的run方法是空方法,等待子類重寫
  線程運行的程序是什麼,未知的,Java工程師,開放多線程技術的時候
  不知道使用Java編程的人員會運行哪些代碼
  提供一個標準,就是run方法:不管線程運行哪些代碼,必須放在run中,線程就運行你run中的代碼

/*
 * 創建線程第一種方式,繼承Thread類
 */
class Demo1 extends Thread{
 public void run(){
  for(int x = 0 ; x < 50 ;x++){
   System.out.println("run..."+x);
  }
 }
}
public class ThreadDemo1 {
 public static void main(String[] args) {
  while(true){
  Demo1 d = new Demo1();
  d.start();//開啓線程,JVM自動調用線程的run方法
  for(int x = 0 ; x < 50 ; x++){
   System.out.println("main..."+x);
  }
  }
 }
}

//======================================================

4. 線程的狀態圖,必須自己會畫

5. 線程名字的獲取和設置
  獲取名字,Thread類的方法getName(),返回字符串
  線程名字JVM命名的 Thread-0 Thread-1
  在Thread子類中,直接使用父類方法getName()獲取名字
  在不是Thread子類中,獲取線程名字
  Thread類中,提供了一個靜態方法 currentThread()返回值是一個Thread類的對象
  方法,返回當前線程對象,既然返回的是對象,方法調用鏈
   String name = Thread.currentThread().getName();
  設置線程的名字:
    Thread類的方法setName()
    Thread類的構造方法
      Thread(String name)傳遞線程名字
    Thread子類中,super語句將線程的名字送到父類構造方法



class ThreadName extends Thread{
 ThreadName(String name){
  super(name);
 }
 public void run(){
  String name =Thread.currentThread().getName();
  System.out.println(name+" 線程ThreadName");
 }
}
public class ThreadDemo {
 public static void main(String[] args) {
  //Thread.currentThread();//返回的就是運行main方法的線程對象
  String name = Thread.currentThread().getName();
  System.out.println(name);
  ThreadName t1 = new ThreadName("西班牙0");
  ThreadName t2 = new ThreadName("智利2");
 
 // t1.setName("小強");
 // t2.setName("旺財");
 
  t1.start();
  t2.start();
 }
}

//======================================================
6. 售票的一個案例
  利用多線程模擬多窗口一起售票
  模擬出了,卡機線程,導致了數據的安全問題
  多線程操作同一個數據的時候,出現數據安全問題
 
   解決辦法:一個線程不執行完畢,其他的線程,不能執行
 
  Java中開放出了同步技術,保證線程的安全性,會阻止其他線程進入
  同步代碼塊
    synchronized(任意對象){
   
       線程操作的共享數據
    }


/*
 * 模擬售票4個窗口一起出售
 * 改造成實現Runnable接口的方式
 */
class Ticket implements Runnable{
 private int tickets = 100;
 private Object obj = new Object();
 public void run(){
  while(true){
   synchronized(obj){
   if(tickets > 0){
    //線程if判斷完畢後,休眠一段時間
    try{
    Thread.sleep(67);
    }catch(Exception e){}
    System.out.println(Thread.currentThread().getName()+"..出售第.."+tickets--);
   }
   }
  }
 }
}
public class ThreadDemo1 {
 public static void main(String[] args) {
  //創建Ticket對象
  Ticket t = new Ticket();
  //創建Thread類對象,傳遞Runnable接口的實現類對象
  Thread t1 = new Thread(t);
  Thread t2 = new Thread(t);
  Thread t3 = new Thread(t);
  Thread t4 = new Thread(t);
  t1.start();
  t2.start();
  t3.start();
  t4.start();
 }
}

//======================================================
7. 創建線程的第二種方式
  定義類,實現Runnable接口
  重寫run方法
  class A implements Runnable{
        public void run(){
 }
  }
  A類不再是線程類了
  直接創建Thread類對象
  構造方法Thread(Runnable target) 接受的數據類型是Runnable接口的子類類型
  new Thread(new A()); 
  調用Thread類中的start();

  兩個線程的創建方式的區別
  繼承Thread類,實現Runnable接口區別
  繼承,單繼承侷限性
  接口,多現實,避免了單繼承的侷限性
  繼承Thread類方式,線程中的數據是對象自己的
  實現接口方法,線程中的數據是共享的
  寫多線程程序推薦使用接口方式
//======================================================
8. 同步的原理
    synchronized(任意對象){
   
       線程操作的共享數據
    }
 
  對象,寫在了同步中
  專業名詞,對象監視器
  通俗一點:鎖
  線程進到同步代碼塊後,線程獲取這把鎖,這扇門永久關閉了
  當線程出去同步代碼塊,將鎖釋放
  廁所原理
  多線程操作同一個數據,安全問題
  如果是單線程,沒有數據安全問題



//======================================================
9. 模擬存錢
  一張卡,可以多個櫃檯存錢
  餘額是0,每個櫃檯每次存100元
  兩個櫃檯,每個存3次
  600元,沒存一次,查看餘額
  同步方法,在方法的聲明上寫同步關鍵字
  線程每次只有一個運行這個方法
  當方法中所有代碼都是線程操作的的共享數據
  同步方法中,鎖是什麼
  確定的是,鎖肯定有,鎖必須是對象
  鎖是本類的對象引用this
  方法中的同步代碼塊,鎖直接寫this
  靜態方法中的,同步鎖是誰
  鎖是對象!
  靜態優先於對象,靜態中的鎖,是本類的class文件對象
  Java中,每一個數據類型,JVM都賦予他們一個靜態成員變量
  class名字,變量的運行結果就是類的class文件對象
  靜態方法中的鎖,就是本類.class字節碼文件對象


/*
 * 存錢的時候
 * 卡,錢,到銀行中去,銀行櫃檯存錢
 * 但是,整個Add方法中的所有代碼,都是線程操作的共享數據
 * 沒有必要同步一段代碼,同步整個方法
 */
class Bank{
 //存錢功能,存一次,看餘額
 private static int sum = 0;
 public static synchronized void add(int money){
 // synchronized(Bank.class){
  sum = sum + money;
  System.out.println(sum);
 // }
 }
}
class Customer implements Runnable{
 private Bank b = new Bank();
 public void run(){
  for(int x = 0 ; x < 3 ; x++){
  Bank.add(100);
  }
 }
}
public class ThreadDemo2 {
 public static void main(String[] args) {
  Customer c = new Customer();
  Thread t1 = new Thread(c);
  Thread t2 = new Thread(c);
  t1.start();
  t2.start();
 }
}


//==============================================================
10. 單例模式
  懶漢有安全隱患,多線程併發訪問懶漢式的時候
  安全問題,同步避免
  效率很低,提高懶漢的效率
  第一次執行s=null
  進同步 創建對象,返回
  第二次執行s!=null
  沒有必要進同步了,直接返回
  兩次判斷,提高效率

class Single{
 private Single(){}
 private static Single s = null;
 public static Single getInstance(){
 
  if(s == null){
 
  synchronized(Single.class){
  if( s == null)
   s = new Single();
        }
  }
 
  return s;
 }
}
class SingleThead implements Runnable{
 public void run(){
  for(int x = 0 ; x < 30 ; x++){
   Single s = Single.getInstance();
   System.out.println(s);
  }
 }
}

//==============================================================
11. 死鎖案例
  在多線程的技術中,兩個或者兩個以上的線程,同時爭奪一個對象監視器
  導致程序的假死現象
  死鎖:出現條件,必須多線程,爭奪一個鎖,程序中,體現在同步代碼塊的嵌套效果
  死鎖的案例,面試過程中經常被考到
//==============================================================
12. 線程的通信
  兩個線程同時對一個資源對象,進行賦值和取值操作
  思考,數據安全問題怎麼發生的
  發生後,怎麼解決
  數據安全問題:線程的隨機性導致程序數據安全隱患
  採用了同步技術,依然沒有解決
  當你發現數據安全問題後,使用了同步技術,還是不能解決
    第一點,確定程序是不是多線程在操作共享數據 確定
    第二點,使用的同步中的鎖,是同一把鎖嗎,鎖不同唯一的
      必須將鎖變成唯一的對象,才能控制數據安全問題
      資源對象,數據的安全性解決了

  線程等待和喚醒的方法
  沒有出現在線程描述類Thread類中
  方法定義在了Object類中,爲什麼這樣設計
  原因是鎖,鎖是一個對象,對象是通過類new出來的
  鎖是哪一個類的對象,無法確定的
  但是將方法寫在Object類中,所有的類的對象,都具有線程的等待與喚醒方法
  wait()方法的使用,將線程永久等待,直到有人喚醒
  wait方法必須有鎖的支持,wait方法必須寫在同步中
  鎖是唯一的
  synchronized(r){
     wait();
     notify();
  }
  synchronized(r){
     notify()
  }
  IllegalMonitorStateException
  異常,運行時期的異常,拋出該異常,說明wait notify沒有鎖的支持,沒有對象監視器

/*
 * 線程通信的代碼的優化
 */
//定義資源類,私有處理
class Recource{
 private String name;
 private String sex;
 private boolean flag = false;
 //提供get set方法,訪問成員變量
 public synchronized void set(String name,String sex){
  if(flag)
   try{
   this.wait();
   }catch(Exception e){}
  this.name = name;
  this.sex = sex;
  flag = true;
  this.notify();
 }
 public synchronized void get(){
  if(!flag)
   try{
   this.wait();
   }catch(Exception e){}
  System.out.println(name+"..."+sex);
  flag = false;
  this.notify();
 }
}
//輸入線程
class Input implements Runnable{
 private Recource r;
 Input(Recource r){this.r = r;}
 public void run(){
  int x = 0 ;
  while(true){
   if(x %2 == 0){
    r.set("張三", "男");
   }else{
    r.set("李四", "女");
   }
   x++;
  }
 }
}
//輸出的線程
class Output implements Runnable{
 private Recource r ;
 Output(Recource r){this.r=r;}
 public void run(){
  while(true){
   r.get();
  }
 }
}
public class ThreadDemo5 {
 public static void main(String[] args) {
  Recource r = new Recource();
  new Thread(new Input(r)).start();
  new Thread(new Output(r)).start();
 }
}


//==============================================================
13. wait() sleep() 導致線程等待,兩個方法區別在哪裏(不要光看表面。)
  
  sleep(毫秒值)自動醒來
  wait()永久等待,需要別的線程喚醒

  sleep()方法是Thread類的靜態方法
  wait()方法是Object類的非靜態方法

  sleep()不需要對象鎖
  wait()必須有鎖的支持

  sleep()方法,執行的時候線程不會釋放對象鎖
  wait()方法,執行的時候,線程放棄對象鎖,被喚醒的時候,從新獲取對象鎖,才能運行
//==============================================================
14. 定時器
  Java程序中,有定時功能,按照一定的時間間隔運行指定的程序
  定時器類,java.util.Timer
  構造方法
    Timer(boolean isDaemon) false 不是守護的線程
    schedule(TimerTask task, Date firstTime, long period)
                       定時器任務              第一次時間         間隔
  抽象類 TimerTask 時間任務,定時器執行的程序,寫在這個類的run方法


/*class Time extends TimerTask{
 public void run(){
  System.out.println("定時2秒鐘發送一次郵件");
 }
}*/
public class TimerDemo {
 public static void main(String[] args) {
  Timer t = new Timer(false);
 
  t.schedule(new TimerTask(){
   public void run(){
    System.out.println("\"定時2秒鐘發送一次郵件\"");
   }
  }, new Date(), 3600000);
 
  System.out.println("main...over");
 }
}


//==============================================================

15. 多線程通信,生產者與消費者
  一個產品,分別線程控制,一個控制生產,一個控制消費
  生產一個,消費一個,多生產者,多消費者
  多個生產與多個消費,全部的喚醒notifyAll()
  喚醒以後,數據安全性還是沒解決
  線程在wait上等待,被喚醒的時候,從Wait這裏起來
  起來以後,不會再次判斷flag是true,還是false,因此數據問題,沒喲解決
  線程,被喚醒以後,但是判斷標記!!
  用的是循環的方式,解決線程倒下去後,再起來,必須還要判斷標記
  但是發現一個問題:
    notifyAll()喚醒了全部等待的線程
    1個活的,7個等着,全部醒來
    浪費資源,能不能喚醒對方的一個線程的
    生產者,只喚醒一個消費者
    消費者,只喚醒一個生產者
    喚醒本方是沒有意義,全部喚醒是浪費資源的
    Java的JDK1.4版本之前,是做不到了
    到了JDK1.5版本後,就可以實現了
    提供一套新的多線程的操作方式
    synchronized,被替換了
    wait(),notify(),notifyAll(),被替換了
    JDK1.5的新特性,線程鎖定操作
    導包 java.util.concurrent.locks



//==============================================================
16. JDK1.5的鎖 lock接口
  Lock接口--synchronized同步
  同步是有鎖提供
  接口中,定義了兩個方法 lock() unlock()
  要同步線程的時候,使用這兩個方法,進行鎖操作
  Lcok接口的實現類對象ReentrantLock
  獲取到接口的實現類對象,調用方法,鎖操作
  新的技術中,JDK提供了一個接口Condition
  替換了原有線程方法,wait,notify
  將線程進行分組管理
  t1-t4 set方法,用鎖就是set方法中的鎖
  t5-t8 get方法,用鎖就是get方法中的鎖
  一個lock接口上,可以實現多個Condition對象
  final Condition notFull = lock.newCondition();
  final Condition notEmpty = lock.newCondition();
  將一個接口Lock,分成兩組管理
  Condition接口中的三個方法
   await() -- wait()
   signal() -- notify()
   signalAll() -- notifyAll();


/*
 * 將案例,改造成lock鎖方式實現功能
 */
import java.util.concurrent.locks.*;
class Product{
 private String name;
 //定義計數器
 private int count ;
 //定義標識
 private boolean flag = false;
 //定義Lock鎖對象
 private Lock lock = new ReentrantLock();
 //通過Lock接口,獲取Condition對象
 private Condition pro = lock.newCondition();
 private Condition cus = lock.newCondition();
 
 //定義生產方法
 public void set(String name){
  //獲取鎖
  lock.lock();
  while(flag)
   try{
     pro.await();
   }catch(Exception e){}
  this.name = name + count++;
  System.out.println(Thread.currentThread().getName()+"生產第..."+this.name);
  flag = true;
  //this.notifyAll();
  //釋放鎖
  cus.signal();
  lock.unlock();
 }
   //定義消費方法
 public void get(){
  lock.lock();
  while(!flag)
   try{
   cus.await();
   }catch(Exception e){}
  System.out.println(Thread.currentThread().getName()+"消費第........."+this.name);
  flag = false;
  //this.notifyAll();
  pro.signal();
  lock.unlock();
 }
}
//定義生產者
class Producter implements Runnable{
 private Product p ;
 Producter(Product p){this.p = p;}
 public void run(){
  while(true){
   p.set("鍵盤");
  }
 }
}
//定義消費這
class Customer implements Runnable{
 private Product p ;
 Customer(Product p){this.p = p;}
 public void run(){
  while(true){
   p.get();
  }
 }
}
public class ThreadDemo {
 public static void main(String[] args) {
  Product p = new Product();
  Producter producter = new Producter(p);
  Customer customer = new Customer(p);
  Thread t1 = new Thread(producter);
  Thread t2 = new Thread(producter);
  Thread t3 = new Thread(producter);
  Thread t4 = new Thread(producter);
  Thread t5 = new Thread(customer);
  Thread t6 = new Thread(customer);
  Thread t7 = new Thread(customer);
  Thread t8 = new Thread(customer);
  t1.start();
  t2.start();
  t3.start();
  t4.start();
  t5.start();
  t6.start();
  t7.start();
  t8.start();
 }
}


//==========================================================
17. 線程的停止方式
  Thread類中,有一個方法stop(),過時

  終止線程的運行,目的結束run方法就行
  停止線程的第一種方式,改變變量的值,結束while循環,結束了run方法
  
處於等待中的線程,怎麼停下
  例子:
    我有一個朋友,失眠,找了一個催眠大師(水平很高)
    進行了催眠,朋友就睡了(wait())
    催眠師說,被我催眠的人,只有我能叫醒
    催眠師死了,不能讓朋友永久的等待下去
    拍你一板磚,醒來,收到了傷害(異常)
 
  線程的第二種停止方式
    void interrupt() + 異常停下,等待中的線程
    打擊線程方法,處於等待的線程,將會拋出異常

/*
 * 線程如何停止下來
 */
class StopThread implements Runnable{
 private boolean flag = true;
 public void run(){
  while(flag){
   synchronized(this){
    try{this.wait();}catch(Exception e){
     e.printStackTrace();
     //System.out.println(e.getMessage()+"打你一板磚");
     flag = false;
    }
   System.out.println("run....");
   }
  }
 }
 public void setFlag(boolean flag){
  this.flag = flag;
 }
}
public class ThreadDemo1 {
 public static void main(String[] args) {
  StopThread st = new StopThread();
  Thread t = new Thread(st);
  t.start();
  for(int x = 0 ; x < 1000 ; x++){
  if(x==999)
      //st.setFlag(false);
   t.interrupt();
  else
   System.out.println("main"+x);
  }
 }
}

//==========================================================
18. 守護線程
  Thread類中。setDaemon(boolean )傳遞是true,線程守護線程
  如果所有運行的線程,都是守護線程,JVM退出

  方法,必須在start開始前調用

  Feiq,開啓多個聊天窗口的時候,一旦關閉飛秋主程序,聊天窗口也就關閉了
  聊天窗口線程,就是飛秋主線程的守護線程,一旦主線程死亡,所有的守護線程就死亡

/*
 * 守護線程
 */
class ThreadDaemon implements Runnable{
 public void run(){
  while(true)
  System.out.println("run....");
 }
}
public class ThreadDemo2 {
 public static void main(String[] args) {
  ThreadDaemon td = new ThreadDaemon();
  Thread t = new Thread(td);
  t.setDaemon(true);
  t.start();
 }
}



//==========================================================
19. Thread中其他方法,toString() setPriority() join() yield()
  toString()繼承Object,重寫toString()
  Thread[Thread-0,5,main]
  5 優先級,main 線程組
  優先級三個級別,最低,默認,最高
  setPriority(int )設置優先級
  但是優先級的效果,多核,多線程的CPU上,效果不是很明顯了
  join() 加入 等待該線程終止
  使用join方法的線程,不停止,其他線程運行不了
  static yield()

/*
 * 線程的讓步方法,static yield
 */
class YieldThread implements Runnable{
 public void run(){
  for(int x = 0 ; x < 100 ; x++){
   Thread.yield();
   System.out.println(Thread.currentThread().getName()+"run.."+x);
  }
 }
}
public class ThreadDemo5 {
 public static void main(String[] args) {
  YieldThread yt = new YieldThread();
  Thread t0 = new Thread(yt);
  Thread t1 = new Thread(yt);
  t0.start();
  t1.start();
 }
}



/*
 * 等待該線程終止
 */
class JoinThread implements Runnable{
 public void run(){
  for(int x = 0 ; x < 100; x++){
   System.out.println(Thread.currentThread().getName()+"run.."+x);
  }
 }
}
public class ThreadDemo4 {
 public static void main(String[] args) throws Exception{
  JoinThread jt = new JoinThread();
  Thread t0 = new Thread(jt);
  Thread t1 = new Thread(jt);
     t0.start();
     t0.join();
     t1.start();
  for(int x = 0 ;x < 100 ; x++){
   System.out.println("main...."+x);
  }
 }
}

/*
 * Thread中的toString()
 */
class ThreadToString implements Runnable{
 public void run(){
  for(int x = 0 ; x < 100 ; x++){
   System.out.println(Thread.currentThread().getName()+"run.."+x);
  }
 }
}
public class ThreadDemo3 {
 public static void main(String[] args) {
  ThreadToString tts = new ThreadToString();
  Thread t0 = new Thread(tts);
  Thread t1 = new Thread(tts);
  t0.setPriority(Thread.MAX_PRIORITY);
  t1.setPriority(Thread.NORM_PRIORITY);
  t0.start();
  t1.start();
 // System.out.println(t);
 }
}



                       --------- android培訓java培訓、java學習型技術博客、期待與您交流! ------------

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章