Java并发编程73道面试题快速一览 -
ThreadLocal
会产生内存泄漏
原因 value为null Thread Ref -> Thread -> ThreaLocalMap -> Entry -> value永远无法回收,造成内存泄漏。
ThreadLocalMap的设计中已经考虑到这种情况,也加上了一些防护措施:在ThreadLocal的get(),set(),remove()的时候都会清除线程ThreadLocalMap里所有key为null的value。
软引用:soft,内存不够时,为了避免oom问题,会回收软引用对应的对象。内存不足
弱引用:weak,无论内存是否足够,只要发生了垃圾回收,就会进行回收 发生GC
那么怎么避免内存泄漏呢?
每次使用完ThreadLocal,都调用它的remove()方法,清除数据。
Intel引入超线程技术后,使核心数与线程数形成1:2的关系
CPU时间片轮转机制
并发的时候一定要加个单位时间
高并发编程的意义
充分利用CPU的资源
加快响应用户的时间
注意事项
安全性
死锁
太多线程会将服务器资源耗尽形成宕机
调用yield() 、sleep()、wait()、notify()等方法对锁有何影响?
yield:让出时间片,不会释放锁
sleep:线程进入睡眠状态,不会释放锁
wait:调动方法之前,必须要持有锁。调用了wait()方法以后,锁就会被释放,进入锁的等待队列,方法返回后重新拿到锁
notify:调动方法之前,必须要持有锁,调用notify()方法本身不会释放锁的。而是通知等待队列中的某一个线程,同步代码块执行完毕后才会释放锁
因为notify先于wait执行就会发生一直等待的问题。
CAS + volatile变量 可以保证 线程安全
显示锁和隐示锁
隐式地获取锁(synchronized),但是它将锁的获取和释放固化了,也就是先获取锁再释放
显示锁try(lock) finally(unlock)
CLH队列锁
也是一种基于链表的可扩展、高性能、公平的自旋锁,申请线程仅仅在本地变量上自旋,它不断轮询前驱的状态,假设发现前驱释放了锁就结束自旋。
AQS是CLH队列锁的一种变体实现。
SkipList
CallerRunsPolicy
Thread.setDaemon()必须在Thread.start()之前调用,否则运行时会抛出异常。
进程是操作系统分配资源的最小单元,线程是操作系统调度的最小单元。
死锁原因因争夺资源而造成的一种互相等待的现象
活锁:任务或者执行者没有被阻塞,由于某些条件没有满足,导致一直重复尝试、失败、尝试、失败。
活锁和死锁的区别在于,处于活锁的实体是在不断的改变状态,所谓的“活”, 而处于死锁的实体表现为等待;活锁有可能自行解开,死锁则不能。
饥饿:一个或者多个线程因为种种原因无法获得所需要的资源,导致一直无法执行的状态。
Executors 工具类的不同方法按照我们的需求创建了不同的线程池,来满足业务的需求。
Executor 接口对象能执行我们的线程任务。
原子操作是指一个不受其他操作影响的操作
BlockingQueue
当生产者线程试图向BlockingQueue放入元素时,如果队列已满,则线程被阻塞,当消费者线程试图从中取出一个元素时,如果队列为空,则该线程会被阻塞
阻塞队列
ArrayBlockingQueue
LinkedBlockingQueue
PriorityBlockingQueue
SynchronousQueue
LinkedTransferQueue
LinkedBlockingDeque
同步容器和并发容器
synchronized来实现同步的容器,如果有多个线程调用同步容器的方法,它们将会串行执行。Vector,Hashtable加上关键字synchronized。
并发容器使用了与同步容器完全不同的加锁策略来提供更高的并发性和伸缩性,例如在ConcurrentHashMap中采用了一种粒度更细的加锁机制,可以称为分段锁,在这种锁机制下,允许任意数量的读线程并发地访问map
run()和start()
当你调用start()方法时你将创建新的线程,并且执行在run()方法里的代码。
但是如果你直接调用run()方法,它不会创建新的线程也不会执行调用线程的代码,只会把run方法当作普通方法去执行。
SynchronizedMap一次锁住整张表来保证线程安全,所以每次只能有一个线程来访为map。
execute()和submit()
execute()方法的返回类型是void,它定义在Executor接口中。
而submit()方法可以返回持有计算结果的Future对象
阻塞式方法是指程序会一直等待该方法完成期间不做其他事情
线程间的同步方法
当线程间是可以共享资源时,线程间通信是协调它们的重要的手段。Object类中wait()、notify()、notifyAll()方法可以用于线程间通信关于资源的锁的状态。
java.util.Timer是一个工具类,可以用于安排一个线程在未来的某个特定时间执行。