Objcet類知識(shí)整合

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)題]

2. 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方法

4. 一些代碼示例

(1) 對(duì)象復(fù)活

[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í)!謝謝各位大神.

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語(yǔ)法,類相關(guān)的語(yǔ)法,內(nèi)部類的語(yǔ)法,繼承相關(guān)的語(yǔ)法,異常的語(yǔ)法,線程的語(yǔ)...
    子非魚_t_閱讀 34,623評(píng)論 18 399
  • JAVA面試題 1、作用域public,private,protected,以及不寫時(shí)的區(qū)別答:區(qū)別如下:作用域 ...
    JA尐白閱讀 1,249評(píng)論 1 0
  • 小編費(fèi)力收集:給你想要的面試集合 1.C++或Java中的異常處理機(jī)制的簡(jiǎn)單原理和應(yīng)用。 當(dāng)JAVA程序違反了JA...
    八爺君閱讀 5,148評(píng)論 1 114
  • 我看到躍動(dòng)的時(shí)間 正在貓的眼睛里靜止 時(shí)間第一次是這樣的姿態(tài)出現(xiàn) 我同它無(wú)聲的攀談 順著陽(yáng)光傾瀉的金色舞步 徜徉在...
    方潭閱讀 508評(píng)論 0 2
  • 什么時(shí)候才會(huì)讓你徹底體會(huì)到自己不再是女孩,而是已經(jīng)步入了女人的階段,我想大概就是懷孕當(dāng)媽媽起吧,第一次懷疑自己懷孕...
    清風(fēng)伊笑閱讀 881評(píng)論 0 0

友情鏈接更多精彩內(nèi)容