Java:基礎(chǔ)知識——數(shù)據(jù)類型丸凭、關(guān)鍵字、集合、IO流惜犀、類铛碑、線程(生產(chǎn)者與消費者)、模式

一虽界、數(shù)據(jù)類型

??1汽烦、 四類八種:
取值范圍小的可向大的轉(zhuǎn),大轉(zhuǎn)小會失去精度

類型 名稱 占用字節(jié) 取值范圍 默認值 包裝類
整形 byte 字節(jié)型 1(8 Bit) -2^7 ~ 2^7-1 0 Byte
short 短整型 2(16 Bit) -2^15 ~ 2^15-1 0 Short
int 整形 4(32 Bit) -2^31 ~ 2^31 - 1 0 Integer
long 長整型 8(64 Bit) -2^63 ~ 2^63-1 0L Long
浮點型 float 浮點型 4(32 Bit) (7位有效數(shù)字) 0.0f Float
double 雙精度浮點型 8(64 Bit) (16位有效數(shù)字) 0.0d Double
字符型 char 字符型 2(16 Bit) \u0000 ~ \uFFFF \u0000 Character
布爾型 boolean 布爾型 1/8(1 Bit) false莉御、true false Boolean

??2撇吞、String 類:字符串,由英文雙引號括起來的由0到多個數(shù)字颈将、字母梢夯、字符共同組成的一個串言疗,可賦值為null晴圾。而char是由英文單引號括起來的1個數(shù)字或字符或字母。


二噪奄、訪問權(quán)限修飾符死姚、通配符

??1、public:類內(nèi)部勤篮、同個package都毒、子類、任何地方碰缔。
??2账劲、protected:類內(nèi)部、同個package金抡、子類瀑焦。即可以被子類和同一個包內(nèi)的類訪問。
??3梗肝、default:類內(nèi)部榛瓮、同個package。即只可以被同一個包內(nèi)的類訪問巫击。
??4禀晓、peivate:類內(nèi)部。即只可以被該類內(nèi)部自己訪問坝锰。
??5粹懒、通配符:使用問號 " ? " 表示所有類型

class Test {
      public void showInfo(List<?> list) {
         
      }
}

??6、有限制的通配符:例如泛型為<? extends Person>顷级,表示只允許泛型類型為Person或Person子類的引用調(diào)用


三凫乖、關(guān)鍵字this、super、static拣凹、final森爽、abstract

??1、this:表示引用本類對象的屬性嚣镜、方法爬迟。先從自己的類中引用,若找不到菊匿,如果有父類付呕,則會從父類里找。

??2跌捆、super:子類用super來調(diào)用父類的屬性徽职、方法或構(gòu)造器,可用于區(qū)分子父類之間同名的成員佩厚。super可以一直向上追溯多個層級的父類姆钉。

??3、static:類變量(靜態(tài)變量)抄瓦,不用通過實例化就能調(diào)用潮瓶,直接 類名.該關(guān)鍵字變量名(Person.country),被所有這個類的實例化對象所共享钙姊√焊ǎ可修飾屬性、方法煞额、代碼塊和內(nèi)部類思恐。

public class Person {
    static Stirng country;
    int age;
    Stirng name;

    public static void getInfo() {
       ······
    }
}

??4、final:標記的變量為常量膊毁,只能被賦值一次胀莹,名稱大寫。標記的類不能被繼承媚媒,標記的方法不能被重寫

??5嗜逻、abstract:修飾類或方法,成為抽象類或抽象方法

四缭召、集合栈顷、迭代、泛型

??1嵌巷、Map:具有映射關(guān)系的集合萄凤,保存具有映射關(guān)系的數(shù)據(jù),即 key-value對 一對一
????????1-①:HashMap是Map的接口的典型實現(xiàn)

Map map = new HashMap();

????????1-②:TreeMap對所有的 key-value對 處于有序狀態(tài)搪哪,默認為自然排序

??2靡努、List:有序,可重復(fù)的集合
????????2-①:ArrayList是List的接口的典型實現(xiàn)

List list= new ArrayList();

??3、Set:無序惑朦、不可重復(fù)的集合
????????3-①:HashSet是Set接口的典型實現(xiàn)兽泄,存放值時根據(jù)值的hashcode值來決定存放位置。集合中的元素可以為null漾月。

Set set = new HashSet();

????????3-②:TreeSet是SortSet接口的實現(xiàn)類病梢,它會調(diào)用集合元素的compareTo(Object obj)方法,確保集合元素處于排序狀態(tài)梁肿。必須放入同樣類的對象蜓陌。默認為自然排序,升序排列吩蔑。

Set set = new TreeSet();

??4钮热、foreach 迭代集合:使用格式為

for(Object obj : 要迭代的對象) {
      System.out.print(obj);
}

??5、Iterator 迭代器遍歷集合:使用格式為

Iterator<Integer> it = set.iterator();
while(it.hashNext()) {
       System.out.print(it.next());
}

??6烛芬、foreach與Iterator的區(qū)別:Iterator 提供可在遍歷時刪除集合內(nèi)元素的方法隧期。

??7、泛型<類/接口>:讓集合只能存放相同類型的對象/類(若是數(shù)據(jù)類型用Integer蛀骇、String之類的)厌秒。

Set<String> set = new HashSett<String>();

interface Generator<T> {
      T test(T t);
}

五读拆、IO(字節(jié)流xxxStream與字符流xxxReader/xxxWriter)

flish():關(guān)于 flush() 的含義擅憔,清空緩沖區(qū)。

??1檐晕、File類:能新建暑诸、刪除和重命名文件和目錄。(如需要訪問文件內(nèi)容則需要輸入/輸出流)
??2辟灰、Input:計算機把外部數(shù)據(jù)(磁盤个榕、光盤等儲存設(shè)備)讀到自己的代碼程序中為“輸入”。(計算機把外部數(shù)據(jù)輸入到自己的程序中)

??3芥喇、Output:計算機把代碼程序中的數(shù)據(jù)寫到磁盤西采、光盤等儲存設(shè)備中為“輸出”。(計算機把程序的數(shù)據(jù)輸出到磁盤中)继控。

??4械馆、文件流(數(shù)據(jù)流的讀寫是基于文件的操作)計算機與硬盤之間的io操作,讀寫較慢
????????4-①:FileInputStream 字節(jié)輸入流

public static void main(String[] args) {
    try {
          FileInputStream in = new FileInputStream("文件位置");  
          // 把文件字節(jié)輸入流放到緩沖字節(jié)輸入流對象里
          // BufferedInputStream bis = new BufferedInputStream(in);

          // 讀取的是字節(jié)
          byte[] b = new byte[100];
          in.read(b);
          // 從磁盤中讀取xx個字節(jié)的數(shù)據(jù)進入到程序中
          System.out.println(new String(b));

          // 最晚開的最早關(guān)
          // bis.cloce(); 
          in.close();
    } catch (Exception e) {
          e.printStackTrace();
    }
}

????????4-②:FileOutputStream 字節(jié)輸出流

public static void main(String[] args) {
    try {
          // 后面 true 為追加模式(append)武通,避免直接覆蓋
          FileOutputStream out = new FileOutputStream("文件位置", true);
          // 把文件字節(jié)輸入流放到緩沖字節(jié)輸入流對象里
          // BufferedOutputStream bos = new BufferedOutputStream(out);
          String str = "一段文本";
  
          // 從程序中輸出字節(jié)到磁盤中保存
          out.write(str.getBytes());
          out.flush();
          // 最晚開的最早關(guān)
          // bos.cloce(); 
          in.close();
    } catch (Exception e) {
          e.printStackTrace();
    }
}

????????4-③:FileReader 字符輸入流

public static void main(String[] args) {
    try {
          FileReader fr = new FileReader("文件位置");
          // 把文件字節(jié)輸入流放到緩沖字節(jié)輸入流對象里
          // BufferedReader br= new BufferedReader (fr);

          // 讀取的是字符
          char[] c = new char[45];
          fr.read(c);
          System.out.println(new String(c));
          // 最晚開的最早關(guān)
          // br.cloce();
          fr.close();
    } catch (Exception e) {
          e.printStackTrace();
    }
}

????????4-④:FileWriter 字符輸出流

public static void main(String[] args) {
    try {
          FileWriter fw = new FileWriter("文件位置", true);
          // 把文件字節(jié)輸入流放到緩沖字節(jié)輸入流對象里
          // BufferedWriter bw= new BufferedWriter(fw);
          String str = "一段文本";

          fw.write(str);
          fw.flush();
          // 最晚開的最早關(guān)
          // bw.cloce();
          fw.close();
    } catch (Exception e) {
          e.printStackTrace();
    }
}

??5霹崎、緩沖流(數(shù)據(jù)流的讀寫是基于內(nèi)存的操作)把數(shù)據(jù)先緩沖進內(nèi)存里,能提高數(shù)據(jù)的讀寫速度
????????5-①BufferedInputStream
????????5-②BufferedOutputStream
????????5-③BufferedReader
????????5-④BufferedWriter

??6冶忱、轉(zhuǎn)換流(字節(jié)流尾菇、字符流之間轉(zhuǎn)換)
????????6-①InputStreamReader:字節(jié)輸入流轉(zhuǎn)換成字符輸入流
例:

FileInputStream fis = new FileInputStream("文件位置");
InputStreamReader isr = new InputStreamReader(fis, "編碼格式");

????????6-②OutputStreamWriter:字節(jié)輸出流轉(zhuǎn)換成字符輸出流

FileOutputStream fos = new FileOutputStream("文件位置");
OutputStreamWriter isr = new OutputStreamWriter(fos, "編碼格式");

??7、隨機存取流
????????7-①:RandomAccessFile類:程序可隨機跳到文件的任意地方來讀寫文件

// "r":只讀
RandomAccessFile ra = new RandomAccessFile("文件位置", "r");
// 文件起始點從零開始
ra.seek(0);

// 設(shè)置讀取字符串的長度
bytep[] b = new byte[100];
System.out.print(new String(b));

??8派诬、對象流
????????8-① 序列化 Serialize 將對象轉(zhuǎn)化成字節(jié)流儲存在硬盤中劳淆。用 ObjectInputStream類將一個Java對象(該java對象必須實現(xiàn)Serializable接口)寫入IO流中,凡是實現(xiàn) Serializable 接口的類默赂,都有一個序列化版本標識符的靜態(tài)變量憔儿。若要序列化和反序列化同一個對象,則要保證serialVersionUID是相同的放可。
實現(xiàn)序列化接口的對象 Person.java

import java.io.Serializable;

public class Person implements Serializable{
    // serialVersionUID  用來表明類的不同版本谒臼,若不自定義則會自動隨機生成
    private static final long serialVersionUID = 1L;
    int age;
    String gender;
    String name ;

    public Person() {}
    public Person(int age, String gender, String name) {
        this.age = age;
        this.gender = gender;
        this.name = name;
    }

    public int getAge() {
        return age;
    }
    public String getGender() {
        return gender;
    }
    public String getName() {
        return name;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public void setGender(String gender) {
        this.gender = gender;
    }
    public void setName(String name) {
        this.name = name;
    }
}

序列化

public static void main(String[] args) {
    Person p = new Person(25, "男", "張三");
    try{
        // 用對象流將java對象轉(zhuǎn)換為字節(jié)流,并輸出到指定文件位置
        FileOutputStream fos = new FileOutputStream("文件位置");
        ObjectOutputStream oos = new ObjectOutputStream(fos);
        // 對象流將對象寫入文件中
        oos.writeObject(p);
        // System.out.println(p.getName());
        oos.flush();
        oos.close();
    }catch(Exception e){
        e.getStackTrace();
    }
 }

????????8-② 反序列化 Deserialize將字節(jié)流轉(zhuǎn)化成對象耀里。用ObjectOutputStream類從IO流中恢復(fù)該Java對象蜈缤。
反序列化

public static void main(String[] args) {
    try{
        // 計算機讀取(輸入)文件冯挎,用對象流將字節(jié)流轉(zhuǎn)換為java對象底哥,并保存到對應(yīng)類型(強制轉(zhuǎn)換)的類中
        FileInputStream fis = new FileInputStream("文件位置");
        ObjectInputStream ois = new ObjectInputStream(fis);
        Person p = (Person)ois.readObject();
        ois.close();
        System.out.println("反序列化:" + p.getName());
    }catch(Exception e){
        e.getStackTrace();
    }
 }

六、類

??1房官、方法重載(同一個類里)
????????方法名相同趾徽,但參數(shù)個數(shù)不同、參數(shù)的數(shù)據(jù)類型不同或者返回值的數(shù)據(jù)類型不同翰守。

??2孵奶、方法重寫(子類對父類方法的覆蓋)
????????子類對父類的方法重寫,只能重寫方法體的代碼(不能使用更嚴格的訪問權(quán)限蜡峰、拋出的異常不能大于父類被重寫方法的異常了袁、同為static或!static)。

??3湿颅、初始化塊{······}(無static修飾)
????????執(zhí)行順序:靜態(tài)代碼塊(只執(zhí)行一次)-->初始化塊(每次實例化一個對象都執(zhí)行一次)-->構(gòu)造方法载绿,按順序執(zhí)行多個初始化塊。

??4油航、抽象類 abstract
????????特征通用的父類崭庸,不能被實例化,里面的方法必須被子類重寫谊囚,方法體由子類提供怕享,且重寫方法時方法上面需添加 @override

??5、接口 interface
????????一個類里可實現(xiàn)多個接口秒啦,接口可繼承其他接口熬粗,實現(xiàn)接口類用 class xxx implements 接口類名{}。接口中所有的成員變量默認都是public static final修飾的余境,所有方法默認都是public abstract修飾的(這些修飾符可忽略不寫)驻呐,接口沒有構(gòu)造器灌诅。

抽象類與接口的區(qū)別:抽象是對一類事物的高度抽象,包括屬性和方法含末,形成一個父類猜拾;接口是對一系列動作的抽象,實現(xiàn)相應(yīng)的接口即為完成一系列動作佣盒。

??6挎袜、枚舉類 enum

public enum Season{
      SPRING("春天","百花");
      SUMMER("夏天","熱烈");
      AUTUMN("秋天","蕭瑟");
      WINTER("冬天","埋葬");

      private final String name;
      private final String desc;

      prinvate Season(Strinf name, String desc) {
        this.name = name;
        this.desc = desc;
      }

      public void showInfo() {
            System.out.print(this.name + ":" + this.desc);
      }
}
public class Test {
      public static void main(String[] args) {
            Season spring = Season.SPRING;
            speing.showInfo();
      }
}

??7、自定義工具類Utils.java
????????里面放入許多個需要反復(fù)執(zhí)行的方法肥惭。

??8盯仪、Collections操作集合的工具類
????????是一個操作List、Map蜜葱、Set等集合的工具類全景。此類提供了大量方法對集合元素進行排序、查詢和修改等操作牵囤。也可解決多線程并發(fā)訪問集合時的線程安全等問題爸黄。

??9、Javabean:是一個包裝好的可以重復(fù)使用的組件揭鳞,一種特殊的Java類——public 類炕贵、有一個無參的公共構(gòu)造器、private 屬性野崇、且有對應(yīng)的 public get set方法用來修改和獲取屬性称开、(也可以有一系列的操作方法和事件處理器)。

七舞骆、線程

生命周期:新建(執(zhí)行start()方法之前钥弯,線程實例創(chuàng)建,尚未被啟動)督禽、就緒(執(zhí)行start()方法之后,在就緒隊列中排隊)总处、運行(獲得CPU資源正在執(zhí)行run()方法)狈惫、阻塞(正在運行的線程讓出CPU并暫停自己的執(zhí)行run()方法,可通過wait()進入等待狀態(tài)鹦马、或通過在獲取同步鎖失敗后進入等待狀態(tài)胧谈、或通過sleep()或join()使線程進入阻塞狀態(tài),阻塞狀態(tài)終止之后會轉(zhuǎn)回就緒狀態(tài)重新排隊)荸频、死亡(自然終止或用stop()方法終止)
??1菱肖、程序:某種語言編寫的一組指令的集合。
??2旭从、進程:是程序的實體稳强,一個程序至少有一個進程场仲,存在它自身的產(chǎn)生、存在和消亡退疫。
??3渠缕、線程:是進程的一個實體,一個線程只能屬于一個進程褒繁,而一個進程至少有一個線程亦鳞,線程是程序內(nèi)部的一條執(zhí)行路徑。(若程序可同一時間執(zhí)行多個線程棒坏,則這個程序就是支持多線程的)
??4燕差、同步與異步:同步是指一個進程在執(zhí)行某個請求的時,若該請求需要一段時間才能返回信息坝冕,那么這個進程會一直等待谁不,直到接收了返回信息才能繼續(xù)執(zhí)行下去;異步是指進程不需要一直等待徽诲,在接受返回信息的期間刹帕,可以繼續(xù)執(zhí)行下面的操作,不管其他進程的狀態(tài)谎替。
??5偷溺、創(chuàng)建線程的兩種方式
????????5-①繼承Thread類:自定義子類繼承→子類重寫 run() →創(chuàng)建Thread子類對象→子類對象調(diào)用線程 strat()
執(zhí)行多次后可發(fā)現(xiàn)線程是異步進行的(main線程與ThreadSon線程)

public class Test {
    public static void main(String[] args) {
        String name = Thread.currentThread().getName();
        Thread thread = new ThreadSon();
        thread.start();
        System.out.println(name + " 線程:4");
        System.out.println(name + " 線程:5");
        System.out.println(name + " 線程:6");
    }
}

class ThreadSon extends Thread{
    @Override
    public void run() {
        System.out.println("1");
        System.out.println("2");
        System.out.println("3");
    }
}

輸出結(jié)果為:

>> main 線程:4
>> 1
>> main 線程:5
>> 2
>> 3
>> main 線程:6

????????5-②實現(xiàn) Runnable 接口

public class Test {
    public static void main(String[] args) {
        Thread thread = new Thread(new toSleepRunnble());
        // 或者使用如下方式:
        // Runnable run = new toSleepRunnble();
        // Thread thread = new Thread(run);

        thread.start();
        System.out.println("4");
        System.out.println("5");
        System.out.println("6");
    }
}

class toSleepRunnble implements Runnable{
    @Override
    public void run() {
        System.out.println("1");
        System.out.println("2");
    }
}

??5、相關(guān)方法
????????5-① yield() 線程讓步:讓其它優(yōu)先級相同或更高且數(shù)據(jù)量更大的線程優(yōu)先獲得運行機會钱贯,此時調(diào)用此方法的線程回到就緒狀態(tài)重新排隊(效果有待商榷)
????????5-② join():即使是低優(yōu)先級的線程也可以獲得先執(zhí)行權(quán)挫掏。即阻塞當前所在類的活動線程,讓調(diào)用了 .join() 方法的線程插入進來活動秩命,等到j(luò)oin進來的線程執(zhí)行完畢尉共,再讓main方法執(zhí)行其它被阻塞的線程。

關(guān)于join()的使用實例

public class Test {
    public static void main(String[] args) {
        String name = Thread.currentThread().getName();
        Thread thread = new ThreadSon();
        thread.start();
        try {
            thread.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(name + " 線程:4");
        System.out.println(name + " 線程:5");
        System.out.println(name + " 線程:6");
    }
}

class ThreadSon extends Thread{
    @Override
    public void run() {
        System.out.println("1");
        System.out.println("2");
        System.out.println("3");
    }
}

輸出結(jié)果為:

>> 1
>> 2
>> 3
>> main 線程:4
>> main 線程:5
>> main 線程:6

若不添加 join() 則:

>> 1
>> main 線程:4
>> main 線程:5
>> 2
>> main 線程:6
>> 3

????????5-③ sleep(long millis):讓當前活動線程“沉睡”指定的時間弃锐,而其他線程先執(zhí)行袄友,但不會釋放占用的同步鎖,時間到后釋放鎖霹菊,回到就緒狀態(tài)重新排隊剧蚣。
????????5-④ wait():令當前線程掛起并放棄CPU、同步資源(同步鎖)旋廷,使別的線程可訪問并修改共享資源鸠按,而當前線程排隊等候再次對資源的訪問。需要使用notify()來喚醒饶碘。

關(guān)于sleep() 與wait()的區(qū)別: wait()(或wait(long))是Object的一個方法目尖,且需放在同步鎖的方法中,且須使用notify()(或timing out)才能喚醒該線程占有的同步鎖扎运;sleep()是Thread的一個方法瑟曲,若該線程在使用了sleep()的前面又使用了同步鎖饮戳,則需要等線程主動醒來才能退出阻塞狀態(tài)且釋放這個鎖。

????????5-⑤ notify()喚醒正在排隊等候同步資源的線程中優(yōu)先級最高者結(jié)束等待测蹲。

關(guān)于wait() 與 notify() 的簡析

????????5-⑥ stop():強制線程生命期結(jié)束
????????5-⑦ boolean isAlive():判斷線程是否還活著

關(guān)于 wait() 和 notify() 的使用實例——消費者與生產(chǎn)者問題
題目:生產(chǎn)者(Producer)將產(chǎn)品交給店員(Clerk莹捡,相當于倉庫),消費者(Customer)從店員處取走產(chǎn)品扣甲。店員一次只能拿 20 個產(chǎn)品(生產(chǎn)者最大生產(chǎn)量)篮赢,如果生產(chǎn)者試圖生產(chǎn)更多產(chǎn)品,店員會叫生產(chǎn)者停一下琉挖,等待下次通知才能生產(chǎn)启泣。當?shù)陠T拿來產(chǎn)品,店員會通知消費者前來消費示辈。如果店中的產(chǎn)品數(shù)量不足寥茫,店員會叫消費者等一下,等待下次店員拿來了產(chǎn)品后通知了消費者矾麻,消費者才能消費纱耻。

一個生產(chǎn)者線程與一個消費者線程

public class TestThread {
    public static void main(String[] args) {
        /**
         * 多個線程排隊獲取CPU的使用權(quán)
         * 情況一:當其中一個線程獲取了同步鎖,并使用了 wait() 方法后险耀,這個線程從運行狀態(tài)退出弄喘,
         * 進入等待阻塞狀態(tài)(進入等待隊列),同時釋放所持有的同步鎖(先獲取鎖再釋放鎖)
         * 直到被另一個線程使用 notify() 喚醒甩牺,其它的線程才從阻塞狀態(tài)進入到可運行狀態(tài)蘑志。(前提是這幾個個線程等待的是同一共享資源(同一個同步鎖))
         * 情況二:當其中一個線程獲取了鎖,根據(jù)判斷條件若不使用 wait() 方法贬派,那么將直接執(zhí)行 wait() 下面的代碼
         * 等這些步驟執(zhí)行完急但,就可以使用 notify()或notifyAll() 方法喚醒(通知)其它線程去排隊獲取同步鎖
         */
        Clerk clerk = new Clerk();  //店員

        Thread producThread = new Thread(new Producer(clerk), "生產(chǎn)者");
        Thread customerThread = new Thread(new Customer(clerk), "消費者");

        producThread.start();
        customerThread.start();
    }
}

//店員
class Clerk{
    // public int totalProduct = 0;    //店員身上有 0 個產(chǎn)品
    // public boolean isEnough = false;    //產(chǎn)品不足
    public int totalProduct = 20;    //店員身上有 20 個產(chǎn)品,初始化時預(yù)設(shè)店員身上產(chǎn)品充足
    public boolean isEnough = true;    //產(chǎn)品是否足夠搞乏,false為庫存不足波桩,true為庫存足夠
    public static final int MAX_PRODUCT = 20;   //店員身上最大的產(chǎn)品數(shù)為20
}


//生產(chǎn)者
class Producer implements Runnable{
    Clerk clerk;
    String name;

    public Producer(Clerk clerk){
        this.clerk = clerk;
    }

    @Override
    public void run() {
        /**
         * 注意使用 while 在外圍包含著同步鎖的原因:為了讓生產(chǎn)者無線次數(shù)地生產(chǎn)產(chǎn)品,消費者不斷地消費產(chǎn)品查描。
         * 若去掉外面的 while 循環(huán)的話突委,生產(chǎn)者線程只能被 notify 一次,即只能執(zhí)行一次 wait() 后面的操作冬三,造成生產(chǎn)者線程只能執(zhí)行一次 run() 
         * 當生產(chǎn)者線程再次獲取cpu使用權(quán)時,因為run方法已經(jīng)執(zhí)行完畢缘缚,就會自動 exit() 結(jié)束線程勾笆。徒留消費者線程一直在 wait 中。
         */
        while(true){
            synchronized(clerk){
                try {
                    //獲取當前線程的名稱
                    name = Thread.currentThread().getName();
                    
                    /**
                     * 當判斷產(chǎn)品足夠時桥滨,執(zhí)行wait()方法窝爪,同步鎖被釋放弛车,生產(chǎn)者線程進入等待阻塞隊列,不執(zhí)行后面的操作
                     * 關(guān)于wait()方法放在while循環(huán)里是為了防止線程被錯誤地蒲每、提早地被喚醒
                     */
                    while(clerk.isEnough){
                        System.out.println( name + " 拿到了同步鎖纷跛,發(fā)現(xiàn)貨物還足夠,于是繼續(xù)等待···");
                        clerk.wait();
                        System.out.println( name + " 再次拿到了同步鎖邀杏,從 wait() 中被喚醒贫奠!");
                    }

                    //需要操作的代碼塊放在 wait()下面是因為,當線程被 notify 之后是從 wait() 下面的代碼塊繼續(xù)執(zhí)行的
                    System.out.println(name + " 正在生產(chǎn)產(chǎn)品···");
                    while(clerk.totalProduct < clerk.MAX_PRODUCT){
                        //生產(chǎn)者每隔 3 秒生產(chǎn) 5 個產(chǎn)品
                        Thread.sleep(3000);
                        if(clerk.MAX_PRODUCT - clerk.totalProduct >= 5){
                            clerk.totalProduct += 5;
                            System.out.println("---->已經(jīng)生產(chǎn) 5 個產(chǎn)品望蜡,現(xiàn)在產(chǎn)品庫存為: " + clerk.totalProduct + " 個產(chǎn)品唤崭!");
                        }
                        else{
                            int num = clerk.MAX_PRODUCT-clerk.totalProduct;
                            clerk.totalProduct += num;
                            System.out.println("---->已經(jīng)生產(chǎn) " + num + " 個產(chǎn)品,現(xiàn)在產(chǎn)品庫存為: " + clerk.totalProduct + " 個產(chǎn)品脖律!");
                            Thread.sleep(1000);
                        }
                    }
                    System.out.println("生產(chǎn)完畢谢肾,產(chǎn)品數(shù)量已充足,請消費者繼續(xù)購物~~~");
                    clerk.isEnough =true;
                    Thread.sleep(3000);
                    
                    //因為此時消費者線程在等待阻塞狀態(tài)小泉,所以使用 notify() 時芦疏,在等待狀態(tài)的線程收到通知后退出等待隊列
                    //多個消費者線程時使用 clerk.notifyAll();
                    clerk.notify();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
       }
    }
}


//消費者
class Customer implements Runnable{
    Clerk clerk;
    String name;
    int num;

    public Customer(Clerk clerk){
        this.clerk = clerk;
    }

    @Override
    public void run() {
        //不斷有消費者來購買產(chǎn)品
        while(true){
            synchronized(clerk){
                try {
                    name = Thread.currentThread().getName();

                    //當產(chǎn)品不足時等待,且釋放同步鎖微姊,讓其它線程有機會獲取同步鎖
                    while(clerk.isEnough == false){
                        System.out.println( name + " 拿到了同步鎖酸茴,發(fā)現(xiàn)貨架上產(chǎn)品不夠了,于是繼續(xù)等待···");
                        clerk.wait();
                        System.out.println( name + " 再次拿到了同步鎖柒桑,從 wait() 中被喚醒弊决!");
                    }
                    
                    //消費者每次執(zhí)行一次購買產(chǎn)品的操作
                    System.out.println( name + " 進來了,正在消費···");
                    //消費者隨機購買[1,8]個產(chǎn)品數(shù)(即最多買 8 個產(chǎn)品魁淳,最少買 1 個)
                    num = (int)(Math.random() * 8) + 1;
                    if(clerk.totalProduct >= num){
                        //消費者每隔 2 秒消費 num 個產(chǎn)品
                        Thread.sleep(2000);
                        clerk.totalProduct -= num;
                        System.out.println("----> " + name + " 購買了 " + num + " 個產(chǎn)品飘诗,現(xiàn)貨柜剩余: " + clerk.totalProduct + " 個產(chǎn)品!");
                        Thread.sleep(1000);
                        System.out.println(name + " 離開了界逛!");
                        Thread.sleep(1000);
                        System.out.println("請下一位消費者上前購物~~~");
                    }else{
                        Thread.sleep(2000);
                        System.out.println(name + " 需要購買 " + num + " 個產(chǎn)品昆稿,看到貨柜產(chǎn)品數(shù)量不太夠自己的需求而離開了!店員正在提醒生產(chǎn)者生產(chǎn)產(chǎn)品~~~");
                        clerk.isEnough = false;
                    }
                    Thread.sleep(3000);
                    //因為此時生產(chǎn)者線程在排隊等待的狀態(tài)息拜,所以使用 notify() 時溉潭,在等待狀態(tài)的線程 收到通知后退出等待隊列
                    //如果有多個線程則 clerk.notifyAll();
                    clerk.notify();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

結(jié)果如下:

一個生產(chǎn)者一個店員一個消費者

一個生產(chǎn)者線程與多個消費者線程

public class TestThread {
    public static void main(String[] args) {
        Clerk clerk = new Clerk();  //店員

        Thread producThread = new Thread(new Producer(clerk), "生產(chǎn)者");
        Thread customerThread = new Thread(new Customer(clerk), "阿大");
        Thread customerThread2 = new Thread(new Customer(clerk), "小二");
        Thread customerThread3 = new Thread(new Customer(clerk), "張三");
        Thread customerThread4 = new Thread(new Customer(clerk), "李四");
        Thread customerThread5 = new Thread(new Customer(clerk), "王五");

        producThread.start();
        customerThread.start();
        customerThread2.start();
        customerThread3.start();
        customerThread4.start();
        customerThread5.start();
    }
}

同時把 notify() 替換為 notifyAll();

結(jié)果如下:

一個生產(chǎn)者多個消費者

需要注意的問題如下:

一、wait() 方法在 while 里面而不在 if 里面的原因:可能會出現(xiàn)某一線程被 提前喚醒 通知去獲取同步鎖(多線程的情況下)
????若 wait() 在 if 語句if(condition){ obj.wait(); }
????那么線程執(zhí)行 run 方法時可能會出現(xiàn)以下這種情況:

A線程按順序執(zhí)行 if 語句→ 若 condition == trueA線程執(zhí)行 wait()方法 放棄同步鎖少欺,進入等待阻塞隊列喳瓣,此時A線程的 run() 方法尚未執(zhí)行完 → 另一個在排隊的B線程搶到同步鎖,獲取同步鎖的B線程執(zhí)行完一系列操作赞别,然后執(zhí)行 notifyAll()方法 喚醒其它線程畏陕,B線程run()方法 執(zhí)行完畢C線程被喚醒,并且搶到同步鎖的C線程執(zhí)行完一系列操作仿滔,然后執(zhí)行 notifyAll()方法 喚醒其它線程 → ······ → 直到A線程從 wait() 中被其它線程的 notify() 喚醒惠毁,搶到同步鎖的 A線程 不再從最開始的 if語句 開始判斷犹芹,而是直接執(zhí)行 wait() 后面的語句——執(zhí)行完 if 語句 范圍內(nèi)的 wait() 后面的語句,再執(zhí)行 if 語句 范圍外的 wait() 下方的語句(在這一步中鞠绰,如果某些條件已經(jīng)被改變了而A線程又不做判斷的話腰埂,很容易導(dǎo)致出錯!),執(zhí)行完一系列操作后接著執(zhí)行 notifyAll() 方法 喚醒其它線程,此時A線程的 run() 方法執(zhí)行完畢(其它線程被喚醒并執(zhí)行一系列操作······)A線程在排隊時再次搶到同步鎖啸澡,然后按順序執(zhí)行 if 語句······

????若 wait() 在 while 語句while(condition){ obj.wait(); }
????那么線程執(zhí)行 run 方法時會出現(xiàn)以下這種情況:

A線程按順序執(zhí)行 while 語句→ 若 condition == trueA線程執(zhí)行 wait()方法 放棄同步鎖穆律,進入等待阻塞隊列,此時A線程的 run() 方法尚未執(zhí)行完 → 另一個在排隊的B線程搶到同步鎖,獲取同步鎖的B線程執(zhí)行完一系列操作,然后執(zhí)行 notifyAll()方法 喚醒其它線程,B線程run()方法 執(zhí)行完畢C線程被喚醒蛔趴,并且搶到同步鎖的C線程執(zhí)行完一系列操作,然后執(zhí)行 notifyAll()方法 喚醒其它線程 → ······ → 直到A線程從 wait() 中被其它線程的 notify() 喚醒例朱,搶到同步鎖的 A線程 不再從最開始的 while 語句 開始判斷孝情,而是直接執(zhí)行 wait() 后面的語句——執(zhí)行完 while 語句 范圍內(nèi)的 wait() 后面的語句,再因為 while 循環(huán) 而回到之前再次判斷 condition是否為 true 洒嗤,若 condition == true 則A線程放棄同步鎖箫荡,繼續(xù)等待;若 condition == false 渔隶,則A線程跳出 while 循環(huán)羔挡,執(zhí)行 while 語句 范圍之外的 wait() 下方的語句,執(zhí)行完一系列操作后接著執(zhí)行 notifyAll() 方法 喚醒其它線程间唉,此時A線程的 run() 方法執(zhí)行完畢(其它線程被喚醒并執(zhí)行一系列操作······)A線程在排隊時再次搶到同步鎖绞灼,然后按順序執(zhí)行 where 語句······

二、synchronized() 鎖外面被 while(true) 包含:為了將生產(chǎn)者這個線程不斷生產(chǎn)和消費者這個線程不斷消費呈野,所以要在鎖外面用 while(true) 設(shè)置死循環(huán)(根據(jù)情況低矮,也可以將 true 改為其他限制條件)

??6、同步鎖 synchronized:一個線程訪問一個對象中的synchronized同步代碼塊時被冒,其他試圖訪問該對象的線程將被阻塞军掂。
模擬用戶的取票操作(用戶先來的先取票,后來的后取票)
????????6-①:在方法聲明中加上鎖昨悼,表示這個方法為同步方法

public class Test {
    public static void main(String[] args) {
        TicketMachine tm = new TicketMachine();
        
        // 新建線程且自定義線程名稱
        Runnable user1_run = new UserRunnable(tm, 47);
        Thread user1 = new Thread(user1_run, "第一個用戶");

        Runnable user2_run = new UserRunnable(tm, 5);
        Thread user2 = new Thread(user2_run, "第二個用戶");
        
        user1.start();
        user2.start();
        
    }
}

// 取票機器
class TicketMachine{
    int total = 50;
    String threadName;

    public synchronized void getTicket(int n){
       threadName = Thread.currentThread().getName();
        if(total >= n){
            System.out.println(threadName +" 操作中蝗锥,原有" + total + "張票!");
            total -= n;
            System.out.println(threadName +" 現(xiàn)在已經(jīng)取了" + n + "張票了率触!玛追,還剩" + total + "張票!闲延!");
        }else{
            System.out.println("票已經(jīng)不足痊剖,請前往另一臺機器取。");
        }
    }

}

class UserRunnable implements Runnable{
    TicketMachine tm;
    int n;
    
    // 帶參數(shù)的構(gòu)造方法
    public UserRunnable(TicketMachine tm, int n) {
        this.tm = tm;
        this.n = n;
    }

    @Override
    public void run() {
        // 用戶從機器中取了n張票
        tm.getTicket(n);
    }
}

不加同步鎖

加了同步鎖


?????????????或

????????6-②:在方法中加上對象鎖(鎖住同一個“取票機器”這一對象)

public void getTicket2(int n, TicketMachine tm){
        synchronized(tm){
            name = Thread.currentThread().getName();
            if(total >= n){
                System.out.println(name+"操作中垒玲,原有" + total + "張票陆馁!");
                total -= n;
                System.out.println(name+"現(xiàn)在已經(jīng)取了" + n + "張票了!合愈,還剩" + total + "張票67贰!");
            }else{
                System.out.println("票已經(jīng)不足佛析,請"+name+"前往另一臺機器取益老。");
            }
        }
    }
    // 相應(yīng)main方法里的傳參變化
    public static void main(String[] args) {
        TicketMachine tm = new TicketMachine();
        
        // 新建線程且自定義線程名稱
        Runnable user1_run = new UserRunnable(47, tm);
        Thread user1 = new Thread(user1_run, "第一個用戶");

        Runnable user2_run = new UserRunnable(5, tm);
        Thread user2 = new Thread(user2_run, "第二個用戶");
        
        user1.start();
        user2.start();
        
    }

八、模式

??1寸莫、單例設(shè)計模式
????????若實例化一個對象要占用大量的時間和資源捺萌,則只實例化一個對象。

??2膘茎、模板方法設(shè)計模式
????????抽象類(父類)作為多個子類的通用模板桃纯,子類在抽象類的基礎(chǔ)上擴展、改造披坏。

??3态坦、工廠方法
????????通過工廠把new對象隔離,通過接口來接受不同產(chǎn)品的實現(xiàn)類棒拂。
例:面包的生產(chǎn)
Bread接口以及Bread類

// 面包接口
public interface Bread {
    // 面包的原材料
    void showMaterialsInfo();
    // 面包的口味介紹
    void showTasteInfo();
}

// 奶油面包
class CreamBread implements Bread{
    @Override
    public void showMaterialsInfo() {
        System.out.println("奶油面包的原材料:牛奶伞梯、黃油、干酵粉帚屉、砂糖谜诫,鹽,和雞蛋涮阔。");
    }

    @Override
    public void showTasteInfo() {
        System.out.println("奶油香甜順滑猜绣,面包松軟。");
    }
    
}

// 芝士面包
class CheeseBread implements Bread{
    @Override
    public void showMaterialsInfo() {
        System.out.println("芝士面包的原材料:高粉敬特、低粉掰邢、雞蛋液、水伟阔、黃油辣之、白糖、鹽皱炉、酵母怀估。");
    }

    @Override
    public void showTasteInfo() {
        System.out.println("剛出爐的芝士面包可拉絲,口感豐富、營養(yǎng)美味多搀。");
    }
    
}

// 法棍
class Baguette implements Bread{
    @Override
    public void showMaterialsInfo() {
        System.out.println("法棍的原材料:面粉歧蕉,酵母,鹽康铭,水惯退。");
    }

    @Override
    public void showTasteInfo() {
        System.out.println("表皮松脆,內(nèi)心柔軟而稍具韌性从藤,越嚼越香催跪,充滿麥香味。");
    }
    
}

BreadFactory接口以及BreadFactory的類

// 面包工廠的接口
public interface BreadFactory {
    // 生產(chǎn)之后的返回類型為面包類
    Bread productBread();
}

// 奶油面包工廠接上面包工廠接口類生產(chǎn)奶油面包
class CreamBreadFactory implements BreadFactory{

    @Override
    public Bread productBread() {
        // 奶油面包生產(chǎn)過程
        System.out.println("生產(chǎn)奶油面包中···");
        // 生產(chǎn)完畢返回一個奶油面包類
        return new CreamBread();
    }

}

class CheeseBreadFactory implements BreadFactory{

    @Override
    public Bread productBread() {
        // 芝士面包生產(chǎn)過程
        System.out.println("生產(chǎn)芝士面包中···");
        // 生產(chǎn)完畢返回一個奶油面包類
        return new CheeseBread();
    }

}

class BaguetteFactory implements BreadFactory{

    @Override
    public Bread productBread() {
        // 法棍生產(chǎn)過程
        System.out.println("生產(chǎn)法棍中···");
        // 生產(chǎn)完畢返回一個法棍類
        return new Baguette();
    }
}

main

public class Test{
    public static void main(String[] args) {
        // 面包通過奶油面包加工廠來生產(chǎn)奶油面包
        Bread creamBread = new CreamBreadFactory().productBread();
        creamBread.showMaterialsInfo();
        creamBread.showTasteInfo();

        // 面包通過芝士面包加工廠來生產(chǎn)芝士面包
        Bread cheeseBread = new CheeseBreadFactory().productBread();
        cheeseBread.showMaterialsInfo();
        cheeseBread.showTasteInfo();
    }
}
工廠模式圖
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末夷野,一起剝皮案震驚了整個濱河市懊蒸,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌悯搔,老刑警劉巖骑丸,帶你破解...
    沈念sama閱讀 206,311評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異鳖孤,居然都是意外死亡者娱,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評論 2 382
  • 文/潘曉璐 我一進店門苏揣,熙熙樓的掌柜王于貴愁眉苦臉地迎上來黄鳍,“玉大人,你說我怎么就攤上這事平匈】蚬担” “怎么了?”我有些...
    開封第一講書人閱讀 152,671評論 0 342
  • 文/不壞的土叔 我叫張陵增炭,是天一觀的道長忍燥。 經(jīng)常有香客問我,道長隙姿,這世上最難降的妖魔是什么梅垄? 我笑而不...
    開封第一講書人閱讀 55,252評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮输玷,結(jié)果婚禮上队丝,老公的妹妹穿的比我還像新娘。我一直安慰自己欲鹏,他們只是感情好机久,可當我...
    茶點故事閱讀 64,253評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著赔嚎,像睡著了一般膘盖。 火紅的嫁衣襯著肌膚如雪胧弛。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,031評論 1 285
  • 那天侠畔,我揣著相機與錄音结缚,去河邊找鬼。 笑死践图,一個胖子當著我的面吹牛掺冠,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播码党,決...
    沈念sama閱讀 38,340評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼斥黑!你這毒婦竟也來了揖盘?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,973評論 0 259
  • 序言:老撾萬榮一對情侶失蹤锌奴,失蹤者是張志新(化名)和其女友劉穎兽狭,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體鹿蜀,經(jīng)...
    沈念sama閱讀 43,466評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡箕慧,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,937評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了茴恰。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片颠焦。...
    茶點故事閱讀 38,039評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖往枣,靈堂內(nèi)的尸體忽然破棺而出伐庭,到底是詐尸還是另有隱情,我是刑警寧澤分冈,帶...
    沈念sama閱讀 33,701評論 4 323
  • 正文 年R本政府宣布圾另,位于F島的核電站,受9級特大地震影響雕沉,放射性物質(zhì)發(fā)生泄漏集乔。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,254評論 3 307
  • 文/蒙蒙 一坡椒、第九天 我趴在偏房一處隱蔽的房頂上張望扰路。 院中可真熱鬧,春花似錦肠牲、人聲如沸幼衰。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽渡嚣。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間识椰,已是汗流浹背绝葡。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留腹鹉,地道東北人藏畅。 一個月前我還...
    沈念sama閱讀 45,497評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像功咒,于是被迫代替她去往敵國和親愉阎。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,786評論 2 345

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