Object.java 简介

Class Object is the root of the class hierarchy. Every class has Object as a superclass. All objects, including arrays, implement the methods of this class.

类 Object 是类层次结构的根类。每个类都使用 Object 作为超类。所有对象(包括数组)都实现这个类的方法。

由上边这句话想到:

  • 方法表的构建从 Object 类开始
  • 每一个数组实例都关联着一个 Class对象,并且其父类就是 Object

getClass 方法

public final native Class<?> getClass();

返回此 Object 的运行时类。

Number n = 0;// 被解释为: Number n = Integer.valueOf(0);
Class<? extends Number> c = n.getClass(); 
System.out.println(c);//class java.lang.Integer

对于HotSpot虚拟机来说,处理过程如下:
getClass
我们获得到的是 Class 对象应对于 InstanceKlass 的一个Java 级别的 _java_mirror。
JVM –>InstanceKlass
Java –> _java_mirror(也就是某个Class实例)
为什么构造出来_java_mirror 这种概念?
因为我们需要在Java层面实现反射。

通过getClass获取 Class 类对象需要两步:

  • 获取元数据
  • 通过元数据获得_java_mirror

JVM 实现也是如此:
参见http://www.cnblogs.com/xy-nb/p/6769586.html

hashCode 方法

public native int hashCode();

返回该对象的哈希码值。支持此方法是为了提高哈希表(例如 java.util.HashMap )的性能。

equals 方法

public boolean equals(Object obj) { return (this == obj); }

指示其他某个对象是否与此对象“相等”。

hashCode 方法与 equals 方法 一般配合使用。
实例:
简单的 HashMap 实现

clone 方法

protected native Object clone() throws CloneNotSupportedException;

创建并返回当前对象的一份拷贝。
一般情况下需要满足:

x.clone() != x
x.clone().getClass() == x.getClass()

super.clone() 将返回一个调用 super.clone() 方法的对象

https://blog.csdn.net/x_iya/article/details/79062656

toString 方法

public String toString() {
    return getClass().getName() + "@" +     Integer.toHexString(hashCode());
}

返回该对象的字符串表示。

finalize 方法

protected void finalize() throws Throwable { }

食之无用弃之也不可惜的方法=。=
https://blog.csdn.net/x_iya/article/details/78590056

package test;

import java.lang.ref.WeakReference;

class Demo {
    @Override
    protected void finalize() throws Throwable {
        System.out.println("i will be destroyed");
    }
}

public class Main {
    public static void main(String[] args) {
        Demo demo = new Demo();
        /**
         * 如果只存在弱引用指向当前对象,则垃圾回收时一定回收该对象
         */
        WeakReference<Demo> carWeakReference = new WeakReference<>(demo);

        int i = 0;
        while (true) {
            if (carWeakReference.get() != null) {
                i++;
                System.out.println("Object is alive for " + i + " loops - " + carWeakReference);
            } else {
                System.out.println("Object has been collected.");
                break;
            }
        }
    }
}

输出:

...
Object is alive for 87908 loops - java.lang.ref.WeakReference@1540e19d
Object is alive for 87909 loops - java.lang.ref.WeakReference@1540e19d
Object has been collected.
i will be destroyed

并发相关的方法

在 Java 中,可以通过调用 Object 对象的wait(long timeout)方法和notify()方法或notifyAll()方法来实现线程间的通信。
在线程中调用wait()方法,将阻塞当前线程并且等待其他线程的通知(其他线程调用notify()方法或notifyAll()方法)。
在线程中调用notify()方法或notifyAll()方法,将通知其他线程从wait()方法处返回。

// 核心的三个方法
public final native void notify();
public final native void notifyAll();
public final native void wait(long timeout) throws InterruptedException;

public final void wait() throws InterruptedException { wait(0); }
public final void wait(long timeout, int nanos) throws InterruptedException {
    if (timeout < 0) {
        throw new IllegalArgumentException("timeout value is negative");
    }
    if (nanos < 0 || nanos > 999999) {
        throw new IllegalArgumentException("nanosecond timeout value out of range");
    }
    if (nanos > 0) {
        timeout++;
    }
    wait(timeout);
}

以上方法构成了Java里面的 等待/通知机制

wait()

  • 该方法用来将当前线程置入等待状态(WAITING),直到接到通知或被中断为止。
  • 在调用wait()之前,线程必须要获得该对象的锁,即只能在同步方法或同步代码块中调用该方法。
  • 进入wait()方法后,当前线程释放锁。
  • 在从wait()返回前,线程与其他线程竞争重新获得锁。如果调用wait()时,没有持有适当的锁,则抛出IllegalMonitorStateException,它是RuntimeException的一个子类,因此,不需要try-catch结构。

notify()

该方法也要在同步方法或同步代码块中调用,即在调用前,线程也必须要获得该对象的锁,如果调用notify()时没有持有适当的锁,也会抛出IllegalMonitorStateException

该方法用来通知那些处于等待(WAITING/TIMED_WAITING)状态的线程。如果有多个线程等待,则线程规划器任意挑选出其中一个线程来发出通知,并使它等待获取该对象的对象锁(notify 后,当前线程不会马上释放该对象锁,wait所在的线程并不能马上获取该对象锁,要等到程序退出 synchronized 代码块后,当前线程才会释放锁,wait 所在的线程也才可以获取该对象锁),但不惊动其他同样在等待被该对象 notify 的线程们。
当第一个获得了该对象锁的 wait 线程运行完毕以后,它会释放掉该对象锁,此时如果该对象没有再次使用 notify 语句,则即便该对象已经空闲,其他等待状态等待的线程由于没有得到该对象的通知,会继续阻塞在等待状态,直到这个对象发出一个 notify 或 notifyAll。这里需要注意:它们等待的是被notify 或 notifyAll,而不是锁。这与下面的 notifyAll() 方法执行后的情况不同。

notifyAll()

该方法与notify()方法的工作方式相同,重要的一点差异是:
notifyAll()使所有原来在该对象上等待(WAITING/TIMED_WAITING)的线程统统退出等待的状态(即全部被唤醒,但由于此时还没有获取到该对象锁,因此还不能继续往下执行),变成等待获取该对象上的锁,一旦该对象锁被释放(调用notify()/notifyAll()的线程退出同步方法/同步代码块),他们就会去竞争。如果其中一个线程获得了该对象锁,它就会继续往下执行,在它退出 synchronized 代码块,释放锁后,其他的已经被唤醒的线程将会继续竞争获取该锁,一直进行下去,直到所有被唤醒的线程都执行完毕。

wait(long timeout) 与 wait(long timeout, int nanos)

显然,这两个方法是设置等待超时时间的,后者在超值时间上加上nanos,精度也难以达到,因此,该方法很少使用。对于前者,如果在等待线程接到通知或被中断之前,已经超过了指定的毫秒数,则它通过竞争重新获得锁,并从wait(long)返回。
另外,需要知道,如果设置了超时时间,当wait()返回时,我们不能确定它是因为接到了通知还是因为超时而返回的,因为wait()方法不会返回任何相关的信息。但一般可以通过设置标志位来判断,在notify之前改变标志位的值,在wait()方法后读取该标志位的值来判断,当然为了保证notify不被遗漏,我们还需要另外一个标志位来循环判断是否调用wait()方法。

总结

这里写图片描述
调用以上方法需要当前线程是对象监视器的所有者,否则发生InterruptedException 异常。


事例

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.TimeUnit;

public class Main {
    private static boolean flag = true;
    private static final Object lock = new Object();

    public static void main(String[] args) {
        Thread waitThread = new Thread(new Wait(), "WaitThread");
        waitThread.start();
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        Thread notifyThread = new Thread(new Notify(), "NotifyThread");
        notifyThread.start();
    }

    static class Wait implements Runnable {
        @Override
        public void run() {
            synchronized (lock) {
                while (flag) {
                    try {
                        System.out.println(Thread.currentThread() + " flag is true. wait @ " + new SimpleDateFormat(" HH: mm: ss ").format(new Date()));
                        lock.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println(Thread.currentThread() + " flag is false. running @ " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
            }
        }
    }

    static class Notify implements Runnable {
        @Override
        public void run() {
            synchronized (lock) {
                System.out.println(Thread.currentThread() + " hold lock. notify @ " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
                lock.notifyAll();
                flag = false;
                try {
                    TimeUnit.SECONDS.sleep(5);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            synchronized (lock) {
                System.out.println(Thread.currentThread() + " hold lock again. sleep @ " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
                try {
                    TimeUnit.SECONDS.sleep(5);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

输出:

Thread[WaitThread,5,main] flag is true. wait @  09: 52: 58 
Thread[NotifyThread,5,main] hold lock. notify @ 09:52:59
Thread[WaitThread,5,main] flag is false. running @ 09:53:04
Thread[NotifyThread,5,main] hold lock again. sleep @ 09:53:04

其中第三行和第四行的输出顺序可能会互换,因为Notify 里的第二次加锁与 Wait 里的wait方法的返回属于同级的。也就是说他们的调用顺序是随机的。

这里写图片描述

这里写图片描述

如果线程调用了对象的wait()方法,那么线程便会处于该对象的等待队列中,等待队列中的线程不会去竞争该对象的锁。

当有线程调用了对象的notifyAll()方法(唤醒所有wait线程)或notify()方法(只随机唤醒一个wait线程),被唤醒的的线程便会进入该对象的同步队列中,同步队列中的线程会去竞争锁(需要等待notify/notifyAll所在代码块执行完毕)。

优先级高的线程竞争到对象锁的概率大,假若某线程没有竞争到该对象锁,它还会留在同步队列中,唯有线程再次调用wait()方法,它才会重新回到等待队列中。而竞争到对象锁的线程则继续往下执行,直到执行完了synchronized代码块,它会释放掉该对象锁,这时同步队列中的线程会继续竞争该对象锁。

总之,notifyAll/notify 使对象从等待状态(不能竞争锁)变为阻塞状态(可以竞争锁)。


使用多线程循环打印1和2

package test;

import java.util.concurrent.TimeUnit;

public class Main {
    public static void main(String[] args) {
        final Object lock  = new Object();
        Thread thread1 = new Thread(() ->{
            while (true) {
                synchronized (lock) {
                    System.out.println(1);
                    /*try {
                        TimeUnit.SECONDS.sleep(1);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }*/
                    lock.notifyAll();// 唤醒 thread2 线程,使 thread2 线程从等待队列(不可竞争锁)加入同步队列(可竞争锁)
                    try {
                        lock.wait();// 使当前线程 thread1(释放锁) 进入同步队列(等待从 wait 方法返回)
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }, "thread1");
        Thread thread2 = new Thread(() ->{
            while (true) {
                synchronized (lock) {
                    System.out.println(2);
                    /*try {
                        TimeUnit.SECONDS.sleep(1);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }*/
                    lock.notifyAll();// 唤醒 thread1 线程,使 thread1 线程从等待队列(不可竞争锁)加入同步队列(可竞争锁)
                    try {
                        lock.wait();// 使当前线程 thread2(释放锁) 进入同步队列(等待从 wait 方法返回)
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }, "thread2");
        thread1.start();
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        thread2.start();
    }
}

参考:
http://fangjian0423.github.io/2016/03/12/java-Object-method/
https://blog.csdn.net/ns_code/article/details/17225469


成长的魅力在于每一天你对一句话、一件事、一个人的理解都是不一样的!
而要达到我所谓的预见性还有很长一段距离。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

N3verL4nd

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值