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虚拟机来说,处理过程如下:
我们获得到的是 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
成长的魅力在于每一天你对一句话、一件事、一个人的理解都是不一样的!
而要达到我所谓的预见性还有很长一段距离。