- Q:什么是ThreadLocal?原理(主内存与工作内存)
ThreadLocal 是一个线程内部的数据存储类,通过它可以在指定的线程中存储数据,数据存储以后,只有在指定线程中可以获取到存储的数据,对于其他线程来说则无法获取到数据。
虽然在不同线程中访问的是同一个 ThreadLocal 对象,但是它们通过 ThreadLocal 获取到的值却是不一样的。
一般来说,当某些数据是以线程为作用域并且不同线程具有不同的数据副本的时候,就可以考虑采用 ThreadLocal。
原理:每个Thread的对象都有一个ThreadLocalMap,当创建一个ThreadLocal的时候,就会将该ThreadLocal对象添加到该Map中,其中键就是ThreadLocal,值可以是任意类型。
详细解析:https://www.jianshu.com/p/f2cf1487d820
- Q:开启一个线程的方法有哪些?
- 继承Thread类,重写run方法;
- 实现Runnable接口,并实现该接口的run方法;
- 实现callable接口,重写call()方法,在新建FutureTask类对象时传入当前类对象,接着新建Thread类对象时传入FutureTask类对象,最后运行Thread对象的start()方法。
- Q:销毁一个线程的方法?
- 设置退出标志,使线程正常退出,也就是当run()方法完成后线程终止
- 使用interrupt()方法中断线程。
使用interrupt()方法来中断线程有两种情况:
1)线程处于阻塞状态,如使用了sleep,同步锁的wait,socket中的receiver,accept等方法时,会使线程处于阻塞状态。当调用线程的interrupt()方法时,会抛出InterruptException异常。阻塞中的那个方法抛出这个异常,通过代码捕获该异常,然后break跳出循环状态,从而让我们有机会结束这个线程的执行。
2)线程未处于阻塞状态,使用isInterrupted()判断线程的中断标志来退出循环。当使用interrupt()方法时,中断标志就会置true,和使用自定义的标志来控制循环是一样的道理。- 使用stop方法强行终止线程(不推荐使用,Thread.stop, Thread.suspend, Thread.resume 和 Runtime.runFinalizersOnExit 这些终止线程运行的方法已经被废弃,使用它们是极端不安全的!)
原因:
thread.stop()调用之后,创建子线程的线程就会抛出ThreadDeatherror的错误,并且会释放子线程所持有的所有锁。一般任何进行加锁的代码块,都是为了保护数据的一致性,如果在调用thread.stop()后导致了该线程所持有的所有锁的突然释放(不可控制),那么被保护数据就有可能呈现不一致性,其他线程在使用这些被破坏的数据时,有可能导致一些很奇怪的应用程序错误。
- Q:同步和非同步、阻塞和非阻塞的概念?
同步:同步,就是在发出一个功能调用时,在没有得到结果之前,该调用就不返回;
异步:当一个异步过程调用发出后,调用者不能立刻得到结果
阻塞:阻塞调用是指调用结果返回之前,当前线程会被挂起;
非阻塞:非阻塞和阻塞的概念相对应,指在不能立刻得到结果之前,该函数不会阻塞当前线程,而会立刻返回。
- Q:Thread的join()有什么作用?
join()方法的作用是调用线程等待该线程完成后,才能继续往下运行。
- Q:线程的有哪些状态?
New: 新创建状态;
Runnable:可运行状态
Blocked:阻塞状态,表示线程被锁阻塞,暂时不活动;
Waiting: 等待状态。线程暂时不活动,并且不运行任何代码,这消耗最少的资源,直到线程调度器再次激活它。
Timed Waiting:等待超时状态。
Terminated: 进程执行完毕(可能是被强行终止的)
- Q:什么是线程安全?保障线程安全有哪些手段?
线程安全:确保在多条线程访问的时候,我们的程序还能按照我们预期的行为去执行。
保证线程安全的方法: synchronized同步代码块;同步方法;Lock锁机制,通过创建Lock对象,采用lock()加锁,采用unlock()解锁,来保护指定代码块。
- Q:ReentrantLock和synchronized的区别?
- 首先synchronized是java内置关键字,在jvm层面,Lock是个java类;
- synchronized无法判断是否获取锁的状态,Lock可以判断是否获取到锁;
- synchronized会自动释放锁(a 线程执行完同步代码会释放锁 ;b 线程执行过程中发生异常会释放锁),Lock需在finally中手工释放锁(unlock()方法释放锁),否则容易造成线程死锁;
- 用synchronized关键字的两个线程1和线程2,如果当前线程1获得锁,线程2线程等待。如果线程1阻塞,线程2则会一直等待下去,而Lock锁就不一定会等待下去,如果尝试获取不到锁,线程可以不用一直等待就结束了;
- synchronized的锁可重入、不可中断、非公平,而Lock锁可重入、可判断、可公平(两者皆可)
- Lock锁适合大量同步的代码的同步问题,synchronized锁适合代码少量的同步问题。
- Q:synchronized和volatile的区别
- volatile轻量级,只能修饰变量。synchronized重量级,还可修饰方法
- volatile只能保证数据的可见性,不能用来同步,因为多个线程并发访问volatile修饰的变量不会阻塞。
- synchronized不仅保证可见性,而且还保证原子性,因为,只有获得了锁的线程才能进入临界区,从而保证临界区中的所有语句都全部执行。多个线程争抢synchronized锁对象时,会出现阻塞。
- Q:synchronized同步代码块还有同步方法本质上锁住的是谁?为什么?
**对象锁: **synchronized(object) 锁住的是对象,每个对象自己拥有一个锁
**类锁: **synchronized(Class) 锁住的是类,也就是同一个类的实例,任意时刻只会有一个线程能获得资源
- Q:sleep()和wait()的区别?
- 从使用角度看,sleep是Thread线程类的方法,而wait是Object顶级类的方法。
- sleep可以在任何地方使用,而wait只能在同步方法或者同步块中使用。
- sleep,wait调用后都会暂停当前线程并让出cpu的执行时间,但不同的是sleep不会释放当前持有的对象的锁资源,到时间后会继续执行,而wait会放弃所有锁并需要notify/notifyAll后重新获取到对象锁资源后才能继续执行。
- sleep需要捕获或者抛出异常,而wait/notify/notifyAll不需要。
- notify的作用?
- 当一个拥有Object锁的线程调用 wait()方法时,就会使当前线程加入object.wait 等待队列中,并且释放当前占用的Object锁,这样其他线程就有机会获取这个Object锁,获得Object锁的线程调用notify()方法,就能在Object.wait 等待队列中随机唤醒一个线程(该唤醒是随机的与加入的顺序无关,优先级高的被唤醒概率会高)。
- 如果调用notifyAll()方法就唤醒全部的线程。注意:调用notify()方法后并不会立即释放object锁,会等待该线程执行完毕后释放Object锁。
- Q:Java有哪些线程池?他们的区别是什么?
- ** newCachedThreadPool**
1)工作线程的创建数量几乎没有限制(其实也有限制的,数目为Interger. MAX_VALUE), 这样可灵活的往线程池中添加线程;
2)如果长时间没有往线程池中提交任务,即如果工作线程空闲了指定的时间(默认为1分钟),则该工作线程将自动终止。终止后,如果你又提交了新的任务,则线程池重新创建一个工作线程;
3)在使用CachedThreadPool时,一定要注意控制任务的数量,否则,由于大量线程同时运行,很有会造成系统瘫痪。- ** newFixedThreadPool**
创建一个指定工作线程数量的线程池,每当提交一个任务就创建一个工作线程,当线程 处于空闲状态时,它们并不会被回收,除非线程池被关闭了,如果工作线程数量达到线程池初始的最大数,则将提交的任务存入到池队列(没有大小限制)中。由于newFixedThreadPool只有核心线程并且这些核心线程不会被回收,这样它更加快速底相应外界的请求。- ** newSingleThreadExecutor**
这类线程池内部只有一个核心线程,以无界队列方式来执行该线程,这使得这些任务之间不需要处理线程同步的问题,它确保所有的任务都在同一个线程中按顺序中执行,并且可以在任意给定的时间不会有多个线程是活动的。- ** newScheduleThreadPool**
创建一个线程池,它的核心线程数量是固定的,而非核心线程数是没有限制的,并且当非核心线程闲置时会被立即回收,它可安排给定延迟后运行命令或者定期地执行。这类线程池主要用于执行定时任务和具有固定周期的重复任务。
- 线程池工作流程是怎样的?
- 如果正在运行的线程数量小于 corePoolSize,那么马上创建线程运行这个任务
- 如果正在运行的线程数量大于或等于 corePoolSize,那么将这个任务放入队列
- 如果这时候队列满了,而且正在运行的线程数量小于 maximumPoolSize,那么还是要创建非核心线程立刻运行这个任务
- 如果队列满了,而且正在运行的线程数量大于或等于 maximumPoolSize,那么线程池会抛出异常RejectExecutionException
- 线程池实现原理是怎样的?
- Cache线程池有哪些弊端?
在使用CachedThreadPool时,一定要注意控制任务的数量,否则,由于大量线程同时运行,很有会造成系统瘫痪。
- 进程和线程的区别?