Object類位于java.lang包中,java.lang包有最基礎(chǔ)的和核心的類,在編譯時(shí)會(huì)自動(dòng)導(dǎo)入;
Object類是所有java類的祖先,每個(gè)類都使用Object作為超類,所有對(duì)象(數(shù)組)都實(shí)現(xiàn)這個(gè)類的方法.可以使用類型為Objcet的變量指向任意類型的對(duì)象
1.clone方法
保護(hù)方法,實(shí)現(xiàn)對(duì)象的淺復(fù)制,只有實(shí)現(xiàn)了Cloneable接口才可以調(diào)用該方法,否則拋出CloneNotSupportedException異常。
主要是JAVA里除了8種基本類型傳參數(shù)是值傳遞,其他的類對(duì)象傳參數(shù)都是引用傳遞,我們有時(shí)候不希望在方法里講參數(shù)改變,這是就需要在類中復(fù)寫clone方法。
直接調(diào)用Object的clone()方法來(lái)copy一個(gè)副本,但是發(fā)現(xiàn)myEclipse的提示中并沒(méi)有該方法,以為在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
后來(lái)看了看源碼,發(fā)現(xiàn)原來(lái)是這樣:
Object源碼中對(duì)于clone()方法的描述:
[java]?view plain?copy
protected?native?Object?clone()?throws?CloneNotSupportedException;??
native修飾詞解析:native的方法就是一個(gè)java調(diào)用非java代碼的接口,是一個(gè)其他語(yǔ)言實(shí)現(xiàn)的方法。
問(wèn)題1:Object類是所有?類的父類,那么為什么子類不能訪問(wèn)父類的protected修飾的方法呢?
以前對(duì)protected的理解是錯(cuò)誤的,protected的方法在自己的包中與public相同
在不同包,指的是通過(guò)自身實(shí)例(自身的引用)訪問(wèn),而不能通過(guò)父類實(shí)例(引用)訪問(wèn)
所以上端代碼中調(diào)用Student類的clone()方法會(huì)包編譯錯(cuò)誤,下面的代碼(通過(guò)自身引用調(diào))就OK的
[java]?view plain?copy
public?class?TestObject?{??
public?static?void?main(String[]?args)?{??
TestBoject?t?=new?TestObject();??
t.clone();//ok??
????}??
}??
問(wèn)題2:要想實(shí)現(xiàn)類似文章開(kāi)頭的代碼功能該怎么辦?
在Object的源碼中注釋到:
首先,如果這個(gè)類沒(méi)有實(shí)現(xiàn)Cloneable這個(gè)接口,將會(huì)拋出CloneNotSupportedException,但是所有的數(shù)組都被看成實(shí)現(xiàn)了這個(gè)接口。此方法執(zhí)行的是該對(duì)象的“淺表復(fù)制”,而不“深層復(fù)制”操作。
Object 類本身不實(shí)現(xiàn)接口 Cloneable,所以在類為 Object 的對(duì)象上調(diào)用 clone 方法將會(huì)導(dǎo)致在運(yùn)行時(shí)拋出異常。
在Cloneable的源碼中注釋到:
此類實(shí)現(xiàn)了 Cloneable 接口,以指示 Object.clone() 方法可以合法地對(duì)該類實(shí)例進(jìn)行按字段復(fù)制。 如果在沒(méi)有實(shí)現(xiàn) Cloneable 接口的實(shí)例上調(diào)用 Object 的 clone 方法,則會(huì)導(dǎo)致拋出 CloneNotSupportedException異常。?
按照慣例,實(shí)現(xiàn)此接口的類應(yīng)該使用公共方法重寫 Object.clone(它是受保護(hù)的)。請(qǐng)參閱 Object.clone(),以獲得有關(guān)重寫此方法的詳細(xì)信息。?
注意,此接口不包含 clone 方法。因此,因?yàn)槟硞€(gè)對(duì)象實(shí)現(xiàn)了此接口就克隆它是不可能的。即使 clone 方法是反射性調(diào)用的,也無(wú)法保證它將獲得成功
解讀:Cloneable接口沒(méi)有任何方法,僅是個(gè)標(biāo)志接口(tagging interface),若要具有克隆能力,實(shí)現(xiàn)Cloneable接口的類必須重寫從Object繼承來(lái)的clone方法,并調(diào)用Object的clone方法(見(jiàn)下面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()
功能:返回該對(duì)象的運(yùn)行時(shí)類的java.lang.Class對(duì)象(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對(duì)象時(shí)(作函數(shù)參數(shù)的時(shí)候),你不能調(diào)用getClass方法,那你只能用類名.class來(lái)達(dá)到效果
例子:
[java]view plaincopy
System.out.println(JButton.class);??
輸出:
class javax.swing.JButton
getName方法:
類型:public String getName()
功能:以String形式返回次Class對(duì)象所表示的實(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工具 可以按"."然后馬上提示很多方法 供你選擇
那他如何知道"."了以后有哪些方法?
他用的語(yǔ)法就是getClass().getMethods();
二、
.class其實(shí)是在java運(yùn)行時(shí)就加載進(jìn)去的
getClass()是運(yùn)行程序時(shí)動(dòng)態(tài)加載的
下面以例子說(shuō)明:
首先建一個(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對(duì)象的方法 Java反射機(jī)制
下面以一個(gè)具體的實(shí)例來(lái)說(shuō)明。此實(shí)例來(lái)自《精通Hibernate 3.0 Java數(shù)據(jù)庫(kù)持久層開(kāi)發(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("通過(guò)類本身獲得對(duì)象");
Class userClass =this.getClass();
System.out.println(userClass.getName());
System.out.println("===========");
System.out.println("通過(guò)子類的實(shí)例獲得父類對(duì)象");
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("通過(guò)類名.class獲取對(duì)象");
Class forClass =com.hqh.reflect.UseInfo.class;
System.out.println(forClass.getName());
System.out.println("===========");
System.out.println("通過(guò)類名的字符串獲取對(duì)象");
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é)果:
通過(guò)類本身獲得對(duì)象
com.hqh.reflect.GetClassTest
===========
通過(guò)子類的實(shí)例獲得父類對(duì)象
com.hqh.reflect.UseInfo
java.lang.Object
===========
通過(guò)類名.class獲取對(duì)象
com.hqh.reflect.UseInfo
===========
通過(guò)類名的字符串獲取對(duì)象
com.hqh.reflect.UseInfo
=============
3.toString方法
返回該對(duì)象的字符串表示。通常,toString方法會(huì)返回一個(gè)“以文本方式表示”此對(duì)象的字符串。結(jié)果應(yīng)是一個(gè)簡(jiǎn)明但易于讀懂的信息表達(dá)式。建議所有子類都重寫此方法。
Object類的toString方法返回一個(gè)字符串,該字符串由類名(對(duì)象是該類的一個(gè)實(shí)例)、at 標(biāo)記符“@”和此對(duì)象哈希碼的無(wú)符號(hào)十六進(jìn)制表示組成。換句話說(shuō),該方法返回一個(gè)字符串,它的值等于:
getClass().getName() + '@' + Integer.toHexString(hashCode())
返回:
該對(duì)象的字符串表示形式。
因?yàn)樗荗bject里面已經(jīng)有了的方法,而所有類都是繼承Object,所以“所有對(duì)象都有這個(gè)方法”。
它通常只是為了方便輸出,比如System.out.println(xx),括號(hào)里面的“xx”如果不是String類型的話,就自動(dòng)調(diào)用xx的toString()方法
總而言之,它只是sun公司開(kāi)發(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);
會(huì)得到輸出: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());? ? ?
}
}
會(huì)得到輸出:xxxx@xxxxxxx的類名加地址形式
System.out.println(obj.getString());
會(huì)得到輸出:this is A

看出區(qū)別了嗎,toString的好處是在碰到“println”之類的輸出方法時(shí)會(huì)自動(dòng)調(diào)用,不用顯式打出來(lái)。
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在屏幕上顯示出來(lái),?則必須首先調(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è)類都會(huì)繼承該方法。它返回對(duì)象的一個(gè)String表示,并且對(duì)于調(diào)試非常有幫助。然而對(duì)于默認(rèn)的toString()方法往往不能滿足需求,需要覆蓋這個(gè)方法。
toString()方法將對(duì)象轉(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è)工具類,用于在控制臺(tái)輸出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類對(duì)象并輸出結(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,然后通過(guò)其他繼承即可。
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();
????}
}
測(cè)試類
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語(yǔ)言
通過(guò)使用一些輔助性工具來(lái)找到程序中的瓶頸,然后就可以對(duì)瓶頸部分的代碼進(jìn)行優(yōu)化。一般有兩種方案:即優(yōu)化代碼或更改設(shè)計(jì)方法。我們一般會(huì)選擇后者,因?yàn)椴蝗フ{(diào)用以下代碼要比調(diào)用一些優(yōu)化的代碼更能提高程序的性能。而一個(gè)設(shè)計(jì)良好的程序能夠精簡(jiǎn)代碼,從而提高性能。
下面將提供一些在JAVA程序的設(shè)計(jì)和編碼中,為了能夠提高JAVA程序的性能,而經(jīng)常采用的一些方法和技巧。
1.對(duì)象的生成和大小的調(diào)整。
JAVA程序設(shè)計(jì)中一個(gè)普遍的問(wèn)題就是沒(méi)有好好的利用JAVA語(yǔ)言本身提供的函數(shù),從而常常會(huì)生成大量的對(duì)象(或?qū)嵗?。由于系統(tǒng)不僅要花時(shí)間生成對(duì)象,以后可能還需花時(shí)間對(duì)這些對(duì)象進(jìn)行垃圾回收和處理。因此,生成過(guò)多的對(duì)象將會(huì)給程序的性能帶來(lái)很大的影響。
例1:關(guān)于String ,StringBuffer,+和append
JAVA語(yǔ)言提供了對(duì)于String類型變量的操作。但如果使用不當(dāng),會(huì)給程序的性能帶來(lái)影響。如下面的語(yǔ)句:
String name=new String("HuangWeiFeng");
System.out.println(name+"is my name");
看似已經(jīng)很精簡(jiǎn)了,其實(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開(kāi)始);
(6)?從java.io.PrintStream類中得到靜態(tài)的out變量;
(7)?生成新的字符串緩沖變量new StringBuffer(STR_BUF_1);
(8)?復(fù)制該字符串緩沖變量;
(9)?調(diào)用字符串緩沖的構(gòu)架器(Constructor);
(10)?保存該字符串緩沖到數(shù)組中(從位置1開(kāi)始);
(11)?以STR_1為參數(shù),調(diào)用字符串緩沖(StringBuffer)類中的append方法;
(12)?加載字符串常量"is my name"(STR_3);
(13)?以STR_3為參數(shù),調(diào)用字符串緩沖(StringBuffer)類中的append方法;
(14)?對(duì)于STR_BUF_1執(zhí)行toString命令;
(15)?調(diào)用out變量中的println方法,輸出結(jié)果。
由此可以看出,這兩行簡(jiǎn)單的代碼,就生成了STR_1,STR_2,STR_3,STR_4和STR_BUF_1五個(gè)對(duì)象變量。這些生成的類的實(shí)例一般都存放在堆中。堆要對(duì)所有類的超類,類的實(shí)例進(jìn)行初始化,同時(shí)還要調(diào)用類極其每個(gè)超類的構(gòu)架器。而這些操作都是非常消耗系統(tǒng)資源的。因此,對(duì)對(duì)象的生成進(jìn)行限制,是完全有必要的。
經(jīng)修改,上面的代碼可以用如下的代碼來(lái)替換。
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開(kāi)始);
(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)?對(duì)于STR_BUF_1執(zhí)行toString命令(STR_3);
(11)調(diào)用out變量中的println方法,輸出結(jié)果。
由此可以看出,經(jīng)過(guò)改進(jìn)后的代碼只生成了四個(gè)對(duì)象變量:STR_1,STR_2,STR_3和STR_BUF_1.你可能覺(jué)得少生成一個(gè)對(duì)象不會(huì)對(duì)程序的性能有很大的提高。但下面的代碼段2的執(zhí)行速度將是代碼段1的2倍。因?yàn)榇a段1生成了八個(gè)對(duì)象,而代碼段2只生成了四個(gè)對(duì)象。
代碼段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提供的庫(kù)函數(shù)來(lái)優(yōu)化程序,對(duì)提高JAVA程序的性能時(shí)非常重要的.其注意點(diǎn)主要有如下幾方面;?
4.finalize方法
該方法用于釋放資源。因?yàn)闊o(wú)法確定該方法什么時(shí)候被調(diào)用,很少使用。
1. finalize的作用
finalize()是Object的protected方法,子類可以覆蓋該方法以實(shí)現(xiàn)資源清理工作,GC在回收對(duì)象之前調(diào)用該方法。
finalize()與C++中的析構(gòu)函數(shù)不是對(duì)應(yīng)的。C++中的析構(gòu)函數(shù)調(diào)用的時(shí)機(jī)是確定的(對(duì)象離開(kāi)作用域或delete掉),但Java中的finalize的調(diào)用具有不確定性
不建議用finalize方法完成“非內(nèi)存資源”的清理工作,但建議用于:① 清理本地對(duì)象(通過(guò)JNI創(chuàng)建的對(duì)象);② 作為確保某些非內(nèi)存資源(如Socket、文件等)釋放的一個(gè)補(bǔ)充:在finalize方法中顯式調(diào)用其他資源釋放方法。其原因可見(jiàn)下文[finalize的問(wèn)題]
一些與finalize相關(guān)的方法,由于一些致命的缺陷,已經(jīng)被廢棄了,如System.runFinalizersOnExit()方法、Runtime.runFinalizersOnExit()方法
System.gc()與System.runFinalization()方法增加了finalize方法執(zhí)行的機(jī)會(huì),但不可盲目依賴它們
Java語(yǔ)言規(guī)范并不保證finalize方法會(huì)被及時(shí)地執(zhí)行、而且根本不會(huì)保證它們會(huì)被執(zhí)行
finalize方法可能會(huì)帶來(lái)性能問(wèn)題。因?yàn)镴VM通常在單獨(dú)的低優(yōu)先級(jí)線程中完成finalize的執(zhí)行
對(duì)象再生問(wèn)題:finalize方法中,可將待回收對(duì)象賦值給GC Roots可達(dá)的對(duì)象引用,從而達(dá)到對(duì)象再生的目的
finalize方法至多由GC執(zhí)行一次(用戶當(dāng)然可以手動(dòng)調(diào)用對(duì)象的finalize方法,但并不影響GC對(duì)finalize的行為)
3. finalize的執(zhí)行過(guò)程(生命周期)
(1) 首先,大致描述一下finalize流程:當(dāng)對(duì)象變成(GC Roots)不可達(dá)時(shí),GC會(huì)判斷該對(duì)象是否覆蓋了finalize方法,若未覆蓋,則直接將其回收。否則,若對(duì)象未執(zhí)行過(guò)finalize方法,將其放入F-Queue隊(duì)列,由一低優(yōu)先級(jí)線程執(zhí)行該隊(duì)列中對(duì)象的finalize方法。執(zhí)行finalize方法完畢后,GC會(huì)再次判斷該對(duì)象是否可達(dá),若不可達(dá),則進(jìn)行回收,否則,對(duì)象“復(fù)活”。
(2) 具體的finalize流程:
對(duì)象可由兩種狀態(tài),涉及到兩類狀態(tài)空間,一是終結(jié)狀態(tài)空間?F = {unfinalized, finalizable, finalized};二是可達(dá)狀態(tài)空間?R = {reachable, finalizer-reachable, unreachable}。各狀態(tài)含義如下:
unfinalized: 新建對(duì)象會(huì)先進(jìn)入此狀態(tài),GC并未準(zhǔn)備執(zhí)行其finalize方法,因?yàn)樵搶?duì)象是可達(dá)的
finalizable: 表示GC可對(duì)該對(duì)象執(zhí)行finalize方法,GC已檢測(cè)到該對(duì)象不可達(dá)。正如前面所述,GC通過(guò)F-Queue隊(duì)列和一專用線程完成finalize的執(zhí)行
finalized: 表示GC已經(jīng)對(duì)該對(duì)象執(zhí)行過(guò)finalize方法
reachable: 表示GC Roots引用可達(dá)
finalizer-reachable(f-reachable):表示不是reachable,但可通過(guò)某個(gè)finalizable對(duì)象可達(dá)
unreachable:對(duì)象不可通過(guò)上面兩種途徑可達(dá)
狀態(tài)變遷圖:
變遷說(shuō)明:
新建對(duì)象首先處于[reachable, unfinalized]狀態(tài)(A)
隨著程序的運(yùn)行,一些引用關(guān)系會(huì)消失,導(dǎo)致?tīng)顟B(tài)變遷,從reachable狀態(tài)變遷到f-reachable(B, C, D)或unreachable(E, F)狀態(tài)
若JVM檢測(cè)到處于unfinalized狀態(tài)的對(duì)象變成f-reachable或unreachable,JVM會(huì)將其標(biāo)記為finalizable狀態(tài)(G,H)。若對(duì)象原處于[unreachable, unfinalized]狀態(tài),則同時(shí)將其標(biāo)記為f-reachable(H)。
在某個(gè)時(shí)刻,JVM取出某個(gè)finalizable對(duì)象,將其標(biāo)記為finalized并在某個(gè)線程中執(zhí)行其finalize方法。由于是在活動(dòng)線程中引用了該對(duì)象,該對(duì)象將變遷到(reachable, finalized)狀態(tài)(K或J)。該動(dòng)作將影響某些其他對(duì)象從f-reachable狀態(tài)重新回到reachable狀態(tài)(L, M, N)
處于finalizable狀態(tài)的對(duì)象不能同時(shí)是unreahable的,由第4點(diǎn)可知,將對(duì)象finalizable對(duì)象標(biāo)記為finalized時(shí)會(huì)由某個(gè)線程執(zhí)行該對(duì)象的finalize方法,致使其變成reachable。這也是圖中只有八個(gè)狀態(tài)點(diǎn)的原因
程序員手動(dòng)調(diào)用finalize方法并不會(huì)影響到上述內(nèi)部標(biāo)記的變化,因此JVM只會(huì)至多調(diào)用finalize一次,即使該對(duì)象“復(fù)活”也是如此。程序員手動(dòng)調(diào)用多少次不影響JVM的行為
若JVM檢測(cè)到finalized狀態(tài)的對(duì)象變成unreachable,回收其內(nèi)存(I)
若對(duì)象并未覆蓋finalize方法,JVM會(huì)進(jìn)行優(yōu)化,直接回收對(duì)象(O)
注:System.runFinalizersOnExit()等方法可以使對(duì)象即使處于reachable狀態(tài),JVM仍對(duì)其執(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í)對(duì)象應(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??梢酝瞥鰋bj1.hash- Code()==obj2.hashCode(),但是hashCode相等不一定就滿足equals。不過(guò)為了提高效率,應(yīng)該盡量使上面兩個(gè)條件接近等價(jià)。
如果不重寫hashcode(),在HashSet中添加兩個(gè)equals的對(duì)象,會(huì)將兩個(gè)對(duì)象都加入進(jìn)去。
7.wait方法
wait方法就是使當(dāng)前線程等待該對(duì)象的鎖,當(dāng)前線程必須是該對(duì)象的擁有者,也就是具有該對(duì)象的鎖。wait()方法一直等待,直到獲得鎖或者被中斷。wait(long timeout)設(shè)定一個(gè)超時(shí)間隔,如果在規(guī)定時(shí)間內(nèi)沒(méi)有獲得鎖就返回。
調(diào)用該方法后當(dāng)前線程進(jìn)入睡眠狀態(tài),直到以下事件發(fā)生。
(1)其他線程調(diào)用了該對(duì)象的notify方法。
(2)其他線程調(diào)用了該對(duì)象的notifyAll方法。
(3)其他線程調(diào)用了interrupt中斷該線程。
(4)時(shí)間間隔到了。
此時(shí)該線程就可以被調(diào)度了,如果是被中斷的話就拋出一個(gè)InterruptedException異常。
wait方法是Object對(duì)象的方法。線程與鎖是分不開(kāi)的,線程的同步、等待、喚醒都與對(duì)象鎖是密不可分的。wait方法會(huì)將當(dāng)前線程放入wait set,等待被喚醒,并放棄lock對(duì)象上的所有同步聲明,當(dāng)前線程會(huì)因?yàn)榫€程調(diào)度的原因處于休眠狀態(tài)而不可用。只有通過(guò)以下四個(gè)方法可以主動(dòng)喚醒:?
1. notify?
2. notifyAll?
3. Thread.interrupt()?
4. 等待時(shí)間過(guò)完。
當(dāng)線程被喚醒后,線程就從wait set中移除了并且重新獲得線程調(diào)度能力,同時(shí)像其它線程一樣持有object的鎖。
一段synchronized的代碼被一個(gè)線程執(zhí)行之前,他要先拿到執(zhí)行這段代碼的權(quán)限,?
在Java里邊就是拿到某個(gè)同步對(duì)象的鎖(一個(gè)對(duì)象只有一把鎖);?
如果這個(gè)時(shí)候同步對(duì)象的鎖被其他線程拿走了,他(這個(gè)線程)就只能等了(線程阻塞在鎖池等待隊(duì)列中)。?
取到鎖后,他就開(kāi)始執(zhí)行同步代碼(被synchronized修飾的代碼);?
線程執(zhí)行完同步代碼后馬上就把鎖還給同步對(duì)象,其他在鎖池中等待的某個(gè)線程就可以拿到鎖執(zhí)行同步代碼了。?
這樣就保證了同步代碼在統(tǒng)一時(shí)刻只有一個(gè)線程在執(zhí)行。
這里就需要補(bǔ)充一下對(duì)象鎖和類鎖的區(qū)別。?
事實(shí)上,synchronized修飾非靜態(tài)方法、同步代碼塊的synchronized (this)用法和synchronized (非this對(duì)象)的用法鎖的是對(duì)象,線程想要執(zhí)行對(duì)應(yīng)同步代碼,需要獲得對(duì)象鎖。
線程正常結(jié)束后,會(huì)使以這個(gè)線程對(duì)象運(yùn)行的wait()等待,退出等待狀態(tài)!而如果在運(yùn)行wait()之前,線程已經(jīng)結(jié)束了,則這個(gè)wait就沒(méi)有程序喚醒了。?
原碼里的join()方法,實(shí)際上就是運(yùn)行的 wait(). 需要運(yùn)行join的線程運(yùn)行join方法,實(shí)際上是在此線程上調(diào)用了需要加入的線程對(duì)象的wait()方法,加入的線程運(yùn)行完后,自然從wait退出了。
到此,就得出了我的結(jié)論:
1 線程對(duì)象的wait()方法運(yùn)行后,可以不用其notify()方法退出,會(huì)在線程結(jié)束后,自動(dòng)退出。
2 線程間的等待喚醒機(jī)制,最好不要用線程對(duì)象做同步鎖!
首先我們看一個(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í)行完后,在開(kāi)始執(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線程會(huì)釋放當(dāng)前鎖,進(jìn)入wait set。然后子線程開(kāi)始運(yùn)行,當(dāng)子線程運(yùn)行完畢后,會(huì)把鎖歸還。
8.notify方法
該方法喚醒在該對(duì)象上等待的某個(gè)線程。
對(duì)于wait()和notify()的理解,還是要從jdk官方文檔中開(kāi)始,在Object類方法中有:
void notify()?
Wakes up a single thread that is waiting on this object’s monitor.?
譯:?jiǎn)拘言诖藢?duì)象監(jiān)視器上等待的單個(gè)線程
void notifyAll()?
Wakes up all threads that are waiting on this object’s monitor.?
譯:?jiǎn)拘言诖藢?duì)象監(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)用此對(duì)象的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)用此對(duì)象的notify() 方法或 notifyAll() 方法,或者指定的時(shí)間過(guò)完。
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)用此對(duì)象的notify( ) 方法或 notifyAll( ) 方法,或者其他線程打斷了當(dāng)前線程,或者指定的時(shí)間過(guò)完。
上面是官方文檔的簡(jiǎn)介,下面我們根據(jù)官方文檔總結(jié)一下:
wait( ),notify( ),notifyAll( )都不屬于Thread類,而是屬于Object基礎(chǔ)類,也就是每個(gè)對(duì)象都有wait( ),notify( ),notifyAll( ) 的功能,因?yàn)槊總€(gè)對(duì)象都有鎖,鎖是每個(gè)對(duì)象的基礎(chǔ),當(dāng)然操作鎖的方法也是最基礎(chǔ)了。
當(dāng)需要調(diào)用以上的方法的時(shí)候,一定要對(duì)競(jìng)爭(zhēng)資源進(jìn)行加鎖,如果不加鎖的話,則會(huì)報(bào) IllegalMonitorStateException 異常
當(dāng)想要調(diào)用wait( )進(jìn)行線程等待時(shí),必須要取得這個(gè)鎖對(duì)象的控制權(quán)(對(duì)象監(jiān)視器),一般是放到synchronized(obj)代碼中。
在while循環(huán)里而不是if語(yǔ)句下使用wait,這樣,會(huì)在線程暫?;謴?fù)后都檢查wait的條件,并在條件實(shí)際上并未改變的情況下處理喚醒通知
調(diào)用obj.wait( )釋放了obj的鎖,否則其他線程也無(wú)法獲得obj的鎖,也就無(wú)法在synchronized(obj){ obj.notify() } 代碼段內(nèi)喚醒A。
notify( )方法只會(huì)通知等待隊(duì)列中的第一個(gè)相關(guān)線程(不會(huì)通知優(yōu)先級(jí)比較高的線程)
notifyAll( )通知所有等待該競(jìng)爭(zhēng)資源的線程(也不會(huì)按照線程的優(yōu)先級(jí)來(lái)執(zhí)行)
假設(shè)有三個(gè)線程執(zhí)行了obj.wait( ),那么obj.notifyAll( )則能全部喚醒tread1,thread2,thread3,但是要繼續(xù)執(zhí)行obj.wait()的下一條語(yǔ)句,必須獲得obj鎖,因此,tread1,thread2,thread3只有一個(gè)有機(jī)會(huì)獲得鎖繼續(xù)執(zhí)行,例如tread1,其余的需要等待thread1釋放obj鎖之后才能繼續(xù)執(zhí)行。
當(dāng)調(diào)用obj.notify/notifyAll后,調(diào)用線程依舊持有obj鎖,因此,thread1,thread2,thread3雖被喚醒,但是仍無(wú)法獲得obj鎖。直到調(diào)用線程退出synchronized塊,釋放obj鎖后,thread1,thread2,thread3中的一個(gè)才有機(jī)會(huì)獲得鎖繼續(xù)執(zhí)行。
2.wait和notify簡(jiǎn)單使用示例
public class WaitNotifyTest{
?// 在多線程間共享的對(duì)象上使用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() + "開(kāi)始等待");
? ? ? ? ? ? ? ? ? ? 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() + "開(kāi)始準(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開(kāi)始等待
線程wait thread3開(kāi)始等待
線程wait thread2開(kāi)始等待
線程notify thread開(kāi)始準(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方法
該方法喚醒在該對(duì)象上等待的所有線程
在Java語(yǔ)言中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(yǔ)言,不僅吸收了C++語(yǔ)言的各種優(yōu)點(diǎn),還摒棄了C++里難以理解的多繼承、指針等概念,因此Java語(yǔ)言具有功能強(qiáng)大和簡(jiǎn)單易用兩個(gè)特征。Java語(yǔ)言作為靜態(tài)面向?qū)ο缶幊陶Z(yǔ)言的代表,極好地實(shí)現(xiàn)了面向?qū)ο罄碚?,允許程序員以優(yōu)雅的思維方式進(jìn)行復(fù)雜的編程。
Java具有簡(jiǎn)單性、面向?qū)ο?、分布式、健壯性、安全性、平臺(tái)獨(dú)立與可移植性、多線程、動(dòng)態(tài)性等特點(diǎn) ?。Java可以編寫桌面應(yīng)用程序、Web應(yīng)用程序、分布式系統(tǒng)和嵌入式系統(tǒng)應(yīng)用程序等。
?在Java多線程中,notify() 與 notifyAll() 是比較重要的方法,通常與wait方法配合使用,它們只能在同步域里面調(diào)用否則會(huì)出現(xiàn)異常IllegalMonitorStateException
下面說(shuō)說(shuō)他們的功能:
notify :作用是喚醒指定某個(gè)線程,在一般實(shí)際使用情況下此方法用的不多,因?yàn)橐话悴恢粫?huì)存在1、2個(gè)線程,當(dāng)線程多的時(shí)候使用此方法維護(hù)起來(lái)就很麻煩,很容易造成死鎖。
notifyAll : 作用是喚醒指定鎖上等待執(zhí)行的所有線程,打個(gè)比方,此時(shí)有線程 T1 線程 T2 線程 T3 現(xiàn)在假設(shè)T2與T3線程先執(zhí)行并且是處于wait等待狀態(tài),他們要等T1去喚醒他們,由于他們兩都是處于wait狀態(tài),除非有其他線程喚醒它們否則他們會(huì)一直處于等待狀態(tài),如果此時(shí)T1已經(jīng)執(zhí)行并且已經(jīng)調(diào)用notifyAll方法,就會(huì)喚醒T2與T3,可能這時(shí)會(huì)有疑問(wèn)線程T1是怎么能夠喚醒其他兩個(gè)線程的呢?首先會(huì)有個(gè)前提條件 T1、T2、T3線程都是處于同一個(gè)鎖域,因?yàn)槿绻皇翘幱谕粋€(gè)鎖域調(diào)用notifyAll或者是notify會(huì)報(bào)錯(cuò)的,代碼例如這樣:
T1:
// 之所以加個(gè)while 以及flag標(biāo)示是因?yàn)榭赡艽嬖赥2執(zhí)行完就執(zhí)行T1的情況或者是先執(zhí)行了T1,這樣就會(huì)造成死鎖,因?yàn)門1一旦先執(zhí)行那就沒(méi)有線程把T2、T3喚醒了,T2 T3則一直等待有個(gè)線程喚醒他,而此時(shí)已沒(méi)有線程能喚醒他們了。
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í)會(huì)很麻煩,因?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()方法之后會(huì)自動(dòng)釋放當(dāng)前線程占用的鎖。
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 注:本文收集于各大網(wǎng)絡(luò),一切只為了學(xué)習(xí)!謝謝各位大神.