目录
十六、说说 JDK1.6 之后的synchronized 关键字底层做了哪些优化,可以详细介绍一下这些优化吗
二十三、synchronzied与ThreadLocal区别
二十四、ReentrantLock与synchronized区别
一、进程与线程区别?
进程:进程是执行程序的一次执行过程。
线程:一个进程可以包括若干个线程,一个进程至少有一个线程。线程是CPU调度和执行的单位。
二、并发与并行的区别
并发:两个或多个事件在同一时间隔发生,发生是统一实体。
并行:两个或多个事件在同一时刻发生,不同实体多个事件。
三、多线程创建的方式
1.继承Thread类,通过start()方法启动,但此时线程处于就绪状态;重写Thread类的run()方法,代表线程已经获取CPU时间片,处于运行状态。
2.实现Runable接口。
3.有返回值返回必须实现Callable接口,通过FutureTask包装器来创建Thread线程;
4.基于线程池的方式,ExecutorService 来创建(推荐使用)。
四、四种线程池
1.newCachedThreadPool
调用execute将重用以前构造的线程,如果现在没有可用的,就创建一个新线程并添加到线程池中,终止并从缓存中移除那些已有60秒未被使用的线程。
2.newFixedThreadPool
创建一个可用的固定线程输的线程池,以无界队列的方式来运行这些线程。
3.newScheduledThreadPool
创建一个线程池,他可安排在给定时间后运行命令后或者定期执行。
4.newSingleThreadExcutor
五、多线程的优势
六、start()方法与run()方法的区别
七、runable()和callable()区别
相同点:都是接口,都可以编写多线程的程序;都采用Thread.start()方法启动线程。
不同点:
runable接口run方法无返回值;callable接口call方法又返回值,是个泛型;
runable接口run方法只能抛出异常,且无法捕获异常并处理;callable接口call允许抛出异常,可以捕获异常信息;
八、线程的生命周期
线程创建之后它将处于 初始状态(NEW),调用 start()
方法后开始运行,线程这时候处于 就绪状态,jvm虚拟机为其创建方法调用栈和程序计数器;
调用run方法,可运行状态的线程获得了 CPU 时间片后就处于 运行状态(RUNNING)
当线程调用同步方法时,在没有获取到锁的情况下,线程将会进入到阻塞状态(BLOCKED)。
注:BLOCKED:线程因某种原因放弃CPU的使用权,也是让出cpu时间片,暂时停止运行;
等待阻塞:(o.wait)等待对列
同步阻塞:lock,线程获取对象的同步锁时,该同步锁被其他的线程占用,则JVM会把该线程放入锁池
其他阻塞:
当线程执行 wait()
方法之后,线程进入 等待状态(WAITING),进入等待状态的线程需要依靠其他线程的通知才能够返回到运行状态【notify()】。 而 超时等待状态(TIME_WAITING)相当于在等待状态的基础上增加了超时限制,【sleep(long millis)/
wait(long millis)】,
当超时时间到达后 Java 线程将会返回到运行状态。
线程在执行 Runnable 的terminated()()
方法之后将会进入到 终止状态(TERMINATED)。
九、线程的基本操作有哪些
1.yield:只会把时间片让给同优先级的线程
2.interrupted:运行中的程序是否被其他线程进行其他操作
十、sleep()与wait()的区别
相同点:两者都可以暂停线程的执行,都会让线程进入等待状态。
sleep()是属于Thread类的静态方法,作用于当前线程;没有释放锁;可以通过超时或者调用interrupt()方法唤醒休眠中的线程。
wait()方法是Object类的实例方法,作用于对象本身;释放了锁;执行wait()方法后,通过调用notify()或notifyAll()方法唤醒等待线程。
十一、解释一下JMM
通过规则控制程序中各个变量在共享数据区域和私有区域的访问方式
主内存:java实例对象,多个线程对同一个变量进行访问可能存在线程安全问题(共享);
工作内存:当前方法的所有本地变量信息(非共享),每个线程私有数据,每个线程无法访问其他工作内存。
十二、什么是线程安全
线程安全是指在堆内存中的数据由于可以被其他的线程进行访问到,在没有限制的情况下存在被意外修改风险。
十三、并发编程的三要素
原子性:一个线程操作数据不能被其他的线程操作影响;(synchronized、lock)
可见性:一个线程对共享变量的修改,另一个线程能立刻看到;(volatile、lock、synchronized)
(1)as-if-serial:不管怎么重排序(编译器和处理器为了提高并行度),(单线程)程序的执行结果不能被改变;runtime 和处理器共同为编写单线程程序的程序员创建了一个幻觉:单线程程序是按程序的顺序来执行的;
(2)happen-before:如果A happen-before B ,A操作结果对B可见且操作A在B之前;如果指令重排序之后的结果,与按照happen-before关系执行结果一致,则指令可以重排。
有序性:程序执行的顺序按照代码的先后顺序进行执行;(volatile)
重排序:为了提高性能,编译器和处理器会对指令进行重排序;(1)针对编译器重排序:编译器重排序会禁止一些特定类型编译器重排序;(2)针对处理器重排序:编译器会在生成指令的时候插入内存屏障来禁止些特定类型的处理器重排序。
十四、说下你对synchronized理解
synchronzied可以修饰实例方法、静态方法、静态代码块;
同步方法:对象头实现访问ACC_SYNCHRONZIED标志为基础;
同步代码块:指虚拟机的指令monitorenter和monitorexit指令实现
四大特性:
可见性:因为某一线程进入synchronized代码前后,线程会获得锁,清空工作内存,从主内存拷贝共享变量最新的值到工作内存成为副本。
原子性:
可重入性:synchronized锁对象的时候有个计数器,会记录下线程取锁的次数+1,在执行完代码块之后计数器就会-1,直到计数器清零,避免一些死锁的现象。
不可中断性:一个线程获取锁后,另一个线程处于阻塞或者等待状态。
十五、synchronized与lock区别
(1)synchronized是java的关键字,lock是接口;
(2)synchronzied在发生异常时,会自动释放线程占用的锁,不会造成死锁;lock没有主动通过unlock()释放锁,很可能造成死锁,因此lock需要在finally块中释放锁;
(3)lock会让等待锁的线程响应中断,而synchronized却不行,等待的线程一直等待下去,不能响应中断;
(4)lock知道线程有没有成功获取锁;
十六、说说 JDK1.6 之后的synchronized 关键字底层做了哪些优化,可以详细介绍一下这些优化吗
无锁 -> 偏向锁 -> 轻量级锁 -> 重量级锁
偏向锁:对象的代码一直被同一线程执行,不存在多个线程竞争,该线程在后续的执行中自动获取锁,降低获取锁带来的性能开销。
轻量级锁:轻量级锁是指当锁是偏向锁的时候,被第二个线程 B 所访问,此时偏向锁就会升级为轻量级锁,线程 B 会通过自旋的形式尝试获取锁,线程不会阻塞,从而提高性能。
重量级锁:指当有一个线程获取锁之后,其余所有等待获取该锁的线程都会处于阻塞状态,重量级锁通过对象内部的监视器(monitor)实现
十七、说下你对volatile理解
目的:用来确保变量的更新操作通知其他线程。
特性:
变量可见性:保证变量对所有的线程可见,这里的可见性指的是当一个线程修改了变量的值,新的值对于其他线程可以立即获取到。
禁止重排、不保证原子性。
在执行volatile变量时不会执行枷锁操作,因此不会执行线程阻塞;比synchronized更轻量级的锁。
十八、volatile与synchronzied区别
(1)volatile修饰的是变量,synchronized可以修饰代码块和方法;
(2)volatile保证可见性、禁止指令重排、但不保证原子性,synchronized保证原子性;
(3)不会造成线程阻塞,synchronzied会造成线程阻塞;
十九、CAS是什么
CAS(Compare-and-Swap),即比较并替换,是一种乐观锁策略。线程在读取数据时不进⾏加锁,在准备写回数据时,先去查询原值,操作的时候⽐较原值是否修改,若未被其他线程修改则写回,若已被修改,则重新执⾏读取流程。
.缺点:
(1)循环时间长开销大:因为CAS操作长时间不成功的话,会导致一直自旋,相当于死循环,CPU的压力会很大。
(2)只能保证一个变量的原子操作:1)使用互斥锁来保证原子性;2)将多个变量封装成对象,通过 AtomicReference 来保证原子性。
(3)ABA问题:
二十、解释一下你对ThreadLocal的理解
ThreadLocal是堆内存中的一个数据复制N份,每个线程领一份,同时规定好,每个线程只能使用自己的那份,不影响其他线程。
目的:解决对象不被多个线程共享的问题
Thread为每个线程维护了ThreadLocalMap,⽽ThreadLocalMap的key是ThreadLocal对象本身,value则是要存储的对象。
二十一、ThreadLocal内存泄漏的原因
由于ThreadLocal在Entry中是弱引用,当外部ThreadLocal实例设置为null后,根据可达性分析,堆中ThreadLocal不可达,会被GC掉,而value不会被回收,如果创建ThreadLocal线程一直持续运行,那么entry对象中的value就有可能一直得不到回收。
解决办法:
调用ThreadLocal的get()方法或者set()方法时完成后在调用remove方法,将entry节点和map的引用关系移除,这样整个entry对象就会在GC Roots分析变成不可达,下次GC时候会被回收。
二十二、ThreadLocalMap中的set()方法
ThreadLocalMap存储时候会给每个ThreadLocal对象一个threadLocalHashCode,根据ThreadLocal对象的hash值,定位table位置;
(1)如果当前位置为空,初始化一个Entry对象放在位置i;
(2)如果位置i不为空,如果Entry对象的key正好是即将设置的key,那么就刷新Entry的value;
(3)如果位置i不为空,而且key不等于entry,那么就找下一个空位置。
二十三、synchronzied与ThreadLocal区别
synchronized同步机制采用以时间换空间,只是提供一份变量,让不同的线程排队访问。
ThreadLocal采用以空间换时间,每个线程只提供一个变量副本,从而实现线程同时访问互不干扰,多线程让每个线程之间数据相互隔离。
二十四、ReentrantLock与synchronized区别
相似:加锁式、阻塞式同步;一个线程获得对象锁进入同步块,其他线程访问同步块的线程必须阻塞在同步块外等待;
区别:锁的粒度与灵活度:ReentrantLock优于synchronzied;
synchronzied由编译器去保证锁的加锁与释放,而ReentrantLock需要手工声明加锁和释放
二十五、java并发中并列队列原理
(1)ArrayBlockingQueue:基于数组实现的一个阻塞队列,在创建ArrayBlockingQueue对象必须指定容量大小(有界),不保证访问者公平访问队列;
(2)LinckedBlockQueue:链表实现有界阻塞队列,先进先出原则对元素进行排序,基于生产者端和消费者端,分别采用独立锁来控制数据同步。
(3)PriorityBlockQueue:支持优先级排序无界阻塞队列,可以自定义实现compareTo()方法来指定元素进行排序准则;
(4)DelayQueue:延时获取元素无界队列,队列使用Priority来实现,队列中元素必须实现Delayed接口;在创建元素时可以指定多久才能从队列中获取当前元素。