文章目录
- 1、常量池Integer
- 2、Java 中 interrupted() 和 isInterrupted()方法的区 别?
- 3、wait()和 sleep()的区别。
- 4、为什么说 ConcurrentHashMap 是 的?以及为何多 个线程并发修改 ConcurrentHashMap 时不会报 ConcurrentModificationException?
- 5、happens-before 原则(先行发生原则):
- 6、java 中的悲观锁和乐观锁?
- 7、用户线程和守护线程有什么区别?
- 8、线程的五个状态
- 9、如何自定义类加载器
- 10、描述 Java 类加载器的工作原理及其组织结构。
- 11、Vector、ArrayList的扩容
1、常量池Integer
Integer和int,类型不同的话,要拆箱比较,直接比较数值大小。
在Java中1字节大小以内的Integer(-128-127)都是存在一个常量池中的,Integer是final类型的。
public static void main(String[] args)
{
int i1 = 128;
Integer i2 = 128;
Integer i3 = new Integer(128);
System.out.println(i1 == i2); //true
System.out.println(i1 == i3); //true
System.out.println(i2 == i3); //true
}
2、Java 中 interrupted() 和 isInterrupted()方法的区 别?
二个方法都是判断线程是否停止的方法。
- 1.前者是静态方法,后者是非静态方法。interrupted 是作用于当前正在运 行的线程,isInterrupted 是作用于调用该方法的线程对象所对应的线程。(线程对象对应的线程不一定是当前运行的线程。例如我们可以在 A 线程中去调用 B 线程对象的 isInterrupted 方法,此时,当前正在运行的线程就是 A 线程。)
- 2.前者会将中断状态清除而后者不会。
3、wait()和 sleep()的区别。
- 1、这两个方法来自不同的类,sleep()来自 Thread 类,是静态方法;wait() 是 Object 类里面的方法,和 notify()或者 notifyAll()方法配套使用,来实现 线程间的通信。
- 2、最主要是 sleep 是将当前线程挂起指定的时间,没有释放锁;而 wait 方法 释放了锁,使得其他线程可以使用同步控制块或者方法。
- 3、使用范围:wait,notify 和 notifyAll 只能在同步控制方法或者同步控制块 里面使用,而 sleep 可以在任何地方使用 。
synchronized(x)
{
x.notify()
//或者 wait()
}
4、为什么说 ConcurrentHashMap 是 的?以及为何多 个线程并发修改 ConcurrentHashMap 时不会报 ConcurrentModificationException?
- 1.ConcurrentHashMap#get()
正是因为 get 操作几乎所有时候都是一个无锁操作(get 中有一个 readValueUnderLock 调用,不过这句执行到的几率极小),使得同一个 Segment 实例上的 put 和 get 可以同时进行,这就是 get 操作是弱一致的根 本原因。 - 2.ConcurrentHashMap#clear() clear 方法很简单,看下代码即知。
因为没有全局的锁,在清除完一个 segment 之后,正在清理下一个segment 的时候,已经清理的 segment 可能又被加入了数据,因此 clear返回的时候,ConcurrentHashMap 中是可能存在数据的。因此,clear 方法是弱一致的。
public void clear()
{
for(int i=0;i<segments.length;i++)
{
segments[i].clear();
}
}
ConcurrentHashMap 中的迭代器
在遍历过程中,如果已经遍历的数组上的内容变化了,迭代器不会抛出
ConcurrentModificationException 异常。如果未遍历的数组上的内容发生了变化,则有可能反映到迭代过程中。这就是ConcurrentHashMap 迭代器弱一 致的表现。
在这种迭代方式中,当 iterator 被创建后,集合再发生改变就不再是抛出 ConcurrentModificationException,取而代之的是在改变时 new 新的数据 从而不影响原有的数据,iterator 完成后再将头指针替换为新的数据,这样 iterator 线程可以使用原来老的数据,而写线程也可以并发的完成改变,更重 要的,这保证了多个线程并发执行的连续性和扩展性,是性能提升的关键。
总结,ConcurrentHashMap 的弱一致性主要是为了提升效率,是一致性 与效率之间的一种权衡。要成为强一致性,就得到处使用锁,甚至是全局锁, 这就与 Hashtable 和同步的 HashMap
5、happens-before 原则(先行发生原则):
- 程序次序规则:一个线程内,按照代码顺序,书写在前面的操作先行发生于书写在后面的操作
- 锁定规则:一个 unLock 操作先行发生于后面对同一个锁的 lock 操作
- volatile 变量规则:对一个变量的写操作先行发生于后面对这个变量的读操作
- 传递规则:如果操作 A 先行发生于操作 B,而操作 B 又先行发生于操作 C,则可以 得出操作 A 先行发生于操作 C
- 线程启动规则:Thread 对象的 start()方法先行发生于此线程的每个一个动作
- 线程中断规则:对线程 interrupt()方法的调用先行发生于被中断线程的代码检测到中断事件的发生
- 线程终结规则:线程中所有的操作都先行发生于线程的终止检测,我们可以通过 Thread.join()方法结束、Thread.isAlive()的返回值手段检测到线程已经终止执行
- 对象终结规则:一个对象的初始化完成先行发生于他的 finalize()方法的开始
6、java 中的悲观锁和乐观锁?
- 悲观锁:悲观锁是认为肯定有其他线程来争夺资源,因此不管到底会不会发生争夺,悲观锁总是会先去锁住资源,会导致其它所有需要锁的线程挂起,等待持有锁的线程释放 锁。Synchronized 和 Lock 都是悲观锁。
- 乐观锁:每次不加锁,假设没有冲突去完成某项操作,如果因为冲突失败就重试,直 到成功为止。就是当去做某个修改或其他操作的时候它认为不会有其他线程来做同样的操 作(竞争),这是一种乐观的态度,通常是基于 CAS 原子指令来实现的。CAS 通常不会将 线程挂起,因此有时性能会好一些。乐观锁的一种实现方式——CAS。
7、用户线程和守护线程有什么区别?
当我们在 Java 程序中创建一个线程,它就被称为用户线程。将一个用户线 程设置为守护线程的方法就是在调用 start()方法之前,调用对象的 setDamon(true)方法。一个守护线程是在后台执行并且不会阻止 JVM 终止的 线程,守护线程的作用是为其他线程的运行提供便利服务。当没有用户线程在 运行的时候,JVM 关闭程序并且退出。一个守护线程创建的子线程依然是守护 线程。
守护线程的一个典型例子就是垃圾回收器。
8、线程的五个状态
在 Java 当中,线程通常都有五种状态,创建、就绪、运行、阻塞和死亡。
9、如何自定义类加载器
自定义类加载器的方法:继承ClassLoader 类,重写findClass()方法。
10、描述 Java 类加载器的工作原理及其组织结构。
Java 类加载器的作用就是在运行时加载类。
Java 类加载器基于三个机制:委托性、可见性和单一性。
委托性
委托机制是指双亲委派模型。当一个类加载和初始化的时候,类仅在有需
要加载的时候被加载。假设你有一个应用需要的类叫作 Abc.class,首先加载这 个类的请求由 Application 类加载器委托给它的父类加载器 Extension 类加载器, 然后再委托给 Bootstrap 类加载器。Bootstrap 类加载器 会先看看 rt.jar 中有没有 这个类,因为并没有这个类,所以这个请求又回到 Extension 类加载器,它会查 看 jre/lib/ext 目录下有没有这个类,如果这个类被 Extension 类加载器找到了, 那么它将被加载,而 Application 类加载器不会加载这个类;而如果这个类没有 被 Extension 类加载器找到,那么再由 Application 类加载器从 classpath 中寻找, 如果没找到,就会抛出异常。双亲委托机制的优点,就是能够提高软件系统的安全性。因为在此机制下, 用户自定义的类加载器不可能加载本应该由父加载器加载的可靠类,从而防止 不可靠的恶意代码代替由父类加载器加载的可靠代码。如,java.lang.Object 类总 是由根类加载器加载的,其他任何用户自定义的类加载器都不可能加载含有恶 意代码的 java.lang.Object 类。
可见性
可见性原理是子类的加载器可以看见所有的父类加载器加载的类,而父类 加载器看不到子类加载器加载的类。
单一性
单一性原理是指仅加载一个类一次,这是由委托机制确保子类加载器不会 再次加载父类加载器加载过的类。
11、Vector、ArrayList的扩容
默认初始容量都是10
Vector的扩容(2倍)
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
capacityIncrement : oldCapacity);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
elementData = Arrays.copyOf(elementData, newCapacity);
}
Vector的扩容为2倍。
ArrayList的扩容(1.5倍)
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
elementData = Arrays.copyOf(elementData, newCapacity);
}