并发编程常见问题(2)

55 阅读4分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第32天,点击查看活动详情

Synchronize和ReentrantLock的区别?

1、synchronize是一个关键字,ReentrantLock是一个类

2、synchronize会自动的加锁和释放锁,ReentrantLock需要手动加锁和释放锁

3、synchronize的底层是jvm层面的锁,ReentrantLock是api层面的锁

4、synchronize是非公平锁,ReentrantLock可以选择公平锁或非公平锁

5、synchronize锁的是对象,锁信息保存在对象头中,ReentrantLock是通过代码中int类型的state标识锁的状态

6、synchronize底层有一个锁升级的过程

ThreadLocal

1、ThreadLocal是java中所提供的线程本地存储机制,可以可用该机制将数据缓存在某个线程内部,该线程可以在任意时刻、任意方法中获取缓存的数据

2、ThreadLocal底层是通过ThreadLocalMap来实现的,每个Thread对象中都存在一个ThreadLocalMap,map的Key为TheadLocal对象,value为需要缓存的对象

3、如果在线程池中使用ThreadLocal会造成内存泄漏,因为ThreadLocal对象使用完之后,应该要把设置的key,value,也就是Entry对象进行回收,但线程池中的线程不会回收,而线程对象是通过强引用指向ThreadLocalMap,ThreadLocalMap也是强引用指向Entry,线程不被回收,Entry对象也就不被回收,从而出现内存泄漏,解决办法是:在使用了ThreadLocal对象之后,手动调用ThreadLocal的remove方法,手动清除Entry对象

4、ThreadLocal经典的应用场景就是连接管理(一个线程持有一个连接,该连接对象可以在不同的方法之间进行传递,线程之间不共享同一个连接)

守护线程

守护线程:为所有非守护线程提供服务的线程;任何一个守护线程都是整个jvm中所有非守护线程的保姆

守护线程的作用是什么?

举例:GC垃圾回收线程:就是一个经典的守护线程,当我们的程序中不在有任何运行的thread,程序就不会再产生垃圾,垃圾回收器也就无事可做,所以当垃圾回收线程是jvm上仅剩的线程时,垃圾回收线程会自动离开。它始终在低级别的状态中运行,用于实时监控和管理系统中的可回收资源

应用场景:(1)来为其它线程提供服务支持的情况;(2)或者在任何情况下,程序结束时,这个线程必须正常且立刻关闭,就可以作为守护线程来使用;反之,如果一个正在执行某个操作的线程必须要正确的关闭掉否则就会出现不好的后果的话,那么这个线程就不是守护线程,而是用户线程。通常都是些关键的事务,比方说,数据库录入或者更新,这些操作都是不能中断的。

线程池线程复用的原理

线程池将线程和任务进行解耦,线程是线程,任务是任务,摆脱了之前通过Thread创建线程时的一个线程必须对应一个任务的限制。

在线程池中,同一个线程可以从阻塞队列中不断获取新任务来执行,其核心原理在于线程池对Thread进行了封装,并不是每次执行任务都会调用Thread.start()来创建新线程,而是让每个线程去执行一个“循环任务”,在这个“循环任务”中不停检查是否有任务需要被执行,如果有则直接执行,也就是调用任务中的run方法,将run方法当成一个普通的方法执行,通过这种方式只使用固定的线程就将所有任务的run方法串联起来。

线程池的生命周期及状态

1、线程通常由五中状态,创建、就绪、运行、阻塞和死亡状态

2、阻塞的情况又分三种:

(1)、等待阻塞:运行的线程执行wait()方法,该线程会释放占用的所有资源,JVM会把该线程放入“等待池”中。进入这个状态后,是不能自动唤醒的,必须依靠其他线程调用notify或notifyAll方法才能被唤醒,wait是object类的方法

(2)、同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入“锁池”中

(3)、其他阻塞:运行的线程执行sleep或join方法,或者发出来I/O请求时,JVM会把该线程设置为阻塞状态,当sleep状态超时,join等待线程终止或超时、或者I/O处理完毕时,线程重新转入就绪状态。sleep是Thread类的方法