您的位置 首页 java

Java基础之Object解析(二)

Java 基础之Object类解析(二)

1. 导读

接上一篇的分享, 我们一起看一下Object类中剩余的6he方法:

1.1 toString();

1.2 notify();

1.3 notifyAll();

1.4 wait();

1.5 finalize();

1.6 registerNatives();

2. toString方法

toString方法是我们比较常用的方法, 在Object中的默认实现返回一个 类名+’@’+hasCode的16进制拼接的 字符串 ;

public String toString() {

return getClass().getName() + “@” + Integer.toHexString(hashCode());

}

注意到toString方法是没有被final修饰的, 证明他可以被子类重写;

划重点:

.1 我们可以根据自身需求重写toString方法, 默认实现是返回类名+’@’+hashCode的16进制拼接而成的字符串;

.2 我们调用System.out.println(obj), 默认调用的就是obj的toString方法;

3. notify, notifyAll 和 wait方法

把这三个方法放一起是因为这三个方法是配套的, 用来实现 java 多线程的协作; 既然是多 线程 相关的方法, 为什么会在Object这个类中呢? 这个是因为调用这三个方法前提都是需要在 synchronized 修饰的同步代码块中, 而synchronized锁的实现是基于对象(Object)监视器的;

那么这三个方法之间是如何协作的? 每个方法又具体干了什么呢?

3.1 wait: 在同步代码块中调用该方法时, 当前线程立即释放锁并等待, 直到有其他线程调用

notify/notifyAll或超时等待时, 才会去再次竞争锁, 成功后继续执行下面的逻辑;

public final native void wait(long timeout) throws InterruptedException;

wait方法被final, native修饰, 证明他是不可被重写的原生方法; 该方法在等待的时候, 有其他线

程打断了他的等待, 那么他会抛出InterruptedException并退出等待;

Object类中还有wait(), wait(long timeout, int nanos)这两种wait的实现, 但其实都是调

用的wait(long timeout);

3.2 notify: 线程A在同步代码块中调用该方法时, 会随机地唤醒一个等待在该对象锁上的线程B, 注意这时候唤醒的线程B还没有持有锁, 必须要等到线程A释放锁后才能持有该把锁;

当线程在A对象的同步代码块中执行B对象的notify时, 会抛出IllegalMonitorStateException;如

果没有这个限制, 我们想想会发生什么情况;

拿最常用的生产者消费者举例:

1. 消费者消费(notify生产者);

2. 当货物不存在时等待生产者生产(wait);

3. 生产者生产货物(notify消费者);

4. 当货物没被消费时等待(wait);

我们期望的是 1–>2–>3–>4–>1–>2…这样的顺序;

但是如果消费者和生产者持有的是两把不同的对象锁, 那么当消费者notify时, 因为生产者等待在另一把锁上, 导致无法唤醒生产者, 那么就会导致:1–>2–>4,生产者和消费者会同时阻塞;

所以为了消除这种竞态条件, 在A对象的同步代码块中, 只能调用A对象的notify方法, 否则就会抛出IllegalMonitorStateException;

3.3 notifyAll: 线程A在同步代码块中调用该方法时, 会唤醒所有等待在该锁上的线程, 同样的, 这些唤醒的线程只有在线程A释放锁以后, 才能再次竞争该把锁, 竞争到锁的线程继续执行, 其他的线程继续等待;

如果调用的线程不是该把锁的持有者, 那么也会抛出IllegalMonitorStateException;

public final native void notify();

public final native void notifyAll();

nofity 和 notifyAll都是不可重写的原生方法, 虽然这两个方法没有显式的抛出

IllegalMonitorStateException这个异常, 但是当竞态条件产生时,

IllegalMonitorStateException这个异常自然就出现了;

划重点:

.1 执行notify | notifyAll时, 唤醒的线程并不会立即持有锁, 故而会形成假唤醒的情况, 那么在

写wait方法的时, 推荐使用以下方式:

2019/1/23 Object(2)

file:///Users/zhongchunyan/Desktop/Object(2)/Object(2).html 3/5

synchronized( lock ) {

while(!condition) {

lock.wait();

}

//doSomething;

}

当条件不满足时, 该线程还需继续等待;

.2 执行wait | notify | notifyAll的对象, 必须与synchronized锁住的对象是同一个, 否则会

形成竞态条件导致IllegalMonitorStateException异常的产生;

.3 java多线程这块内容较多, 这期只是简略的介绍下这三个方法, 后面会有一系列的文章专门分享多线程相关的内容;

4. finalize方法

protected void finalize() throws Throwable { }

java的内存管理依赖于 JVM 实现的GC(Garbage Collection)机制来实现内存的回收, GC相关的内容后面再展开; JVM在进行GC时, 如果这个对象需要被回收, 会先判断该方法是否有被重写, 若未重写, 则直接回收该对象内存空间;

反之则判断该对象的finalize是否被执行过, 如果没有执行过, 会先放入一个队列中, 由低优先级的线程去执行该对象的finalize方法, 执行完毕后再判断该对象是否需要回收;

如果该对象已经执行过一遍finalize方法了, 直接回收对象的内存空间;

GC调用finalize过程

上图就是对GC执行回收对象finaliz方法时对象状态变化的过程;

划重点:

.1 重写了finalize方法后, 在对象的整个生命周期中GC只会执行一次finalize方法;

5. registerNatives方法

最后来看一下registerNatives方法, 可能看过源码的同学都知道这个方法是Object类中的第一个方法, 我把他放到最后将的原因是和他的功能相关;

private static native void registerNatives();

static {

registerNatives();

}

首先应该关注到的是static代码块, 静态代码块是在类加载时就会执行的, 这个代码块中只是调用了registerNatives方法;

再看到registerNatives方法:

5.1 static: 这是个 静态方法 , 因为静态只能调用静态, 也就是只有静态方法才能在静态代码块中直接调用;

5.2 native: 原生方法, 他是由C实现的;

5.3 看到方法名, 我们可以猜到他是注册Object类中的原生方法的, 实现java中声明的native方法与C/C++实现函数的绑定;

static JNINativeMethod methods[] = {

{“hashCode”, “()I”, (void *)&JVM_IHashCode},

{“wait”, “(J)V”, (void *)&JVM_MonitorWait},

{“notify”, “()V”, (void *)&JVM_MonitorNotify},

{“notifyAll”, “()V”, (void *)&JVM_MonitorNotifyAll},

{“clone”, “()Ljava/lang/Object;”, (void *)&JVM_Clone},

};

JNI EXPORT void JNICALL

Java_java_lang_Object_registerNatives(JNIEnv *env, jclass cls)

{

(*env)->RegisterNatives(env, cls,

methods, sizeof (methods)/sizeof(methods[0]));

}

这是OpenJDK6对Object中native方法的绑定, Java_java_lang_Object_registerNatives这样

函数可直接调用java中native函数, 通过上面的代码可以清晰的看到registerNatives实现了method中native方法的绑定;

同时也可以看到methods中是没有getClass这个方法, 自然可以猜到他也是采用规定函数名称直接调用的方式实现绑定的(Java_java_lang_Object_getClass);

JNI这里就不做展开, 感兴趣的可以阅读下Java? Native Interface: Programmer’s Guide and Specification;

至此, Object类中的所有方法的解析已经告一段落了, 如有错误之处, 欢迎指正;

文章来源:智云一二三科技

文章标题:Java基础之Object解析(二)

文章地址:https://www.zhihuclub.com/188745.shtml

关于作者: 智云科技

热门文章

网站地图