Object类
介绍
- Object类是Java所有类的根类,Java中的类都要直接或间接继承Object类,所以Object类中定义的方法是所有Java对象的通用行为
内容
- 首先,Object类中有一个空的的构造方法
public Object() {}。由于Java中如果子类没有显式地调用父类的构造方法,编译器会自动调用父类的构造方法,而Object是所有类的根类,如果没有构造方法,那么Java中的其他类在实例化时会找不到其Object类的构造方法而导致编译失败。
不过如果一个类如果没有显式的定义构造方法,编译器会默认生成 一个构造方法,即使JDK的源码中现实的构造了这个方法,其效果也等价于"不编写构造方法,编译器自动构造一个构造方法"。 public final native Class<?> getClass();该方法用于返回当前对象运行时类的class对象,class对象包括当前类的各种信息(类名、接口、方法、父类等)。public native int hashCode();该方法用于获取对象的哈希码,哈希码可以让哈希表以O(1)的时间复杂度进行插入、删除、查找操作。哈希表的存储逻辑是使用数组完成的,获得某个对象的哈希码后,可以通过哈希算法得到该对象的数组索引,用数组的索引访问可以得到桶,而由于桶内的元素通常是常数,在桶内(链表等方式存储)进行增删查的操作的时间复杂度也是O(1),所以哈希码可以使Java集合框架高效运行public boolean equals(Object obj) { return (this == obj); }该方法通过比较两个对象的引用地址来判断两个对象是否相等。如果需要判断两个对象的逻辑是否相同,而不是简单的判断引用地址是否相同的话,则需要重写equals方法,重写equals方法的同时也需要重写hashCode方法 ,因为相同对象的哈希码必须相同。protected native Object clone() throws CloneNotSupportedException;该方法用于克隆一个对象的副本。该副本需要满足:副本与原始对象应是不同对象;副本与原始对象对应的类应相同;副本与原始对象在逻辑上相等(需重写equals方法),但不强制(因为clone方法默认执行浅拷贝,只复制当前字段值,如果需要深拷贝,直接重写clone方法即可)。public String toString() { return getClass().getName() + "@" + Integer.toHexString(hashCode()); }该方法用于返回对象的"文本表示",其默认返回格式为"类名+@+哈希码的十六进制无符号表示",一般对于自定义的类,我们是需要进行重写toString方法以得到更加可读的文本信息。- 线程同步的相关方法:
notify(),notifyAll(),wait()这三个方法都是用于多线程间的协作,必须由持有线程监听器锁的线程调用,否则会抛出异常。对象监听器的工作流程:
- 某个对象obj会有一个关联的monitor(对象监听器),这个monitor会维护两个队列:入口集(Entry Set)、等待集(Wait Set)。入口集用于存放所有正在等待获取monitor锁的线程,等待集用于存放调用了
obj.wait()方法后,释放锁并等待的线程。初始状态monitor并未被任何线程持有(锁空闲) - 当线程t1进入
synchronized(obj){}时,该线程会尝试持有obj的monitor,此时obj的monitor并未被持有,因此t1线程成功持有obj的monitor,并开始执行synchronized代码块中的代码。 - 若此时有一个线程t2尝试进入
synchronized(obj){}时,由于obj的monitor已经被线程t1所占用,t2无法获取monitor并会被阻塞,此时的t2将会进入入口集并等待。 - 此时如果t1在执行
synchronized(obj){}代码块时执行了wait()方法,t1线程会释放锁,并进入等待集进行等待。此时obj的monitor便会被释放,处于空闲状态,位于入口集的线程(如t2)会竞争该monitor - 当t2持有锁之后,开始执行代码块中的内容,若此时执行了
notify()方法,等待集中的线程会被随机选择一个,将它转移到入口集。 - t2执行完毕代码或执行
wait()后,它会释放锁,此时位于入口集的线程会竞争锁,谁拿到了monitor谁就可以执行代码块中的内容。假如是刚刚的t1拿到了锁,它就会从wait()的调用处继续执行代码,因此,wait()经常会放在while循环中,直到条件满足后才让它继续执行代码。
阻塞队列的实现
思路逻辑
用好wait() 和notifyAll()方法,让队列满(空)的时候,无法进行入队(出队)操作即可
代码实现
java
package com.nwu.by0321_SimpleBlockingQueue;
import java.util.LinkedList;
public class SimpleBlockingQueue <T> {
private LinkedList<T> list ; //用链表来实现队列
int capacity;
public SimpleBlockingQueue(int capacity) { //创建构造方法,判断大小是否合规
if (capacity <= 0) {
throw new IllegalArgumentException();
}
this.capacity = capacity;
list=new LinkedList<>();
}
public synchronized int size(){
return list.size();
}
public synchronized void put(T t) throws InterruptedException{ //入队方法
//若队列满,则等待
while(list.size()==capacity){
wait();
}
//未满则直接添加元素
list.add(t);
notifyAll();
}
public synchronized T take() throws InterruptedException{ //出队
//如果为空则等待
while(list.isEmpty()){
wait();
}
T t=list.remove();
notifyAll();
return t ;//删除并返回第一个值
}
}
class Main{
public static void main(String[] args) {
SimpleBlockingQueue<String> bq=new SimpleBlockingQueue<>(5);
Thread t1= new Thread(){ //生产线程
@Override
public void run() {
for (int i = 1; i <= 21; i++) {
if(i!=21){
try {
sleep(500);
bq.put("Task"+i);//添加10个任务
System.out.println("正在生产任务----------"+"Task"+i);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}else{
try {
bq.put(" ");
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
};
t1.start();
Thread t2= new Thread(){ //加工线程
@Override
public void run() {
while(true){
try {
sleep(1000);
String str=bq.take();
System.out.println(str+"已完成加工");
if(str.equals(" ")){
break;
}
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
};
t2.start();
Thread t3= new Thread(){ //监听线程
@Override
public void run() {
while (true) {
try {
sleep(500);
System.out.println("当前还剩"+bq.size()+"个任务未加工完成");
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
};
t3.start();
}
}
运行截图
