Java开发校招面试考点汇总第二篇:java SE(二)/java集合、java线程

53.● 请说明Collection 和 Collections的区别。
54.Vector、HashTable、StringBuffer的线程安全。
55.● 请说说快速失败(fail-fast)和安全失败(fail-safe)的区别?****(还是不太懂)
56.● 请简单说明一下什么是迭代器?
57.● 请你说说Iterator和ListIterator的区别?
58、● 请解释为什么集合类没有实现Cloneable和Serializable接口?
59、● 请你说明一下ConcurrentHashMap的原理?
60、● 请你说明concurrenthashmap有什么优势以及1.7和1.8区别?
61、● 请解释一下TreeMap
62、红黑树
63、B/B+/B*树
64.● 请你简单介绍一下ArrayList和LinkedList的区别,并说明如果一直在list的尾部添加元素,用哪种方式的效率高?
65.● 如果hashMap的key是一个自定义的类,怎么办?
66.● 请你解释一下hashMap具体如何实现的?
67、● 如何保证线程安全?
68、● 请你简要说明一下线程的基本状态以及状态之间的关系?
69、● 请你解释一下什么是线程池(thread pool)?
70.关于同步、异步与阻塞、非阻塞的理解
71、● 请介绍一下线程同步和线程调度的相关方法。
72、● 请问当一个线程进入一个对象的synchronized方法A之后,其它线程是否可进入此对象的synchronized方法B?
73、● 请简述一下线程的sleep()方法和yield()方法有什么区别?
74、● 请回答以下几个问题: 第一个 问题:Java中有几种方法可以实现一个线程? 第二个问题:用什么关键字修饰同步方法? 第三个问题:stop()和suspend()方法为何不推荐使用,请说明原因?
75、● 多线程同步有几种实现方法?
76、● 请说出你所知道的线程同步的方法
77、● 启动一个线程是用run()还是start()?
78、● 请使用内部类实现线程设计4个线程,其中两个线程每次对j增加1,另外两个线程对j每次减少1。
79、● 请说明一下线程中的同步和异步有何异同?并且请举例说明在什么情况下会使用到同步和异步?
80、● 请说明一下sleep() 和 wait() 有什么区别?
81、● 请你说明一下在监视器(Monitor)内部,是如何做到线程同步的?在程序又应该做哪种级别的同步呢? (不太懂)
82、● 请分析一下同步方法和同步代码块的区别是什么?
83、● 请详细描述一下线程从创建到死亡的几种状态都有哪些?
84、● 请解释一下Java多线程回调是什么意思?
85、请列举一下启动线程有哪几种方式
86、线程池的种类都有哪些?并且详细描述一下线程池的实现过程
87、● 请简要说明一下JAVA中cyclicbarrier和countdownlatch的区别分别是什么?
88、● 请说明一下线程池有什么优势?
89、● 请列举一下创建线程的方法,并简要说明一下在这些方法中哪个方法更好,原因是什么?
90、● 请简短说明一下你对AQS的理解。
91、● 请简述一下实现多线程同步的方法?
92、● 如何在线程安全的情况下实现一个计数器?
93、● 多线程中的i++线程安全吗?请简述一下原因?
94、请简述一下线程池的运行流程,使用参数以及方法策略等
95、● 线程,进程,然后线程创建有很大开销,怎么优化?
96、jion()方法的作用是什么
97、hashmap/hashtable、ArrayList/Vector/LinkedList的区别

53.● 请说明Collection 和 Collections的区别。
Collection是集合类的上级接口,继承与他的接口主要有Set 和List和Queue.
Collections是针对集合类的一个帮助类,他提供一系列静态方法实现对各种集合的搜索、排序、线程安全化等操作。
同样的,Arrays也是帮助类

54.Vector、HashTable、StringBuffer的线程安全。
都是通过 synchronized 关键字来保证其线程安全。

55.● 请说说快速失败(fail-fast)和安全失败(fail-safe)的区别?
HashMap的迭代器(Iterator)是fail-fast迭代器(快速失败),而Hashtable的enumerator迭代器。
一:快速失败(fail—fast)
在用迭代器遍历一个集合对象时,如果遍历过程中对集合对象的内容进行了修改(增加、删除、修改),则会抛出Concurrent Modification Exception。

原理:迭代器在遍历时直接访问集合中的内容,并且在遍历过程中使用一个 modCount 变量。集合在被遍历期间如果内容发生变化,就会改变modCount的值。每当迭代器使用hashNext()/next()遍历下一个元素之前,都会检测modCount变量是否为expectedmodCount值,是的话就返回遍历;否则抛出异常,终止遍历。

注意:这里异常的抛出条件是检测到 modCount!=expectedmodCount 这个条件。如果集合发生变化时修改modCount值刚好又设置为了expectedmodCount值,则异常不会抛出。因此,不能依赖于这个异常是否抛出而进行并发操作的编程,这个异常只建议用于检测并发修改的bug。
场景:java.util包下的集合类都是快速失败的不能在多线程下发生并发修改(迭代过程中被修改)。
总而言之,快速失败就是直接在原集合上遍历,如果遍历的过程中,原集合发生改变,则出现异常。

二:安全失败(fail—safe)
采用安全失败机制的集合容器,在遍历时不是直接在集合内容上访问的,而是先复制原有集合内容,在拷贝的集合上进行遍历。

原理:由于迭代时是对原集合的拷贝进行遍历,所以在遍历过程中对原集合所作的修改并不能被迭代器检测到,所以不会触发Concurrent Modification Exception。

缺点:基于拷贝内容的优点是避免了Concurrent Modification Exception,但同样地,迭代器并不能访问到修改后的内容,即:迭代器遍历的是开始遍历那一刻拿到的集合拷贝,在遍历期间原集合发生的修改迭代器是不知道的。

场景:java.util.concurrent包下的容器都是安全失败,可以在多线程下并发使用,并发修改。如ConcurrentHashMap。
总而言之,安全失败就是不在原集合上遍历了,而是在拷贝的集合上遍历,这样如果其他线程对原集合进行了修改,也不会影响到已经拷贝的集合。

56.● 请简单说明一下什么是迭代器?
迭代器(Iterator)是一个对象,它的工作是遍历并选择序列中的对象。它提供了一种访问一个容器对象中的各个元素,而又不必暴露该对象内部细节的方法。
Collection接口实现Iterable接口, 所以所有集合都可以使用迭代器。

通过实现Iterable接口中iterator()方法返回Iterator接口的实例, 然后对集合的元素进行迭代操作.
其中有hasNext()、remove()方法。

57.● 请你说说Iterator和ListIterator的区别?

1、Iterator可用来遍历Set和List集合,但是ListIterator只能用来遍历List。
2、Iterator对集合只能是前向遍历,ListIterator既可以前向也可以后向。
3、ListIterator实现了Iterator接口,并包含其他的功能,比如:增加元素,替换元素,获取前一个和后一个元素的索引,等等。

58、● 请解释为什么集合类没有实现Cloneable和Serializable接口?
克隆(cloning)或者是序列化(serialization)的语义和含义是跟具体的实现相关的。因此,应该由集合类的具体实现来决定如何被克隆或者是序列化。
实现Serializable序列化的作用:将对象的状态保存在存储媒体中以便可以在以后重写创建出完全相同的副本;按值将对象从一个从一个应用程序域发向另一个应用程序域。
实现 Serializable接口的作用就是可以把对象存到字节流,然后可以恢复。所以你想如果你的对象没有序列化,怎么才能进行网络传输呢?要网络传输就得转为字节流,所以在分布式应用中,你就得实现序列化。如果你不需要分布式应用,那就没必要实现实现序列化。

59、● 请你说明一下ConcurrentHashMap的原理?
ConcurrentHashMap 类中包含两个静态内部类 HashEntry 和 Segment。HashEntry 用来封装映射表的键 / 值对;Segment 用来充当锁的角色,每个 Segment是由若干个 HashEntry 对象链接起来的链表。一个 ConcurrentHashMap 实例中包含由若干个 Segment 对象组成的数组。

static final class HashEntry {
       final K key;                       // 声明 key 为 final 型
       final int hash;                   // 声明 hash 值为 final 型
       volatile V value;                 // 声明 value 为 volatile 型
       final HashEntry next;      // 声明 next 为 final 型
  
       HashEntry(K key, int hash, HashEntry next, V value) {
           this.key = key;
           this.hash = hash;
           this.next = next;
           this.value = value;
       }
}

在ConcurrentHashMap 中,在散列时如果产生“碰撞”,将采用“分离链接法”来处理“碰撞”:把“碰撞”的 HashEntry 对象链接成一个链表。由于 HashEntry 的 next 域为 final 型,所以新节点只能在链表的表头处插入。 下图是在一个空桶中依次插入 A,B,C 三个 HashEntry 对象后的结构图:

Segment 类继承于 ReentrantLock 类,从而使得 Segment 对象能充当锁的角色。每个 Segment 对象用来守护其(成员对象 table 中)包含的若干个HashEntry 。

60、● 请你说明concurrenthashmap有什么优势以及1.7和1.8区别?
Concurrenthashmap线程安全的,1.7是在jdk1.7中采用Segment + HashEntry的方式进行实现的,lock加在Segment上面。1.7size计算是先采用不加锁的方式,连续计算元素的个数,最多计算3次:1、如果前后两次计算结果相同,则说明计算出来的元素个数是准确的;2、如果前后两次计算结果都不同,则给每个Segment进行加锁,再计算一次元素的个数;

1.8中放弃了Segment臃肿的设计,取而代之的是采用Node + CAS + Synchronized来保证并发安全进行实现,1.8中使用一个volatile类型的变量baseCount记录元素的个数,当插入新数据或则删除数据时,会通过addCount()方法更新baseCount,通过累加baseCount和CounterCell数组中的数量,即可得到元素的总个数;

61、● 请解释一下TreeMap
TreeMap是一个有序的key-value集合,基于红黑树(Red-Black tree)的 NavigableMap实现。该映射根据其键的自然顺序进行排序,或者根据创建映射时提供的 Comparator进行排序,具体取决于使用的构造方法。

62、红黑树
由于平衡二叉树的插入消耗太大,所以引入了红黑树。
红黑树是一种近似平衡的二叉查找树,它能够确保任何一个节点的左右子树的高度差不会超过二者中较低那个的一陪。
https://blog.csdn.net/u014138443/article/details/89739181

63、B/B+/B*树
https://blog.csdn.net/u014138443/article/details/89741129

64.● 请你简单介绍一下ArrayList和LinkedList的区别,并说明如果一直在list的尾部添加元素,用哪种方式的效率高?
ArrayList是用数组实现的,存储在连续空间
LinkedList是用双向链表实现的。

当输入的数据一直是小于千万级别的时候,大部分是LinkedList效率高,而当数据量大于千万级别的时候,就会出现ArrayList的效率比较高了。为什么呢?

原来 LinkedList每次增加的时候,会new 一个Node对象来存新增加的元素,所以当数据量小的时候,这个时间并不明显,而ArrayList需要扩容,所以LinkedList的效率就会比较高,其中如果ArrayList出现不需要扩容的时候,那么ArrayList的效率应该是比LinkedList高的,当数据量很大的时候,new对象的时间大于扩容的时间,那么就会出现ArrayList的效率比LinkedList高了。

65.● 如果hashMap的key是一个自定义的类,怎么办?
使用HashMap,如果key是自定义的类,为了避免key值重复,就必须重写hashcode()和equals()。(详情看问题15)

66.● 请你解释一下hashMap具体如何实现的?
Hashmap基于数组链表实现的,通过对key的hashcode & 数组的长度得到在数组中位置,如当前数组有元素,则数组当前元素next指向要插入的元素,这样来解决hash冲突的,形成了拉链式的结构。
需要注意的是,HashMap在JDK1.8的版本中引入了红黑树结构做优化,当链表元素个数大于等于8时,链表转换成树结构;

67、● 如何保证线程安全?
(JVM390页,还未看完)
如果一个对象可以安全的被多个线程同时使用,那它就是线程安全的。
通过合理的时间调度,避开共享资源的存取冲突。另外,在并行任务设计上可以通过适当的策略,保证任务与任务之间不存在共享资源,设计一个规则来保证一个客户的计算工作和数据访问只会被一个线程或一台工作机完成,而不是把一个客户的计算工作分配给多个线程去完成。

68、● 请你简要说明一下线程的基本状态以及状态之间的关系?

其中Running表示运行状态,Runnable表示就绪状态(万事俱备,只欠CPU),Blocked表示阻塞状态,阻塞状态又有多种情况,可能是因为调用wait()方法进入等待池,也可能是执行同步方法或同步代码块进入等锁池,或者是调用了sleep()方法或join()方法等待休眠或其他线程结束,或是因为发生了I/O中断。
因为线程不占有资源,所以线程没有挂起状态。

69、● 请你解释一下什么是线程池(thread pool)?

**在面向对象编程中,创建和销毁对象是很费时间的,因为创建一个对象要获取内存资源或者其它更多资源。**在Java中更是如此,虚拟机将试图跟踪每一个对象,以便能够在对象销毁后进行垃圾回收。所以提高服务程序效率的一个手段就是尽可能减少创建和销毁对象的次数,特别是一些很耗资源的对象创建和销毁,这就是”池化资源”技术产生的原因。线程池顾名思义就是事先创建若干个可执行的线程放入一个池(容器)中,需要的时候从池中获取线程不用自行创建,使用完毕不需要销毁线程而是放回池中,从而减少创建和销毁线程对象的开销。

Java 5+中的Executor接口定义一个执行线程的工具。它的子类型即线程池接口是ExecutorService。要配置一个线程池是比较复杂的,尤其是对于线程池的原理不是很清楚的情况下,因此在工具类Executors面提供了一些静态工厂方法,生成一些常用的线程池,如下所示:

  • newSingleThreadExecutor:创建一个单线程的线程池。这个线程池只有一个线程在工作,也就是相当于单线程串行执行所有任务。**如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它。**此线程池保证所有任务的执行顺序按照任务的提交顺序执行。
  • newFixedThreadPool:创建固定大小的线程池。每次提交一个任务就创建一个线程,直到线程达到线程池的最大大小。线程池的大小一旦达到最大值就会保持不变,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程。
  • newCachedThreadPool:创建一个可缓存的线程池。如果线程池的大小超过了处理任务所需要的线程,那么就会回收部分空闲(60秒不执行任务)的线程,当任务数增加时,此线程池又可以智能的添加新线程来处理任务。此线程池不会对线程池大小做限制,线程池大小完全依赖于操作系统(或者说JVM)能够创建的最大线程大小。
  • newScheduledThreadPool:创建一个大小无限的线程池。此线程池支持定时以及周期性执行任务的需求。

70.关于同步、异步与阻塞、非阻塞的理解
https://www.zhihu.com/question/19732473/answer/20851256
https://www.zhihu.com/question/19732473/answer/23434554
“阻塞”与"非阻塞"与"同步"与“异步"不能简单的从字面理解,提供一个从分布式系统角度的回
1.同步与异步
同步和异步关注的是消息通信机制 (synchronous communication/ asynchronous communication)所谓同步,就是在发出一个调用时,在没有得到结果之前,该调用就不返回。但是一旦调用返回,就得到返回值了。换句话说,就是由调用者主动等待这个调用的结果。而异步则是相反,调用在发出之后,这个调用就直接返回了,所以没有返回结果。换句话说,当一个异步过程调用发出后,调用者不会立刻得到结果。而是在调用发出后,被调用者通过状态、通知来通知调用者,或通过回调函数处理这个调用。

2、 阻塞与非阻塞
阻塞和非阻塞关注的是程序在等待调用结果(消息,返回值)时的状态.阻塞调用是指调用结果返回之前,当前线程会被挂起。调用线程只有在得到结果之后才会返回。非阻塞调用指在不能立刻得到结果之前,该调用不会阻塞当前线程。还是上面的例子,你打电话问书店老板有没有《分布式系统》这本书,你如果是阻塞式调用,你会一直把自己“挂起”,直到得到这本书有没有的结果,如果是非阻塞式调用,你不管老板有没有告诉你,你自己先一边去玩了, 当然你也要偶尔过几分钟check一下老板有没有返回结果。在这里阻塞与非阻塞与是否同步异步无关。跟老板通过什么方式回答你结果无关

老张爱喝茶,废话不说,煮开水。
出场人物:老张,水壶两把(普通水壶,简称水壶;会响的水壶,简称响水壶)。
1 老张把水壶放到火上,立等水开。(同步阻塞)
老张觉得自己有点傻
2 老张把水壶放到火上,去客厅看电视,时不时去厨房看看水开没有。(同步非阻塞)
老张还是觉得自己有点傻,于是变高端了,买了把会响笛的那种水壶。水开之后,能大声发出嘀~~~~的噪音。
3 老张把响水壶放到火上,立等水开。(异步阻塞)
老张觉得这样傻等意义不大
4 老张把响水壶放到火上,去客厅看电视,水壶响之前不再去看它了,响了再去拿壶。(异步非阻塞)
老张觉得自己聪明了。
所谓同步异步,只是对于水壶而言。
普通水壶,同步;响水壶,异步。
虽然都能干活,但响水壶可以在自己完工之后,提示老张水开了。这是普通水壶所不能及的。同步只能让调用者去轮询自己(情况2中),造成老张效率的低下。
所谓阻塞非阻塞,仅仅对于老张而言。
立等的老张,阻塞;看电视的老张,非阻塞。
情况1和情况3中老张就是阻塞的,媳妇喊他都不知道。虽然3中响水壶是异步的,可对于立等的老张没有太大的意义。所以一般异步是配合非阻塞使用的,这样才能发挥异步的效用。

71、● 请介绍一下线程同步和线程调度的相关方法。

  • wait():使一个线程处于等待(阻塞)状态,并且释放所持有的对象的锁;
  • sleep():使一个正在运行的线程处于睡眠状态,是一个静态方法,调用此方法要处理InterruptedException异常;
  • notify():唤醒一个处于等待状态的线程,当然在调用此方法的时候,并不能确切的唤醒某一个等待状态的线程,而是由JVM确定唤醒哪个线程,而且与优先级无关;
  • notityAll():唤醒所有处于等待状态的线程,该方法并不是将对象的锁给所有线程,而是让它们竞争,只有获得锁的线程才能进入就绪状态;

72、● 请问当一个线程进入一个对象的synchronized方法A之后,其它线程是否可进入此对象的synchronized方法B?

不能。其它线程只能访问该对象的非同步方法,同步方法则不能进入。因为非静态方法上的synchronized修饰符要求执行方法时要获得对象的锁,如果已经进入A方法说明对象锁已经被取走,那么试图进入B方法的线程就只能在等锁池(注意不是等待池哦)中等待对象的锁。

73、● 请简述一下线程的sleep()方法和yield()方法有什么区别?
①sleep()方法给其他线程运行机会时不考虑线程的优先级,因此会给低优先级的线程以运行的机会;yield()方法只会给相同优先级或更高优先级的线程以运行的机会;
② 线程执行sleep()方法后转入阻塞(blocked)状态,而执行yield()方法后转入就绪(ready)状态;
③ sleep()方法声明抛出InterruptedException,而yield()方法没有声明任何异常;
④ sleep()方法比yield()方法(跟操作系统CPU调度相关)具有更好的可移植性。
注意:sleep方法和yield 方法都不释放锁,wait方法会释放锁(看问题80)

74、● 请回答以下几个问题: 第一个 问题:Java中有几种方法可以实现一个线程? 第二个问题:用什么关键字修饰同步方法? 第三个问题:stop()和suspend()方法为何不推荐使用,请说明原因?
一、有三种实现方法,分别是1、继承Thread类,重写run()方法(Thread类其实也是实现Runnable接口的一个实例)。2、实现Runnable接口,重写run()方法。3、实现Callable接口,重写call()方法。实现Callable接口的好处是当任务结束后可以提供一个返回值。

二、用synchronized关键字修饰同步方法。
三、反对使用stop(),是因为它不安全。它会解除由线程获取的所有锁定。假如一个线程正在执行:synchronized void { x = 3; y = 4;} 由于方法是同步的,多个线程访问时总能保证x,y被同时赋值,而如果一个线程正在执行到x = 3;时,被调用了 stop()方法,即使在同步块中,它也干脆地stop了,这样就产生了不完整的残废数据。
suspend()方法容易发生死锁。调用suspend()的时候,目标线程会停下来,但却仍然持有在这之前获得的锁定。此时,其他任何线程都不能访问锁定的资源,除非被”挂起”的线程恢复运行。
现在不建议使用stop和suspend方法终止线程,建议设置一个flag标志来控制循环是否继续执行,通过这种方式让线程离开run()方法,从而终止线程。

75、● 多线程同步有几种实现方法?

同步的实现方面有三种,分别是synchronized,wait与notify,Lock。

76、● 请说出你所知道的线程同步的方法
wait():使一个线程处于等待状态,并且释放所持有的对象的lock。
sleep():使一个正在运行的线程处于睡眠状态,是一个静态方法,调用此方法要捕捉InterruptedException异常。
notify():唤醒一个处于等待状态的线程,注意的是在调用此方法的时候,并不能确切的唤醒某一个等待状态的线程,而是由JVM确定唤醒哪个线程,而且不是按优先级。
Allnotity():唤醒所有处入等待状态的线程,注意并不是给所有唤醒线程一个对象的锁,而是让它们竞争。

77、● 启动一个线程是用run()还是start()?
启动一个线程是调用start()方法,使线程所代表的虚拟处理机处于可运行状态,这意味着它可以由JVM调度并执行。这并不意味着线程就会立即运行,而是等到合适的时机JVM才执行。
run()方法只是类的一个普通方法而已,如果直接调用Run方法,程序中依然只有主线程这一个线程,其程序执行路径还是只有一条,还是要顺序执行,还是要等待run方法体执行完毕后才可继续执行下面的代码,这样就没有达到写线程的目的。

总结:调用start方法方可启动线程,而run方法只是thread的一个普通方法调用,如果调用run()方法,则依然只有主线程一个线程。

78、● 请使用内部类实现线程设计4个线程,其中两个线程每次对j增加1,另外两个线程对j每次减少1。

public class ThreadTest1 {
	private int j;

	public static void main(String args[]) {
		ThreadTest1 tt = new ThreadTest1();
		Inc inc = tt.new Inc();
		Dec dec = tt.new Dec();
		for (int i = 0; i < 2; i++) {
			Thread t = new Thread(inc);
			t.start();
			t = new Thread(dec);
			t.start();
		}
	}

	private synchronized void inc() {  //多线程同步
		j++;
		System.out.println(Thread.currentThread().getName() + "-inc:" + j);
	}

	private synchronized void dec() {//多线程同步
		j--;
		System.out.println(Thread.currentThread().getName() + "-dec:" + j);
	}

	class Inc implements Runnable {//多线程
		public void run() {
			for (int i = 0; i < 100; i++) {
				inc();
			}
		}
	}

	class Dec implements Runnable {//多线程
		public void run() {
			for (int i = 0; i < 100; i++) {
				dec();
			}
		}
	}
}

79、● 请说明一下线程中的同步和异步有何异同?并且请举例说明在什么情况下会使用到同步和异步?
同步方法调用一旦开始,调用者必须等到方法调用返回后,才能继续后续的行为。
异步方法调用更像一个消息传递,一旦开始,方法调用就会立即返回,调用者就可以继续后续的操作。而,异步方法通常会在另外一个线程中,“真实”地执行着。整个过程,不会阻碍调用者的工作。

如果数据将在线程间共享。例如正在写的数据以后可能被另一个线程读到,或者正在读的数据可能已经被另一个线程写过了,那么这些数据就是共享数据,必须进行同步存取。
当应用程序在对象上调用了一个需要花费很长时间来执行的方法,并且不希望让程序等待方法的返回时,就应该使用异步编程,在很多情况下采用异步途径往往更有效率。

80、● 请说明一下sleep() 和 wait() 有什么区别?
sleep是线程类(Thread)的方法,导致此线程暂停执行指定时间,把执行机会给其他线程,但是监控状态依然保持,到时后会自动恢复调用sleep不会释放对象锁

wait是Object类的方法,对此对象调用wait方法导致本线程放弃对象锁,进入等待此对象的等待锁定池,只有针对此对象发出notify方法(或notifyAll)后本线程才进入对象锁定池准备获得对象锁进入运行状态。

因为sleep不会释放锁,所以容易导致死锁问题的发生,建议使用wait()方法。

sleep/suspend/yield不释放锁
stop/wait释放锁

81、● 请你说明一下在监视器(Monitor)内部,是如何做到线程同步的?在程序又应该做哪种级别的同步呢? (不太懂)
监视器和锁在Java虚拟机中是一块使用的。监视器监视一块同步代码块,确保一次只有一个线程执行同步代码块。每一个监视器都和一个对象引用相关联。线程在获取锁之前不允许执行同步代码。

82、● 请分析一下同步方法和同步代码块的区别是什么?
区别:
同步方法使用当前实例对象或者类作为锁, synchronized可以加在同步方法上。
同步代码块可以选择以什么来加锁,比同步方法要更细颗粒度,我们可以选择只同步会发生同步问题的部分代码而不是整个方法。

83、● 请详细描述一下线程从创建到死亡的几种状态都有哪些?

  1. 新建( new ):新创建了一个线程对象。
  2. 可运行( runnable ):线程对象创建后,其他线程(比如 main 线程)调用了该对象 的 start ()方法。该状态的线程位于可运行线程池中,等待被线程调度选中,获 取 cpu 的使用权 。
  3. 运行( running ):可运行状态( runnable )的线程获得了 cpu 时间片( timeslice ) ,执行程序代码。
  4. 阻塞( block ):阻塞状态是指线程因为某种原因放弃了 cpu 使用权,也即让出了 cpu timeslice ,暂时停止运行。直到线程进入可运行( runnable )状态,才有 机会再次获得 cpu timeslice 转到运行( running )状态。阻塞的情况分三种:
    (一). 等待阻塞:运行( running )的线程执行 o . wait ()方法, JVM 会把该线程放 入等待队列( waitting queue )中。
    (二). 同步阻塞:运行( running )的线程在获取对象的同步锁时,若该同步锁 被别的线程占用,则 JVM 会把该线程放入锁池( lock pool )中。
    (三). 其他阻塞: 运行( running )的线程执行 Thread . sleep ( long ms )或 t . join ()方法,或者发出了 I / O 请求时, JVM 会把该线程置为阻塞状态。 当 sleep ()状态超时、 join ()等待线程终止或者超时、或者 I / O 处理完毕时,线程重新转入可运行( runnable )状态。
  5. 死亡( dead ):线程 run ()、 main () 方法执行结束,或者因异常退出了 run ()方法,则该线程结束生命周期。死亡的线程不可再次复生。

84、● 请解释一下Java多线程回调是什么意思?
所谓回调,就是客户程序C调用服务程序S中的某个方法A,然后方法A又在某个时候反过来调用C中的某个方法B,对于C来说,这个B便叫做回调方法。

85、请列举一下启动线程有哪几种方式

一、继承Thread类创建线程类

(1)定义Thread类的子类,并重写该类的run方法,该run方法的方法体就代表了线程要完成的任务。因此把run()方法称为执行体。

(2)创建Thread子类的实例,即创建了线程对象。

(3)调用线程对象的start()方法来启动该线程。

package com.thread;
public class FirstThreadTest extends Thread{
    int i = 0;
    //重写run方法,run方法的方法体就是现场执行体
    public void run()
    {
        for(;i<100;i++){
        System.out.println(getName()+"  "+i);
         
        }
    }
    public static void main(String[] args)
    {
        for(int i = 0;i< 100;i++)
        {
            System.out.println(Thread.currentThread().getName()+"  : "+i);
            if(i==20)
            {
                new FirstThreadTest().start();
                new FirstThreadTest().start();
            }
        }
    }
  
}

二、通过Runnable接口创建线程类

(1)定义runnable接口的实现类,并重写该接口的run()方法,该run()方法的方法体同样是该线程的线程执行体。

(2)创建 Runnable实现类的实例,并依此实例作为Thread的target来创建Thread对象,该Thread对象才是真正的线程对象。

(3)调用线程对象的start()方法来启动该线程。

package com.thread;
  
public class RunnableThreadTest implements Runnable
{
  
    private int i;
    public void run()
    {
        for(i = 0;i <100;i++)
        {
            System.out.println(Thread.currentThread().getName()+" "+i);
        }
    }
    public static void main(String[] args)
    {
        for(int i = 0;i < 100;i++)
        {
            System.out.println(Thread.currentThread().getName()+" "+i);
            if(i==20)
            {
                RunnableThreadTest rtt = new RunnableThreadTest();
                new Thread(rtt,"新线程1").start();
                new Thread(rtt,"新线程2").start();
            }
        }
  
    }
  
}

三、通过Callable和Future创建线程

(1)创建Callable接口的实现类,并实现call()方法,该call()方法将作为线程执行体,并且有返回值。

(2)创建Callable实现类的实例,使用FutureTask类来包装Callable对象,该FutureTask对象封装了该Callable对象的call()方法的返回值。

(3)使用FutureTask对象作为Thread对象的target创建并启动新线程。

(4)调用FutureTask对象的get()方法来获得子线程执行结束后的返回值

package com.thread;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
  
public class CallableThreadTest implements Callable
{
  
    public static void main(String[] args)
    {
        CallableThreadTest ctt = new CallableThreadTest();
        FutureTask ft = new FutureTask<>(ctt);
        for(int i = 0;i < 100;i++)
        {
            System.out.println(Thread.currentThread().getName()+" 的循环变量i的值"+i);
            if(i==20)
            {
                new Thread(ft,"有返回值的线程").start();
            }
        }
        try
        {
            System.out.println("子线程的返回值:"+ft.get());
        } catch (InterruptedException e)
        {
            e.printStackTrace();
        } catch (ExecutionException e)
        {
            e.printStackTrace();
        }
  
    }
  
    @Override
    public Integer call() throws Exception
    {
        int i = 0;
        for(;i<100;i++)
        {
            System.out.println(Thread.currentThread().getName()+" "+i);
        }
        return i;
    }
  
}

86、线程池的种类都有哪些?并且详细描述一下线程池的实现过程
1、newFixedThreadPool创建一个指定工作线程数量的线程池。每当提交一个任务就创建一个工作线程,如果工作线程数量达到线程池初始的最大数,则将提交的任务存入到池队列中。
2、newCachedThreadPool创建一个可缓存的线程池。这种类型的线程池特点是:
1).工作线程的创建数量几乎没有限制(其实也有限制的,数目为Interger. MAX_VALUE), 这样可灵活的往线程池中添加线程。
2).如果长时间没有往线程池中提交任务,即如果工作线程空闲了指定的时间(默认为1分钟),则该工作线程将自动终止。终止后,如果你又提交了新的任务,则线程池重新创建一个工作线程。
3、newSingleThreadExecutor创建一个单线程化的Executor,即只创建唯一的工作者线程来执行任务,如果这个线程异常结束,会有另一个取代它,保证顺序执行(我觉得这点是它的特色)。单工作线程最大的特点是可保证顺序地执行各个任务,并且在任意给定的时间不会有多个线程是活动的 。
4、newScheduleThreadPool创建一个定长的线程池,而且支持定时的以及周期性的任务执行,类似于Timer。(这种线程池原理暂还没完全了解透彻)

87、● 请简要说明一下JAVA中cyclicbarrier和countdownlatch的区别分别是什么?(没太看)
CountDownLatch和CyclicBarrier都能够实现线程之间的等待,只不过它们侧重点不同:

CountDownLatch一般用于某个线程A等待若干个其他线程执行完任务之后,它才执行;

而CyclicBarrier一般用于一组线程互相等待至某个状态,然后这一组线程再同时执行;

另外,CountDownLatch是不能够重用的,而CyclicBarrier是可以重用的。

88、● 请说明一下线程池有什么优势?
第一:降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。

第二:提高响应速度。当任务到达时,任务可以不需要等到线程创建就能执行。

第三:提高线程的可管理性,线程是稀缺资源,如果无限制地创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一分配、调优和监控。

89、● 请列举一下创建线程的方法,并简要说明一下在这些方法中哪个方法更好,原因是什么?
需要从Java.lang.Thread类派生一个新的线程类,重载它的run()方法;

实现Runnalbe接口,重载Runnalbe接口中的run()方法。

实现Runnalbe接口更好,使用实现Runnable接口的方式创建的线程可以处理同一资源,从而实现资源的共享.另外,java开发人员认为,一个类仅在他们需要被加强时才会被修改,因此,如果没有必要重写Thread中的其他方法,那么尽量不要继承。

90、● 请简短说明一下你对AQS的理解。
https://www.cnblogs.com/waterystone/p/4920797.html
1、AQS(AbstractQueuedSynchronizer)其实就是一个可以给我们实现锁的框架

2、内部实现的关键是:先进先出的队列、state状态,state是volatile int类型的。根据state的状态,程序知道是否可以获取锁或者释放锁。

3、拥有两种线程模式独占模式(只有一个线程能执行,如ReentrantLock)和共享模式(共享,多个线程可同时执行,如Semaphore/CountDownLatch)。

91、● 请简述一下实现多线程同步的方法?
1、synchronized
2、wait()方法与notify()方法。
3、Lock

92、● 如何在线程安全的情况下实现一个计数器?
可以使用加锁,比如synchronized或者lock。也可以使用Concurrent包下的原子类。

93、● 多线程中的i++线程安全吗?请简述一下原因?
不安全。i++不是原子性操作。i++分为读取i值,对i值加一,再赋值给i++,执行期中任何一步都是有可能被其他线程抢占的。

94、请简述一下线程池的运行流程,使用参数以及方法策略等
线程池主要就是指定线程池核心线程数大小,最大线程数,存储的队列,拒绝策略,空闲线程存活时长。当需要任务大于核心线程数时候,就开始把任务往存储任务的队列里,当存储队列满了的话,就开始增加线程池创建的线程数量,如果当线程数量也达到了最大,就开始执行拒绝策略,比如说记录日志,直接丢弃,或者丢弃最老的任务。

95、● 线程,进程,然后线程创建有很大开销,怎么优化?
可以使用线程池。

96、jion()方法的作用是什么
A线程中调用t.join(),则阻塞A线程的运行,直到t线程运行完毕,再运行A线程中t.join()之后的代码。

97、hashmap/hashtable、ArrayList/Vector/LinkedList的区别
https://blog.csdn.net/u014138443/article/details/89714471

你可能感兴趣的:(java基础)