一虽界、數(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é)束等待测蹲。
????????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)者線程與多個消費者線程
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é)果如下:
需要注意的問題如下:
一、wait() 方法在 while 里面而不在 if 里面的原因:可能會出現(xiàn)某一線程被 提前喚醒 通知去獲取同步鎖(多線程的情況下)
????若 wait() 在 if 語句里if(condition){ obj.wait(); }
????那么線程執(zhí)行 run 方法時可能會出現(xiàn)以下這種情況:A線程按順序執(zhí)行 if 語句→ 若
condition == true
→ A線程執(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 == true
→ A線程執(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();
}
}