Object類位于java.lang包中,java.lang包有最基礎(chǔ)的和核心的類,在編譯時(shí)會自動導(dǎo)入;
Object類是所有java類的祖先,每個(gè)類都使用Object作為超類,所有對象(數(shù)組)都實(shí)現(xiàn)這個(gè)類的方法.可以使用類型為Objcet的變量指向任意類型的對象
1.clone方法
保護(hù)方法,實(shí)現(xiàn)對象的淺復(fù)制宅荤,只有實(shí)現(xiàn)了Cloneable接口才可以調(diào)用該方法殖侵,否則拋出CloneNotSupportedException異常。
主要是JAVA里除了8種基本類型傳參數(shù)是值傳遞奏司,其他的類對象傳參數(shù)都是引用傳遞,我們有時(shí)候不希望在方法里講參數(shù)改變樟插,這是就需要在類中復(fù)寫clone方法韵洋。
直接調(diào)用Object的clone()方法來copy一個(gè)副本竿刁,但是發(fā)現(xiàn)myEclipse的提示中并沒有該方法,以為在jdk1.7中取消了該方法搪缨,然后我直接敲上clone()后:
[java]?view plain?copy
public?class?TestObject?{??
public?static?void?main(String[]?args)?{??
Student?s?=new?Student(1,?"小時(shí)");??
s.clone();//報(bào)錯(cuò)??
????}??
}??
編譯報(bào)錯(cuò):The method clone() from the type Object is not visible
后來看了看源碼食拜,發(fā)現(xiàn)原來是這樣:
Object源碼中對于clone()方法的描述:
[java]?view plain?copy
protected?native?Object?clone()?throws?CloneNotSupportedException;??
native修飾詞解析:native的方法就是一個(gè)java調(diào)用非java代碼的接口,是一個(gè)其他語言實(shí)現(xiàn)的方法副编。
問題1:Object類是所有?類的父類,那么為什么子類不能訪問父類的protected修飾的方法呢?
以前對protected的理解是錯(cuò)誤的负甸,protected的方法在自己的包中與public相同
在不同包,指的是通過自身實(shí)例(自身的引用)訪問,而不能通過父類實(shí)例(引用)訪問
所以上端代碼中調(diào)用Student類的clone()方法會包編譯錯(cuò)誤,下面的代碼(通過自身引用調(diào))就OK的
[java]?view plain?copy
public?class?TestObject?{??
public?static?void?main(String[]?args)?{??
TestBoject?t?=new?TestObject();??
t.clone();//ok??
????}??
}??
問題2:要想實(shí)現(xiàn)類似文章開頭的代碼功能該怎么辦痹届?
在Object的源碼中注釋到:
首先呻待,如果這個(gè)類沒有實(shí)現(xiàn)Cloneable這個(gè)接口,將會拋出CloneNotSupportedException队腐,但是所有的數(shù)組都被看成實(shí)現(xiàn)了這個(gè)接口蚕捉。此方法執(zhí)行的是該對象的“淺表復(fù)制”,而不“深層復(fù)制”操作香到。
Object 類本身不實(shí)現(xiàn)接口 Cloneable鱼冀,所以在類為 Object 的對象上調(diào)用 clone 方法將會導(dǎo)致在運(yùn)行時(shí)拋出異常。
在Cloneable的源碼中注釋到:
此類實(shí)現(xiàn)了 Cloneable 接口悠就,以指示 Object.clone() 方法可以合法地對該類實(shí)例進(jìn)行按字段復(fù)制千绪。 如果在沒有實(shí)現(xiàn) Cloneable 接口的實(shí)例上調(diào)用 Object 的 clone 方法,則會導(dǎo)致拋出 CloneNotSupportedException異常梗脾。?
按照慣例荸型,實(shí)現(xiàn)此接口的類應(yīng)該使用公共方法重寫 Object.clone(它是受保護(hù)的)。請參閱 Object.clone()炸茧,以獲得有關(guān)重寫此方法的詳細(xì)信息瑞妇。?
注意,此接口不包含 clone 方法梭冠。因此辕狰,因?yàn)槟硞€(gè)對象實(shí)現(xiàn)了此接口就克隆它是不可能的。即使 clone 方法是反射性調(diào)用的控漠,也無法保證它將獲得成功
解讀:Cloneable接口沒有任何方法蔓倍,僅是個(gè)標(biāo)志接口(tagging interface),若要具有克隆能力盐捷,實(shí)現(xiàn)Cloneable接口的類必須重寫從Object繼承來的clone方法偶翅,并調(diào)用Object的clone方法(見下面Object#clone的定義),重寫后的方法應(yīng)為public 的碉渡。For example(標(biāo)準(zhǔn)寫法):
示例代碼:
[java]?view plain?copy
public?class?TestObject?{??
public?static?void?main(String[]?args)?{??
Student?s?=new?Student(1,?"小時(shí)");??
????????System.out.println(s.clone().getClass());??
????????System.out.println(s.clone().equals(s));??
????}??
}??
class?Student?implements?Cloneable{??
public?int?id;??
public?String?name;??
Student(int?id,?String?name){??
this.id?=?id;??
this.name?=?name;??
????}??
public?Object?clone(){??
Student?s?=null;??
try{??
s?=?(Student)super.clone();??
}catch(CloneNotSupportedException?e){??
????????????????e.printStackTrace();??
????????????}??
return?s;???
????}??
}??
2.getClass方法
final方法聚谁,獲得運(yùn)行時(shí)類型。
一滞诺、
getClass方法:
類型:public final Class getClass()
功能:返回該對象的運(yùn)行時(shí)類的java.lang.Class對象(API上的解釋)
有方法類型可以知道形导,該方法只能由類的實(shí)例變量調(diào)用
例子:
[java]view plaincopy
JButton?b1?=?new?JButton("button1");??
System.out.println(b1.getClass());??
輸出:
class javax.swing.JButton
class屬性
當(dāng)你要獲得一個(gè)類的Class對象時(shí)(作函數(shù)參數(shù)的時(shí)候)环疼,你不能調(diào)用getClass方法,那你只能用類名.class來達(dá)到效果
例子:
[java]view plaincopy
System.out.println(JButton.class);??
輸出:
class javax.swing.JButton
getName方法:
類型:public String getName()
功能:以String形式返回次Class對象所表示的實(shí)體名稱
例子:
[java]view plaincopy
JButton?b1?=?new?JButton("button1");??
System.out.println(b1.getName());??
輸出:
javax.swing.JButton
可以發(fā)現(xiàn)用class屬性和getClass返回的輸出是一樣的朵耕,用getName返回的比前面兩種少了class和一個(gè)空格秦爆。
?.eclipse工具 可以按"."然后馬上提示很多方法 供你選擇
那他如何知道"."了以后有哪些方法?
他用的語法就是getClass().getMethods();
二、
.class其實(shí)是在java運(yùn)行時(shí)就加載進(jìn)去的
getClass()是運(yùn)行程序時(shí)動態(tài)加載的
下面以例子說明:
首先建一個(gè)基類Baseclass??
packageclassyongfa;
publicclassBaseclass?{
privateString?height;
publicString?getHeight(){??
returnheight;
}??
publicvoidsetHeight(String?height){??
this.height=height;
}??
下面是繼承Baseclass類Extendclass??
packageclassyongfa;
publicclassExtendclassextendsBaseclass?{
privateString?width;
publicString?getWidth(){??
returnwidth;
}??
publicvoidsetWidth(String?width){??
this.width=width;
}??
publicstaticvoidmain(String[]?arg0){??
Baseclass?baseclass1=newExtendclass();
Baseclass?baseclass2=newBaseclass();
System.out.println(baseclass1.getClass().getSimpleName());//實(shí)際運(yùn)行的是繼承類Extendclass
System.out.println(baseclass2.getClass().getSimpleName());//實(shí)際運(yùn)行的是Baseclass
System.out.println(Baseclass.class.getSimpleName());//加載時(shí)類名
System.out.println(Extendclass.class.getSimpleName());//加載時(shí)類名
}??
結(jié)果是??
Extendclass??
Baseclass??
Baseclass??
Extendclass
三憔披、四種獲取Class對象的方法 Java反射機(jī)制
下面以一個(gè)具體的實(shí)例來說明。此實(shí)例來自《精通Hibernate 3.0 Java數(shù)據(jù)庫持久層開發(fā)實(shí)踐》一書爸吮。
先在com.hqh.reflect下建一個(gè)文件UseInfojava
package com.hqh.reflect;
public classUseInfo{
private Integer id;
private String userName;
private String password;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
package com.hqh.reflect;
public classGetClassTest{
public static void main(String[] args) {
GetClassTest test = new GetClassTest();
if(test.ClassCheck())
System.out.println("OK");
}
public boolean ClassCheck() {
try {
System.out.println("通過類本身獲得對象");
Class userClass =this.getClass();
System.out.println(userClass.getName());
System.out.println("===========");
System.out.println("通過子類的實(shí)例獲得父類對象");
UseInfo useInfo = new UseInfo();
userClass = useInfo.getClass();
System.out.println(userClass.getName());
Class subUserClass = userClass.getSuperclass();
System.out.println(subUserClass.getName());
System.out.println("===========");
System.out.println("通過類名.class獲取對象");
Class forClass =com.hqh.reflect.UseInfo.class;
System.out.println(forClass.getName());
System.out.println("===========");
System.out.println("通過類名的字符串獲取對象");
Class forName =Class.forName("com.hqh.reflect.UseInfo");
System.out.println(forName.getName());
System.out.println("=============");
} catch (Exception e) {
e.printStackTrace();
return false;
}
return true;
}
}
結(jié)果:
通過類本身獲得對象
com.hqh.reflect.GetClassTest
===========
通過子類的實(shí)例獲得父類對象
com.hqh.reflect.UseInfo
java.lang.Object
===========
通過類名.class獲取對象
com.hqh.reflect.UseInfo
===========
通過類名的字符串獲取對象
com.hqh.reflect.UseInfo
=============
3.toString方法
返回該對象的字符串表示芬膝。通常,toString方法會返回一個(gè)“以文本方式表示”此對象的字符串形娇。結(jié)果應(yīng)是一個(gè)簡明但易于讀懂的信息表達(dá)式锰霜。建議所有子類都重寫此方法。
Object類的toString方法返回一個(gè)字符串桐早,該字符串由類名(對象是該類的一個(gè)實(shí)例)癣缅、at 標(biāo)記符“@”和此對象哈希碼的無符號十六進(jìn)制表示組成。換句話說哄酝,該方法返回一個(gè)字符串友存,它的值等于:
getClass().getName() + '@' + Integer.toHexString(hashCode())
返回:
該對象的字符串表示形式。
因?yàn)樗荗bject里面已經(jīng)有了的方法陶衅,而所有類都是繼承Object屡立,所以“所有對象都有這個(gè)方法”。
它通常只是為了方便輸出搀军,比如System.out.println(xx)膨俐,括號里面的“xx”如果不是String類型的話,就自動調(diào)用xx的toString()方法
總而言之罩句,它只是sun公司開發(fā)java的時(shí)候?yàn)榱朔奖闼蓄惖淖址僮鞫匾饧尤氲囊粋€(gè)方法
寫這個(gè)方法的用途就是為了方便操作焚刺,所以在文件操作里面可用可不用
例子1:
publicclassOrc{
public static class A {
public String toString() {
return"this is A";
}
}
public static void main(String[] args) {
????????A obj = newA( );
????????System.out.println(obj);
}
}
如果某個(gè)方法里面有如下句子:?
A obj=new A();
System.out.println(obj);
會得到輸出:this is A
例子2:
public class Orc{
public static class A {
public String getString() {
return"this is A";? ? ? ? ? ? ?
}? ? ?
}
public static void main(String[] args)? {? ? ?
? ? ? ? A obj =newA();? ? ? ? ? ? ?
? ? ? ? ?System. out. println(obj);? ? ? ? ? ? ?
? ? ? ? ?System. out. println(obj.getString());? ? ?
}
}
會得到輸出:xxxx@xxxxxxx的類名加地址形式
System.out.println(obj.getString());
會得到輸出:this is A
看出區(qū)別了嗎,toString的好處是在碰到“println”之類的輸出方法時(shí)會自動調(diào)用门烂,不用顯式打出來乳愉。
public class Zhang {
public static void main(String[] args) {?
? ? ?StringBuffer MyStrBuff1 =newStringBuffer();? ? ? ??
? ? ?MyStrBuff1.append("Hello,Guys!");
? ? ?System.out.println(MyStrBuff1.toString());
? ? ?MyStrBuff1.insert(6, 30);
? ? System.out.println(MyStrBuff1.toString());??
}
}
值得注意的是,?若希望將StringBuffer在屏幕上顯示出來,?則必須首先調(diào)用toString方法把它變成字符串常量,因?yàn)镻rintStream的方法println()不接受StringBuffer類型的參數(shù).
public class Zhang{
public static void main(String[] args){
????String MyStr =new StringBuffer();?
? ? MyStr =new? StringBuffer().append(MyStr).append(" Guys!").toString();
? ? System.out.println(MyStr);?
?}
}
toString()方法在此的作用是將StringBuffer類型轉(zhuǎn)換為String類型.
public class Zhang{
public static void main(String[] args){
? ? ?String MyStr =new StringBuffer().append("hello").toString();
? ? ?MyStr =new StringBuffer().append(MyStr).append(" Guys!").toString();
? ? ?System.out.println(MyStr);
}}
1.toString()方法
Object類具有一個(gè)toString()方法,你創(chuàng)建的每個(gè)類都會繼承該方法诅福。它返回對象的一個(gè)String表示匾委,并且對于調(diào)試非常有幫助。然而對于默認(rèn)的toString()方法往往不能滿足需求氓润,需要覆蓋這個(gè)方法赂乐。
toString()方法將對象轉(zhuǎn)換為字符串】看以下代碼:
package sample;
class Villain {
????private String name;
????protected void set(String nm) {
???????name = nm;
????}
????public Villain(String name) {
???????this.name = name;
????}
????public String toString() {
???????return "I'm a Villain and my name is " + name;
????}
}
public class Orc extends Villain {
????private int orcNumber;
????public Orc(String name, int orcNumber) {
???????super(name);
???????this.orcNumber = orcNumber;
????}
????public void change(String name, int orcNumber) {
???????set(name);
???????this.orcNumber = orcNumber;
????}
????public String toString() {
???????return "Orc" + orcNumber + ":" + super.toString();
????}
????public static void main(String[] args) {
???????Orc orc = new Orc("Limburger", 12);
???????System.out.println(orc);
???????orc.change("Bob", 19);
???????System.out.println(orc);
????}
}
結(jié)果:
sample.Orc@11b86e7
sample.Orc@11b86e7
如果去掉注釋挨措,即加入2個(gè)toString()方法后挖滤,得到
結(jié)果:
Orc12:I'm a Villain and my name is Limburger
Orc19:I'm a Villain and my name is Bob
2.在容器類中使用toString()
編寫一個(gè)工具類,用于在控制臺輸出Iterator浅役。
import java.util.Iterator;
public class Printer {
????static void printAll(Iterator e){
???????while(e.hasNext()){
???????????System.out.println(e.next());
???????}
????}
}
在Hamster類中重寫父類的toString()方法斩松。
public class Hamster {
????private int hamsterNumber;
????public Hamster(int hamsterNumber){
???????this.hamsterNumber=hamsterNumber;
????}
????public String toString(){
???????return "This is Hamster #"+hamsterNumber;
????}
}
在HamsterMaze類中使用容器類加載Hamster類對象并輸出結(jié)果。
import java.util.ArrayList;
import java.util.List;
public class HamsterMaze {
????@SuppressWarnings("unchecked")
????public static void main(String[] args){
???????List list=new ArrayList();
???????for(int i=0;i<3;i++)
???????????list.add(new Hamster(i));
???????Printer.printAll(list.iterator());
????}
}
結(jié)果:
This is Hamster #0
This is Hamster #1
This is Hamster #2
3.一個(gè)實(shí)現(xiàn)toString()的通用的Bean
在作一個(gè)項(xiàng)目時(shí)發(fā)現(xiàn),許多bean需要實(shí)現(xiàn)toString()方法,就實(shí)現(xiàn)一個(gè)通用的bean,然后通過其他繼承即可觉既。
import java.lang.reflect.Field;
public class BaseBean {
????public String toString() {
???????StringBuffer sb = new StringBuffer();
???????try {
???????????Class t = this.getClass();
???????????Field[] fields = t.getDeclaredFields();
???????????for (int i = 0; i < fields.length; i++) {
??????????????Field field = fields[i];
??????????????field.setAccessible(true);
??????????????sb.append("{");
??????????????sb.append(field.getName());
??????????????sb.append(":");
??????????????if (field.getType() == Integer.class) {
??????????????????sb.append(field.getInt(this));
??????????????} else if (field.getType() == Long.class) {
??????????????????sb.append(field.getLong(this));
??????????????} else if (field.getType() == Boolean.class) {
??????????????????sb.append(field.getBoolean(this));
??????????????} else if (field.getType() == char.class) {
??????????????????sb.append(field.getChar(this));
??????????????} else if (field.getType() == Double.class) {
??????????????????sb.append(field.getDouble(this));
??????????????} else if (field.getType() == Float.class) {
??????????????????sb.append(field.getFloat(this));
??????????????} else
??????????????????sb.append(field.get(this));
??????????????sb.append("}");
???????????}
???????} catch (Exception e) {
???????????e.printStackTrace();
???????}
???????return sb.toString();
????}
}
測試類
public class TestBean extends BaseBean {
????private int id;
????public int getId() {
???????return id;
????}
????public void setId(int id) {
???????this.id = id;
????}
????public static void main(String[] args) {
???????TestBean testBean = new TestBean();
???????testBean.setId(9);
???????System.out.println(testBean.toString());
????}
}
結(jié)果
{id:9}
關(guān)于String ,StringBuffer的性能
博客分類:?java語言
通過使用一些輔助性工具來找到程序中的瓶頸谣旁,然后就可以對瓶頸部分的代碼進(jìn)行優(yōu)化。一般有兩種方案:即優(yōu)化代碼或更改設(shè)計(jì)方法抓谴。我們一般會選擇后者蛛壳,因?yàn)椴蝗フ{(diào)用以下代碼要比調(diào)用一些優(yōu)化的代碼更能提高程序的性能。而一個(gè)設(shè)計(jì)良好的程序能夠精簡代碼符欠,從而提高性能嫡霞。
下面將提供一些在JAVA程序的設(shè)計(jì)和編碼中,為了能夠提高JAVA程序的性能希柿,而經(jīng)常采用的一些方法和技巧诊沪。
1.對象的生成和大小的調(diào)整。
JAVA程序設(shè)計(jì)中一個(gè)普遍的問題就是沒有好好的利用JAVA語言本身提供的函數(shù)曾撤,從而常常會生成大量的對象(或?qū)嵗┒艘ΑS捎谙到y(tǒng)不僅要花時(shí)間生成對象,以后可能還需花時(shí)間對這些對象進(jìn)行垃圾回收和處理盾戴。因此寄锐,生成過多的對象將會給程序的性能帶來很大的影響。
例1:關(guān)于String ,StringBuffer尖啡,+和append
JAVA語言提供了對于String類型變量的操作橄仆。但如果使用不當(dāng),會給程序的性能帶來影響衅斩。如下面的語句:
String name=new String("HuangWeiFeng");
System.out.println(name+"is my name");
看似已經(jīng)很精簡了盆顾,其實(shí)并非如此。為了生成二進(jìn)制的代碼畏梆,要進(jìn)行如下的步驟和操作:
(1)?生成新的字符串?new String(STR_1);
(2)?復(fù)制該字符串;
(3)?加載字符串常量"HuangWeiFeng"(STR_2);
(4)?調(diào)用字符串的構(gòu)架器(Constructor);
(5)?保存該字符串到數(shù)組中(從位置0開始);
(6)?從java.io.PrintStream類中得到靜態(tài)的out變量;
(7)?生成新的字符串緩沖變量new StringBuffer(STR_BUF_1);
(8)?復(fù)制該字符串緩沖變量;
(9)?調(diào)用字符串緩沖的構(gòu)架器(Constructor);
(10)?保存該字符串緩沖到數(shù)組中(從位置1開始);
(11)?以STR_1為參數(shù)您宪,調(diào)用字符串緩沖(StringBuffer)類中的append方法;
(12)?加載字符串常量"is my name"(STR_3);
(13)?以STR_3為參數(shù),調(diào)用字符串緩沖(StringBuffer)類中的append方法;
(14)?對于STR_BUF_1執(zhí)行toString命令;
(15)?調(diào)用out變量中的println方法奠涌,輸出結(jié)果宪巨。
由此可以看出,這兩行簡單的代碼溜畅,就生成了STR_1,STR_2,STR_3,STR_4和STR_BUF_1五個(gè)對象變量捏卓。這些生成的類的實(shí)例一般都存放在堆中。堆要對所有類的超類慈格,類的實(shí)例進(jìn)行初始化怠晴,同時(shí)還要調(diào)用類極其每個(gè)超類的構(gòu)架器遥金。而這些操作都是非常消耗系統(tǒng)資源的。因此蒜田,對對象的生成進(jìn)行限制稿械,是完全有必要的。
經(jīng)修改冲粤,上面的代碼可以用如下的代碼來替換美莫。
StringBuffer name=new StringBuffer("HuangWeiFeng");
System.out.println(name.append("is my name.").toString());
系統(tǒng)將進(jìn)行如下的操作:
(1)?生成新的字符串緩沖變量new StringBuffer(STR_BUF_1);
(2)?復(fù)制該字符串緩沖變量;
(3)?加載字符串常量"HuangWeiFeng"(STR_1);
(4)?調(diào)用字符串緩沖的構(gòu)架器(Constructor);
(5)?保存該字符串緩沖到數(shù)組中(從位置1開始);
(6)?從java.io.PrintStream類中得到靜態(tài)的out變量;
(7)?加載STR_BUF_1;
(8)?加載字符串常量"is my name"(STR_2);
(9)?以STR_2為參數(shù),調(diào)用字符串緩沖(StringBuffer)實(shí)例中的append方法;
(10)?對于STR_BUF_1執(zhí)行toString命令(STR_3);
(11)調(diào)用out變量中的println方法梯捕,輸出結(jié)果茂嗓。
由此可以看出,經(jīng)過改進(jìn)后的代碼只生成了四個(gè)對象變量:STR_1,STR_2,STR_3和STR_BUF_1.你可能覺得少生成一個(gè)對象不會對程序的性能有很大的提高科阎。但下面的代碼段2的執(zhí)行速度將是代碼段1的2倍。因?yàn)榇a段1生成了八個(gè)對象忿族,而代碼段2只生成了四個(gè)對象锣笨。
代碼段1:
String name= new StringBuffer("HuangWeiFeng");
name+="is my";
name+="name";
代碼段2:
StringBuffer name=new StringBuffer("HuangWeiFeng");
name.append("is my");
name.append("name.").toString();
因此,充分的利用JAVA提供的庫函數(shù)來優(yōu)化程序道批,對提高JAVA程序的性能時(shí)非常重要的.其注意點(diǎn)主要有如下幾方面错英;?
4.finalize方法
該方法用于釋放資源。因?yàn)闊o法確定該方法什么時(shí)候被調(diào)用隆豹,很少使用椭岩。
1. finalize的作用
finalize()是Object的protected方法,子類可以覆蓋該方法以實(shí)現(xiàn)資源清理工作璃赡,GC在回收對象之前調(diào)用該方法判哥。
finalize()與C++中的析構(gòu)函數(shù)不是對應(yīng)的。C++中的析構(gòu)函數(shù)調(diào)用的時(shí)機(jī)是確定的(對象離開作用域或delete掉)碉考,但Java中的finalize的調(diào)用具有不確定性
不建議用finalize方法完成“非內(nèi)存資源”的清理工作塌计,但建議用于:① 清理本地對象(通過JNI創(chuàng)建的對象);② 作為確保某些非內(nèi)存資源(如Socket侯谁、文件等)釋放的一個(gè)補(bǔ)充:在finalize方法中顯式調(diào)用其他資源釋放方法锌仅。其原因可見下文[finalize的問題]
一些與finalize相關(guān)的方法,由于一些致命的缺陷墙贱,已經(jīng)被廢棄了热芹,如System.runFinalizersOnExit()方法、Runtime.runFinalizersOnExit()方法
System.gc()與System.runFinalization()方法增加了finalize方法執(zhí)行的機(jī)會惨撇,但不可盲目依賴它們
Java語言規(guī)范并不保證finalize方法會被及時(shí)地執(zhí)行伊脓、而且根本不會保證它們會被執(zhí)行
finalize方法可能會帶來性能問題。因?yàn)镴VM通常在單獨(dú)的低優(yōu)先級線程中完成finalize的執(zhí)行
對象再生問題:finalize方法中串纺,可將待回收對象賦值給GC Roots可達(dá)的對象引用丽旅,從而達(dá)到對象再生的目的
finalize方法至多由GC執(zhí)行一次(用戶當(dāng)然可以手動調(diào)用對象的finalize方法椰棘,但并不影響GC對finalize的行為)
(1) 首先,大致描述一下finalize流程:當(dāng)對象變成(GC Roots)不可達(dá)時(shí)榄笙,GC會判斷該對象是否覆蓋了finalize方法邪狞,若未覆蓋,則直接將其回收茅撞。否則帆卓,若對象未執(zhí)行過finalize方法,將其放入F-Queue隊(duì)列米丘,由一低優(yōu)先級線程執(zhí)行該隊(duì)列中對象的finalize方法剑令。執(zhí)行finalize方法完畢后,GC會再次判斷該對象是否可達(dá)拄查,若不可達(dá)吁津,則進(jìn)行回收,否則堕扶,對象“復(fù)活”碍脏。
(2) 具體的finalize流程:
對象可由兩種狀態(tài),涉及到兩類狀態(tài)空間稍算,一是終結(jié)狀態(tài)空間?F = {unfinalized, finalizable, finalized}典尾;二是可達(dá)狀態(tài)空間?R = {reachable, finalizer-reachable, unreachable}。各狀態(tài)含義如下:
unfinalized: 新建對象會先進(jìn)入此狀態(tài)糊探,GC并未準(zhǔn)備執(zhí)行其finalize方法钾埂,因?yàn)樵搶ο笫强蛇_(dá)的
finalizable: 表示GC可對該對象執(zhí)行finalize方法,GC已檢測到該對象不可達(dá)科平。正如前面所述褥紫,GC通過F-Queue隊(duì)列和一專用線程完成finalize的執(zhí)行
finalized: 表示GC已經(jīng)對該對象執(zhí)行過finalize方法
reachable: 表示GC Roots引用可達(dá)
finalizer-reachable(f-reachable):表示不是reachable,但可通過某個(gè)finalizable對象可達(dá)
unreachable:對象不可通過上面兩種途徑可達(dá)
狀態(tài)變遷圖:
變遷說明:
新建對象首先處于[reachable, unfinalized]狀態(tài)(A)
隨著程序的運(yùn)行瞪慧,一些引用關(guān)系會消失故源,導(dǎo)致狀態(tài)變遷,從reachable狀態(tài)變遷到f-reachable(B, C, D)或unreachable(E, F)狀態(tài)
若JVM檢測到處于unfinalized狀態(tài)的對象變成f-reachable或unreachable汞贸,JVM會將其標(biāo)記為finalizable狀態(tài)(G,H)绳军。若對象原處于[unreachable, unfinalized]狀態(tài),則同時(shí)將其標(biāo)記為f-reachable(H)矢腻。
在某個(gè)時(shí)刻门驾,JVM取出某個(gè)finalizable對象,將其標(biāo)記為finalized并在某個(gè)線程中執(zhí)行其finalize方法多柑。由于是在活動線程中引用了該對象奶是,該對象將變遷到(reachable, finalized)狀態(tài)(K或J)。該動作將影響某些其他對象從f-reachable狀態(tài)重新回到reachable狀態(tài)(L, M, N)
處于finalizable狀態(tài)的對象不能同時(shí)是unreahable的,由第4點(diǎn)可知聂沙,將對象finalizable對象標(biāo)記為finalized時(shí)會由某個(gè)線程執(zhí)行該對象的finalize方法秆麸,致使其變成reachable。這也是圖中只有八個(gè)狀態(tài)點(diǎn)的原因
程序員手動調(diào)用finalize方法并不會影響到上述內(nèi)部標(biāo)記的變化及汉,因此JVM只會至多調(diào)用finalize一次沮趣,即使該對象“復(fù)活”也是如此。程序員手動調(diào)用多少次不影響JVM的行為
若JVM檢測到finalized狀態(tài)的對象變成unreachable坷随,回收其內(nèi)存(I)
若對象并未覆蓋finalize方法房铭,JVM會進(jìn)行優(yōu)化,直接回收對象(O)
注:System.runFinalizersOnExit()等方法可以使對象即使處于reachable狀態(tài)温眉,JVM仍對其執(zhí)行finalize方法
[java]view plaincopy
publicclass?GC?{??
publicstatic?GC?SAVE_HOOK?=null;??
publicstaticvoid?main(String[]?args)throws?InterruptedException?{??
SAVE_HOOK?=new?GC();??
SAVE_HOOK?=null;??
????????System.gc();??
Thread.sleep(500);??
if?(null?!=?SAVE_HOOK)?{//此時(shí)對象應(yīng)該處于(reachable,?finalized)狀態(tài)??
System.out.println("Yes?,?I?am?still?alive");??
}else?{??
System.out.println("No?,?I?am?dead");??
????????}??
SAVE_HOOK?=null;??
????????System.gc();??
Thread.sleep(500);??
if?(null?!=?SAVE_HOOK)?{??
System.out.println("Yes?,?I?am?still?alive");??
}else?{??
System.out.println("No?,?I?am?dead");??
????????}??
????}??
@Override??
protectedvoid?finalize()throws?Throwable?{??
super.finalize();??
System.out.println("execute?method?finalize()");??
SAVE_HOOK?=this;??
????}??
}?
5.equals方法
該方法是非常重要的一個(gè)方法缸匪。一般equals和==是不一樣的,但是在Object中兩者是一樣的类溢。子類一般都要重寫這個(gè)方法凌蔬。
6.hashCode方法
該方法用于哈希查找,可以減少在查找中使用equals的次數(shù)闯冷,重寫了equals方法一般都要重寫hashCode方法龟梦。這個(gè)方法在一些具有哈希功能的Collection中用到。
一般必須滿足obj1.equals(obj2)==true窃躲。可以推出obj1.hash- Code()==obj2.hashCode()钦睡,但是hashCode相等不一定就滿足equals蒂窒。不過為了提高效率,應(yīng)該盡量使上面兩個(gè)條件接近等價(jià)荞怒。
如果不重寫hashcode(),在HashSet中添加兩個(gè)equals的對象洒琢,會將兩個(gè)對象都加入進(jìn)去。
7.wait方法
wait方法就是使當(dāng)前線程等待該對象的鎖褐桌,當(dāng)前線程必須是該對象的擁有者衰抑,也就是具有該對象的鎖。wait()方法一直等待荧嵌,直到獲得鎖或者被中斷呛踊。wait(long timeout)設(shè)定一個(gè)超時(shí)間隔,如果在規(guī)定時(shí)間內(nèi)沒有獲得鎖就返回啦撮。
調(diào)用該方法后當(dāng)前線程進(jìn)入睡眠狀態(tài)谭网,直到以下事件發(fā)生。
(1)其他線程調(diào)用了該對象的notify方法赃春。
(2)其他線程調(diào)用了該對象的notifyAll方法愉择。
(3)其他線程調(diào)用了interrupt中斷該線程。
(4)時(shí)間間隔到了。
此時(shí)該線程就可以被調(diào)度了锥涕,如果是被中斷的話就拋出一個(gè)InterruptedException異常衷戈。
wait方法是Object對象的方法。線程與鎖是分不開的层坠,線程的同步殖妇、等待、喚醒都與對象鎖是密不可分的窿春。wait方法會將當(dāng)前線程放入wait set拉一,等待被喚醒,并放棄lock對象上的所有同步聲明旧乞,當(dāng)前線程會因?yàn)榫€程調(diào)度的原因處于休眠狀態(tài)而不可用蔚润。只有通過以下四個(gè)方法可以主動喚醒:?
1. notify?
2. notifyAll?
3. Thread.interrupt()?
4. 等待時(shí)間過完。
當(dāng)線程被喚醒后尺栖,線程就從wait set中移除了并且重新獲得線程調(diào)度能力嫡纠,同時(shí)像其它線程一樣持有object的鎖。
一段synchronized的代碼被一個(gè)線程執(zhí)行之前延赌,他要先拿到執(zhí)行這段代碼的權(quán)限除盏,?
在Java里邊就是拿到某個(gè)同步對象的鎖(一個(gè)對象只有一把鎖);?
如果這個(gè)時(shí)候同步對象的鎖被其他線程拿走了挫以,他(這個(gè)線程)就只能等了(線程阻塞在鎖池等待隊(duì)列中)者蠕。?
取到鎖后,他就開始執(zhí)行同步代碼(被synchronized修飾的代碼)掐松;?
線程執(zhí)行完同步代碼后馬上就把鎖還給同步對象踱侣,其他在鎖池中等待的某個(gè)線程就可以拿到鎖執(zhí)行同步代碼了。?
這樣就保證了同步代碼在統(tǒng)一時(shí)刻只有一個(gè)線程在執(zhí)行大磺。
這里就需要補(bǔ)充一下對象鎖和類鎖的區(qū)別抡句。?
事實(shí)上,synchronized修飾非靜態(tài)方法杠愧、同步代碼塊的synchronized (this)用法和synchronized (非this對象)的用法鎖的是對象待榔,線程想要執(zhí)行對應(yīng)同步代碼,需要獲得對象鎖流济。
線程正常結(jié)束后锐锣,會使以這個(gè)線程對象運(yùn)行的wait()等待,退出等待狀態(tài)绳瘟!而如果在運(yùn)行wait()之前刺下,線程已經(jīng)結(jié)束了,則這個(gè)wait就沒有程序喚醒了稽荧。?
原碼里的join()方法橘茉,實(shí)際上就是運(yùn)行的 wait(). 需要運(yùn)行join的線程運(yùn)行join方法工腋,實(shí)際上是在此線程上調(diào)用了需要加入的線程對象的wait()方法,加入的線程運(yùn)行完后畅卓,自然從wait退出了擅腰。
到此,就得出了我的結(jié)論:
1 線程對象的wait()方法運(yùn)行后翁潘,可以不用其notify()方法退出趁冈,會在線程結(jié)束后,自動退出拜马。
2 線程間的等待喚醒機(jī)制渗勘,最好不要用線程對象做同步鎖!
首先我們看一個(gè)實(shí)例:
public class TestDemo{
public static void main(String [ ]args) throws Interrupted Exception{?
?? ? ? MyThread myThread =new MyThread();?
?? ? ? System.out.println("before");?
?? ? ? myThread.start();? ? ? ??
? ? ? ?System.out.println("after");? ?
?}
????static class MyThread extends Thread{public void run(){
? ? ? ? ? ? synchronized (this) {
? ? ? ? ? ? ? ? ? ?for(inti=0;i<3;i++){? ? ? ? ? ? ? ? ? ?
? ? ? ? ? ? ? ? ? ? ? ? System.out.println("number:"+ i);? ? ? ? ? ? ??
? }? ? ? ? ??
? }? ? ??
? }? ?
?}}
輸出的結(jié)果是:?
before?
after?
number:0?
number:1?
number:2
首先是Main線程具有搶占cpu資源俩莽,然后執(zhí)行完后旺坠,在開始執(zhí)行子線程。
實(shí)例2:
public class TestDemo{
?public static void main(String []args) throws Interrupted Exception{? ? ? ??
????????MyThread myThread =new MyThread();? ? ? ??
????????System.out.println("before");? ? ? ??
????????myThread.start();? ? ? ??
????????synchronized (myThread) {??
? ? ? ? myThread.wait();? ? ? ?
??}? ? ? ?
?System.out.println("after");? ?
?}
?static class MyThread extends Thread{
????public void run(){? ? ? ? ??
????? synchronized (this) {
????????for(int i=0;i<3;i++){
? ? ? ? ? ? ?System.out.println("number:"+ i);? ? ? ? ? ? ? ?
?}? ? ? ? ? ?
?}? ? ? ??
}??
? }}
before?
number:0?
number:1?
number:2?
after
我們調(diào)用wait方法扮超,Main線程會釋放當(dāng)前鎖取刃,進(jìn)入wait set。然后子線程開始運(yùn)行出刷,當(dāng)子線程運(yùn)行完畢后璧疗,會把鎖歸還。
8.notify方法
該方法喚醒在該對象上等待的某個(gè)線程馁龟。
對于wait()和notify()的理解崩侠,還是要從jdk官方文檔中開始,在Object類方法中有:
void notify()?
Wakes up a single thread that is waiting on this object’s monitor.?
譯:喚醒在此對象監(jiān)視器上等待的單個(gè)線程
void notifyAll()?
Wakes up all threads that are waiting on this object’s monitor.?
譯:喚醒在此對象監(jiān)視器上等待的所有線程
void wait( )?
Causes the current thread to wait until another thread invokes the notify() method or the notifyAll( ) method for this object.?
譯:導(dǎo)致當(dāng)前的線程等待坷檩,直到其他線程調(diào)用此對象的notify( ) 方法或 notifyAll( ) 方法
void wait(long timeout)?
Causes the current thread to wait until either another thread invokes the notify( ) method or the notifyAll( ) method for this object, or a specified amount of time has elapsed.?
譯:導(dǎo)致當(dāng)前的線程等待却音,直到其他線程調(diào)用此對象的notify() 方法或 notifyAll() 方法,或者指定的時(shí)間過完淌喻。
void wait(long timeout, int nanos)?
Causes the current thread to wait until another thread invokes the notify( ) method or the notifyAll( ) method for this object, or some other thread interrupts the current thread, or a certain amount of real time has elapsed.?
譯:導(dǎo)致當(dāng)前的線程等待,直到其他線程調(diào)用此對象的notify( ) 方法或 notifyAll( ) 方法雀摘,或者其他線程打斷了當(dāng)前線程裸删,或者指定的時(shí)間過完。
上面是官方文檔的簡介阵赠,下面我們根據(jù)官方文檔總結(jié)一下:
wait( )涯塔,notify( ),notifyAll( )都不屬于Thread類清蚀,而是屬于Object基礎(chǔ)類匕荸,也就是每個(gè)對象都有wait( ),notify( )枷邪,notifyAll( ) 的功能榛搔,因?yàn)槊總€(gè)對象都有鎖,鎖是每個(gè)對象的基礎(chǔ),當(dāng)然操作鎖的方法也是最基礎(chǔ)了践惑。
當(dāng)需要調(diào)用以上的方法的時(shí)候腹泌,一定要對競爭資源進(jìn)行加鎖,如果不加鎖的話尔觉,則會報(bào) IllegalMonitorStateException 異常
當(dāng)想要調(diào)用wait( )進(jìn)行線程等待時(shí)凉袱,必須要取得這個(gè)鎖對象的控制權(quán)(對象監(jiān)視器),一般是放到synchronized(obj)代碼中侦铜。
在while循環(huán)里而不是if語句下使用wait专甩,這樣,會在線程暫投ど裕恢復(fù)后都檢查wait的條件涤躲,并在條件實(shí)際上并未改變的情況下處理喚醒通知
調(diào)用obj.wait( )釋放了obj的鎖,否則其他線程也無法獲得obj的鎖嫁盲,也就無法在synchronized(obj){ obj.notify() } 代碼段內(nèi)喚醒A篓叶。
notify( )方法只會通知等待隊(duì)列中的第一個(gè)相關(guān)線程(不會通知優(yōu)先級比較高的線程)
notifyAll( )通知所有等待該競爭資源的線程(也不會按照線程的優(yōu)先級來執(zhí)行)
假設(shè)有三個(gè)線程執(zhí)行了obj.wait( ),那么obj.notifyAll( )則能全部喚醒tread1羞秤,thread2缸托,thread3,但是要繼續(xù)執(zhí)行obj.wait()的下一條語句瘾蛋,必須獲得obj鎖俐镐,因此,tread1哺哼,thread2佩抹,thread3只有一個(gè)有機(jī)會獲得鎖繼續(xù)執(zhí)行,例如tread1取董,其余的需要等待thread1釋放obj鎖之后才能繼續(xù)執(zhí)行棍苹。
當(dāng)調(diào)用obj.notify/notifyAll后,調(diào)用線程依舊持有obj鎖茵汰,因此枢里,thread1,thread2蹂午,thread3雖被喚醒栏豺,但是仍無法獲得obj鎖。直到調(diào)用線程退出synchronized塊豆胸,釋放obj鎖后奥洼,thread1,thread2晚胡,thread3中的一個(gè)才有機(jī)會獲得鎖繼續(xù)執(zhí)行灵奖。
2.wait和notify簡單使用示例
public class WaitNotifyTest{
?// 在多線程間共享的對象上使用wait private String[ ]? shareObj = { "true" };
? ? public static void main(String[] args) {
? ? ? ? WaitNotifyTest test = new WaitNotifyTest();
? ? ? ? ThreadWait threadWait1 = test.new ThreadWait("wait thread1");
? ? ? ? threadWait1.setPriority(2);
? ? ? ? ThreadWait threadWait2 = test.new ThreadWait("wait thread2");
? ? ? ? threadWait2.setPriority(3);
? ? ? ? ThreadWait threadWait3 = test.new ThreadWait("wait thread3");
? ? ? ? threadWait3.setPriority(4);
? ? ? ? ThreadNotify threadNotify = test.new ThreadNotify("notify thread");
? ? ? ? threadNotify.start();
? ? ? ? threadWait1.start();
? ? ? ? threadWait2.start();
? ? ? ? threadWait3.start();
? ? }
? ? class ThreadWait extends Thread {
? ? ? ? public ThreadWait(String name){
? ? ? ? ? ? super(name);
? ? ? ? }
? ? ? ? public void run() {
? ? ? ? ? ? synchronized (shareObj) {
? ? ? ? ? ? ? ? while ("true".equals(shareObj[0])) {
? ? ? ? ? ? ? ? ? ? System.out.println("線程"+ this.getName() + "開始等待");
? ? ? ? ? ? ? ? ? ? long startTime = System.currentTimeMillis();
? ? ? ? ? ? ? ? ? ? try {
? ? ? ? ? ? ? ? ? ? ? ? shareObj.wait();
? ? ? ? ? ? ? ? ? ? } catch (InterruptedException e) {
? ? ? ? ? ? ? ? ? ? ? ? e.printStackTrace();
? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? ? ? long endTime = System.currentTimeMillis();
? ? ? ? ? ? ? ? ? ? System.out.println("線程" + this.getName()
? ? ? ? ? ? ? ? ? ? ? ? ? ? + "等待時(shí)間為:" + (endTime - startTime));
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? ? ? System.out.println("線程" + getName() + "等待結(jié)束");
? ? ? ? }
? ? }
? ? class ThreadNotify extends Thread {
? ? ? ? public ThreadNotify(String name){
? ? ? ? ? ? super(name);
? ? ? ? }
? ? ? ? public void run() {
? ? ? ? ? ? try {
? ? ? ? ? ? ? ? // 給等待線程等待時(shí)間? ? ? ? ? ? ? ? sleep(3000);
? ? ? ? ? ? } catch (InterruptedException e) {
? ? ? ? ? ? ? ? e.printStackTrace();
? ? ? ? ? ? }
? ? ? ? ? ? synchronized (shareObj) {
? ? ? ? ? ? ? ? System.out.println("線程" + this.getName() + "開始準(zhǔn)備通知");
? ? ? ? ? ? ? ? shareObj[0] = "false";
? ? ? ? ? ? ? ? shareObj.notifyAll();
? ? ? ? ? ? ? ? System.out.println("線程" + this.getName() + "通知結(jié)束");
? ? ? ? ? ? }
? ? ? ? ? ? System.out.println("線程" + this.getName() + "運(yùn)行結(jié)束");
? ? ? ? }
? ? }
}
運(yùn)行結(jié)果:
線程wait thread1開始等待
線程wait thread3開始等待
線程wait thread2開始等待
線程notify thread開始準(zhǔn)備通知
線程notify thread通知結(jié)束
線程notify thread運(yùn)行結(jié)束
線程wait thread2等待時(shí)間為:2998線程wait thread2等待結(jié)束
線程wait thread3等待時(shí)間為:2998線程wait thread3等待結(jié)束
線程wait thread1等待時(shí)間為:3000線程wait thread1等待結(jié)束
9.notifyAll方法
該方法喚醒在該對象上等待的所有線程
在Java語言中notifyAll()方法的實(shí)際作用如下:
1.notifyAll():?Wakes up all threads that are waiting on this object's monitor嚼沿;
2.當(dāng)一個(gè)線程使用的同步方法中用到某個(gè)變量,而此變量又需要其它線程修改后才能符合本線程的需要,則可以在同步方法中調(diào)用wait()方法,使本線程等待,并允許其它線程調(diào)用這個(gè)同步方法
3.其它線程在使用這個(gè)同步方法不需要等待,當(dāng)它使用完這個(gè)同步方法時(shí),用notifyAll()通知所有由于使用這個(gè)同步方法而處于等待的線程結(jié)束,再次使用這個(gè)同步方法
4.如果使第一個(gè)處于等待的線程結(jié)束等待,則調(diào)用方法notify()
Java是一門面向?qū)ο缶幊陶Z言,不僅吸收了C++語言的各種優(yōu)點(diǎn)桑寨,還摒棄了C++里難以理解的多繼承伏尼、指針等概念,因此Java語言具有功能強(qiáng)大和簡單易用兩個(gè)特征尉尾。Java語言作為靜態(tài)面向?qū)ο缶幊陶Z言的代表爆阶,極好地實(shí)現(xiàn)了面向?qū)ο罄碚摚试S程序員以優(yōu)雅的思維方式進(jìn)行復(fù)雜的編程沙咏。
Java具有簡單性辨图、面向?qū)ο蟆⒎植际街辍⒔研怨屎印踩浴⑵脚_獨(dú)立與可移植性吆豹、多線程鱼的、動態(tài)性等特點(diǎn) ?。Java可以編寫桌面應(yīng)用程序痘煤、Web應(yīng)用程序凑阶、分布式系統(tǒng)和嵌入式系統(tǒng)應(yīng)用程序等。
?在Java多線程中衷快,notify() 與 notifyAll() 是比較重要的方法宙橱,通常與wait方法配合使用,它們只能在同步域里面調(diào)用否則會出現(xiàn)異常IllegalMonitorStateException
下面說說他們的功能:
notify :作用是喚醒指定某個(gè)線程蘸拔,在一般實(shí)際使用情況下此方法用的不多师郑,因?yàn)橐话悴恢粫嬖?、2個(gè)線程调窍,當(dāng)線程多的時(shí)候使用此方法維護(hù)起來就很麻煩宝冕,很容易造成死鎖。
notifyAll : 作用是喚醒指定鎖上等待執(zhí)行的所有線程邓萨,打個(gè)比方地梨,此時(shí)有線程 T1 線程 T2 線程 T3 現(xiàn)在假設(shè)T2與T3線程先執(zhí)行并且是處于wait等待狀態(tài),他們要等T1去喚醒他們先誉,由于他們兩都是處于wait狀態(tài)湿刽,除非有其他線程喚醒它們否則他們會一直處于等待狀態(tài)的烁,如果此時(shí)T1已經(jīng)執(zhí)行并且已經(jīng)調(diào)用notifyAll方法褐耳,就會喚醒T2與T3,可能這時(shí)會有疑問線程T1是怎么能夠喚醒其他兩個(gè)線程的呢渴庆?首先會有個(gè)前提條件 T1铃芦、T2雅镊、T3線程都是處于同一個(gè)鎖域,因?yàn)槿绻皇翘幱谕粋€(gè)鎖域調(diào)用notifyAll或者是notify會報(bào)錯(cuò)的刃滓,代碼例如這樣:
T1:
// 之所以加個(gè)while 以及flag標(biāo)示是因?yàn)榭赡艽嬖赥2執(zhí)行完就執(zhí)行T1的情況或者是先執(zhí)行了T1,這樣就會造成死鎖,因?yàn)門1一旦先執(zhí)行那就沒有線程把T2仁烹、T3喚醒了,T2 T3則一直等待有個(gè)線程喚醒他咧虎,而此時(shí)已沒有線程能喚醒他們了卓缰。
while(true){ ??
if(flag){
synchronized(lock){
println("執(zhí)行了T1");
notifyAll();
flag = false;
? ? ? ? ? }
}
}
// 還能這樣,將T2砰诵,T3線程傳入T1中征唬,調(diào)用它們的join方法等待他們執(zhí)行完才執(zhí)行notifyAll(),相比前一個(gè)代碼這個(gè)代碼更直接些茁彭,但是存在多個(gè)不同線程時(shí)會很麻煩总寒,因?yàn)橐阉麄冏鳛閰⑷雮魅牖蛘哒{(diào)用。
T1:
T2 t2;
T3 t3;
synchronized(lock){
t2.join();
t3.join();
println("執(zhí)行了T1");
notifyAll();
}
T2:
synchronized(lock){
flag?=?true;
wait();
println("執(zhí)行了T2");
}
T3:
synchronized(lock){
flag?=?true;
wait();
println("執(zhí)行了T3");
}
注意:當(dāng)某個(gè)線程調(diào)用wait()方法之后會自動釋放當(dāng)前線程占用的鎖理肺。
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 注:本文收集于各大網(wǎng)絡(luò),一切只為了學(xué)習(xí)!謝謝各位大神.