1、线程创建启动的方式
1、继承Thread类,复写run方法,调用start方法启动。
2、实现Runnable接口,复习run方法,以Runnable的实现类为target对象创建Thread对象,
调用Thread类的start方法启动。
3、实现Callable接口,实现call方法(代替run方法)。
4、线程池创建。
2、start方法和run方法的区别
run方法是Thread类中的一个普通,start方法是启动线程的。调用start方法启动线程后,自动调用run方法。
3、Synchronized和lock的区别
1、lock是Java的一个interface接口。而synchronized是Java中的关键字,synchronized是由jdk实现的,不需要手动控制加锁、释放锁。
2、synchronized修饰的代码出现异常时,jdk会自动释放线程占有的锁,所以不会导致死锁。但是当lock发生异常时,如果没有手动unlock()去释放锁,则看可能会造成死锁现象,因此lock一般在finally中释放锁。
3、lock可以让等待线程响应中断处理,synchronized会让线程一直等待,不能中断。
4、synchronized是非公平锁,lock可以设置是否公平,默认非公平。
5、lock的范围有局限性,仅适用于代码块。synchronized可以锁代码块、对象实例、类。
6、Lock可以绑定条件,实现分组唤醒需要的线程;synchronized要么随机唤醒一个,要么全部唤醒。
4、悲观锁和乐观锁
悲观锁:他是一种悲观思想,它总会认为最坏的情况可能会出现,它认为数据很可能会被其他人所修改,所以悲观锁在持有数据的时候总会把资源或数据锁住。Java中实现方式:synchronized和ReentrantLock。
乐观锁:读取的时候不会上锁,只有进行写操作的时候会判断当前数据是否被修改过。实现方式:版本号、CAS。
CAS:当多个线程使用CAS同时更新一个变量时,只有其中一个线程能成功,其他的会失败并可以再次尝试修改。原理:CAS包含三个操作数(内存位置(V)、预期原值(A)、新值(B))。如果内存位置的值与预期原来的值匹配,那么处理器会自动将该位置值更新为新值。否则处理器不会做任何操作。Java中java.util.concurrent就是建立在CAS上的。
5、线程的几种状态
新建(new):新建一个线程对象。
可运行(runable):线程创建后,其他线程调用该线程的start方法。该线程处于可运行线程池中,等待被调用。
运行(running):可运行状态的线程获取到cpu的时间片(timeslice),执行代码。
阻塞(blocked):线程因为某种原因放弃了cpu的使用权,让出了时间片。停止运行,直到线程进入可运行状态,等待获取时间片执行线程。
死亡(dead):线程run、main方法执行结束,或因为异常退出run方法,则该线程生命周期结束
阻塞情况:等待阻塞:运行的线程执行wait方法,JVM把该现场放入等待队列中。
同步阻塞: 运行线程在获取对象的同步锁时,该同步锁被其他线程被别的线程占
用,JVM会把该线程放入锁池中。
其他阻塞:运行的线程执行Thread.sleep或join方法,或者发出了IO请求。JVM会
把该线程置为阻塞状态。当以上方法终止,线程重新转入可运行状
6、Thread和Runnable的区别
Thread是一个类。Runnable是一个接口。
Thread底层是实现了Runnable接口,没有本质的区别。
实现复杂的线程操作用Thread。简单的任务使用Runnable。
7、sleep和wait的区别
sleep:Thread类的一个方法。
使当前线程暂停执行,并让出CPU给其他线程,但是他的监控状态依然保持,到指定
时间自动恢复运行状态。
在调用sleep方法后,线程不会释放对象锁。
可能有异常,需要处理。
可以在任何地方使用。
wait :Object的成员方法。
在调用wait方法时,线程会放弃对象锁,进入此对象的等待锁定池,只有针对此对象
调用notify方法后本线程才处于准备状态。
只能在同步方法和同步代码块中使用。
8、synchronized加在方法上锁的是什么,加在静态方法上锁住的对象是是什么。
普通方法:锁是加在单个对象上,不同的对象没有竞争关系;对象锁。
静态方法:锁的是加载类,类中的所有对象竞争同一把锁。类锁。
9、synchronized(this)和synchronized(User.Class)的区别
this:锁的是当前对象。
class:锁的是当前类。和锁静态方法一样。
10、synchronized和volatile的区别
volatile是线程同步的轻量级实现,性能比synchronized好。但volatile只能用于变量,而synchronized可以修饰方法、代码块。
多线程访问volatile不会阻塞。
volatile能保证数据的可见性,不能保证原子性。synchronized都能。
volatile能解决多线程变量之间的可见性。synchronized解决多线程之间的资源同步。
11、synchronized的原理,JDK6之后做了什么优化
同步代码块:主要是使用monitorenter和monitorexit指令实现
同步方法:是依靠方法修饰符上的ACC_Synchrnized实现
优化:主要引入自旋锁、适应性自旋锁、锁消除、锁粗化、偏向锁、轻量级锁等锁技术
12、乐观锁的使用场景
在表中添加一个version字段,当数据被修改时version也跟着修改。当A线程更新数据时,读取version的值,提交修改的时候对比获取到的version和数据库中的version版本是否一致,相同才提交。
13、CAS
compare and swap:比较交换,是一种乐观锁。包含三个值:当前内存值、预期原来的值、期待更改的值。当内存值和预期原来的值相同时,把期待更改的值设置为内存值。
AtomicInterger怎么保证并发安全性的:它底层基于volatile和CAS实现。
14、 什么是乐观锁,什么是悲观锁,什么是重入锁,什么是自旋锁,怎么是阻塞。
乐观锁:每次拿数据都不会上锁,只在更新数据前对比数据是否被更改。
悲观锁:每次拿数据都会上锁,别人拿数据得等到锁释放
重入锁:线程获取锁后,可以重复执行锁区域。即允许同一个线程多次获取同一把锁
自旋锁:当一个线程获取锁时,如果锁已经被其他线程占用,则该线程进入循环等待,然 后一直判断是否锁被释放,直到获取到锁
阻塞:当前线程不能获取到cpu的执行权
15、ThreadLocal的作用、原理、使用场景
作用:为每个线程提供一个独立的变量副本解决了变量并发访问的冲突问题。它在很多情况下比synchronizaed更简单、方便,是程序有更高的并发性。
原理:ThreadLocal是和当前线程有关,每个线程内部都有一个ThreadLocal.ThreadLocalMap类型的成员变量ThreadLocals,它用来保存变量副本,key是ThreadLocal变量,value是变量副本。当调用Get方法时,就会在当前线程的ThreadLocals中查找,它以当前ThreadLocal变量为key获取当前线程的变量副本。
应用场景:SpringSecurity:使用SecurityContextHolder来获取SecurityContext。
SpringMVC:通过RequestContextHolder来获取当前请求。
Zuul:通过ContextHolder获取当前请求。
16、线程池的作用,常用的线程池有几种,分别是什么。
作用:控制并发数量,实现线程复用,同时也可以管理线程的生命周期。
常见的线程池:
CachedThreadPool:可缓存线程池
没有核心线程,线程数最大为Integer的最大值,线程空闲60s销毁。
适用于执行很多短期异步的程序,负载较轻的服务器
FixedThreadPool:固定长度线程池
最大线程数等于核心数,不会因为线程闲置被销毁,必须达到最大线程数才会复用
空闲线程。
适用于执行长期任务。
SingleThreadPool:单个线程池
只有一个线程,多出的任务会进入队列。
适用于任务单执行的场景。
ScheduledThreadPool:可调度线程池
最大线程数为Integer的最大值,有延迟执行、周期执行
适用于周期性执行任务。
17、线程池的执行流程
核心线程空闲直接使用
超过核心线程数的任务进入队列。
队列使用完,创建非核心线程数来使用。
超过最大线程数则交给Handler处理(拒绝策略)。
18、线程池构造器的7个参数
CorePoolSize:核心线程数,不会被销毁
MaximumPoolSize:最大线程数(核心+非核心),非核心会在用完后销毁。
KeepAliveTime:非核心线程最大空闲时间,到点未用就销毁。
Unit:空闲时间单位。
WorkQueue:BlockingQueue阻塞队列,超过核心线程数的任务进入队列排队。
ThreadFactory:使用这玩意儿创建型的线程。
Handler:拒绝策略,任务超过最大线程数+队列排队数,多出来的任务交给Handler处理。
19、