【多线程】

线程概念

什么是自旋

很多synchronized 里面的代码只是一些简单的代码,执行时间非常快,这是让等待锁的线程再阻塞在那就不是很划算,于是就让做忙循环,这就是自旋。如果做了多次循环还没有获得锁,再阻塞。
忙循环:就是让一个线程等待不像wait、sleep、yield等方法放弃了CPU的控制权,而是运行一个空循环,继续使用CPU。这样做是为了保留CPU缓存,在多核系统中,一个等待线程醒来的时候可能会在另一个内核运行,这样会重建缓存。

synchronized 锁升级的原理是什么

锁对象的对象头里面有一个字段 threadid,首次访问锁对象,threadid 为空,JVM让其持有 偏向锁 ,并将threadid 设置为当前线程 id;再次访问锁对象,会先判断threadid 是否与当前线程 id 一致,若一致则可以直接使用此对象,若不一致,就把偏向锁升级为轻量级锁;等待锁对象时,通过自旋循环一定次数来获取锁,执行一定次数后,若还没有获取到,就会把轻量级锁升级成重量级锁。
锁升级可以减轻性能消耗;锁的升级不可逆。

线程的生命周期

线程的创建方式

继承 Thread 类
实现 Runnable 接口
实现 Callable 接口
使用匿名内部类
通过线程池创建
线程的实现方式.note

Runnable 和 Callable 的区别

Runnable的run方法没有返回值,Callable 的 call 方法有返回值。需要调用FutureTask.get()得到,此方法会阻塞主进程,如果不调用不会阻塞。

为什么我们调用start()方法时会执行run()方法,为什么我们不能直接调用run()方法?

调用start方法线程进入就绪状态,是执行一个线程,而掉run方法,只是会当作一个普通的方法执行。
调用start()方法,线程进入了就绪状态,当分配到时间片后就可以开始运行了,然后自动执行run()方法的内容。是真正的多线程工作。
而直接执行run()方法,会把run方法当成一个mian线程下的普通方法去执行,并不会在某个线程中执行它,这并不是多线程工作。

线程的状态

新建状态(New)
用new语句创建的线程处于新建状态,此时它和其他Java对象一样,仅仅在堆区中被分配了内存。
就绪状态(Runnable)
当一个线程对象创建后,其他线程调用它的start()方法,该线程就进入就绪状态,Java虚拟机会为它创建方法调用栈和程序计数器。处于这个状态的线程位于可运行池中,等待获得CPU的使用权。
运行状态(Running)
处于这个状态的线程占用CPU,执行程序代码。只有处于就绪状态的线程才有机会转到运行状态。
阻塞状态(Blocked)
阻塞状态是指线程因为某些原因放弃CPU,暂时停止运行。
阻塞状态可分为以下3种:
位于对象等待池中的阻塞状态(Blocked in object’s wait pool):当线程处于运行状态时,如果执行了某个对象的wait()方法,Java虚拟机就会把线程放到这个对象的等待池中。
位于对象锁池中的阻塞状态(Blocked in object’s lock pool):当线程处于运行状态时,试图获得某个对象的同步锁时,如果该对象的同步锁已经被其他线程占用,Java虚拟机就会把这个线程放到这个对象的锁池中。
其他阻塞状态(Otherwise Blocked):当前线程执行了sleep()方法,或者调用了其他线程的join()方法,或者发出了I/O请求时,就会进入这个状态。
死亡状态(Dead)
当线程退出run()方法时,就进入死亡状态,该线程结束生命周期。

线程的方法与关键字

synchronized与Lock的区别

请添加图片描述
void lock(): 获取锁,如果锁被暂用就一直等待
void unlock(): 释放锁
boolean tryLock(): 获取锁,判断锁的状态,被占用就返回false,否则为true
void lockInterruptibly(): 获取锁,如果线程在获取锁的阶段进入了等待,那么可以中断此线程
lock:一般使用ReentrantLock类做为锁。在加锁和解锁处需要通过lock()和unlock()显示指出。所以一般会在finally块中写unlock()以防死锁。
使用 lock() 时,调用 interrupt() 需要首先获得锁,(等待结束,获取锁后)才能进行中断
当换成 lockInterruptibly() 时,调用 interrupt()可以在没获取到锁的情况下(等待时)直接中断

在java中wait和sleep方法的不同?

类不同:sleep 是Thread类的静态方法,wait 是 Object类的方法;
wait 会释放锁,sleep不会;
wait 一般用于线程之间的交互,sleep 用于暂停执行;
用法不同:wait 方法被调用后,线程不会自动苏醒,需要别的线程调用同一个对象上的notify() 或 notifyAll() 方法,或者可以用wait(long timeout)超时后线程会自动苏醒,sleep 方法会自动苏醒。
wait、notify 方法必须要写在synchronized块或方法中,锁对象与调用wait、notify方法的对象是同一个,因为当前线程已经获取锁后才能有效调用wait、notify。
Thread 类中的yield方法有什么作用?
使当前线程从运行状态变成就绪状态。

Java中能创建volatile 数组吗

可以,Java中可以创建 volatile 数组,如果改变数组的引用,volatile会生效,但是如果多个线程同时改变数组的元素,volatile 不会生效

线程池

为什么要用线程池

降低资源消耗。通过重复利用已创建的线程,降低了线程频繁创建和销毁造成的消耗;
提高响应速度。当任务到达时,任务不需要等线程创建就能立即执行;
增加了线程的可管理性。

线程池的核心属性有哪些

corePoolSize(核心线程数):当线程池运行的线程少于 corePoolSize 时,将创建一个新线程来处理请求,即使其他线程处于空闲状态;
maximumPoolSize (最大线程数):线程池运行开启的最大线程数;
keepAliveTime (保持存活时间):如果线程池当前线程数超过 corePoolSize,则多余的线程空闲时间超过 keepAliveTime 时会被终止;
unit(空闲线程存活时间单位):keepAliveTime 的计量单位;
workQueue (队列):用于保留任务并移交给工作线程的阻塞队列;
threadFactory(线程工厂):用于创建线程;
handler(拒绝策略):往线程池添加任务时,有两种情况会触发拒绝策略:线程池运行状态不是RUNNING;线程池已经达到最大线程数,并且阻塞列队已满时。

线程池的创建方法

Executors
newFixedThreadPool(固定线程数的线程池):corePoolSize = maximumPoolSize,keepAliveTime为0,工作列队使用无界的LinkedBlockingQueue。适用于任务量已知,比较耗时的任务。
newCachedThreadPool(按需求创建新线程的线程池):核心线程数为0,最大线程数为int的最大值,keepAliveTime为60秒,工作队列使用同步移交 SynchronousQueue(没有线程来取是放不进去的)。该线程池可以无限扩展,当需求增加时,可以添加新的线程,而当需求降低时会自动回收空闲线程。适用于任务比较密集,或者服务器负载较轻的情况。
newScheduledThreadPool(创建一个以延迟或者定时的方式来执行任务的线程池):工作队列是 DelayedWorkQueue。适用于需要多个后台线程执行周期任务。
newSingleThreadExecutor(只有一个线程的线程池):corePoolSize = maximumPoolSize = 1,keepAliveTime 为0,工作队列使用无界的LinkedBlockQueue。适用于需要保证顺序执行各个任务的场景。如果线程因为异常而停止,会自动新建一个线程补充。

使用队列有什么需要注意的

使用有界队列时,需要注意线程池满了后,被拒绝的任务如何处理。
使用无界队列时,需要注意如果任务的提交速度大于线程池的处理速度,可能会导致内存溢出。

线程池有哪些拒绝策略

AbortPolicy:中止策略。默认的拒绝策略,直接抛出 异常。调用者可以捕获这个异常,然后根据需求编写处理代码。
DiscardPolicy:抛弃策略。什么都不做,直接抛弃被拒绝的任务。
DiscardOldestPolicy:抛弃最老策略。
CallerRunsPolicy:调用者运行策略。在调用者线程中执行此任务。

如何终止线程池

shutdown:温柔的关闭线程池。不接受新任务,但是在关闭前会将之前提交的任务处理完毕。
shutdownNow:粗暴的关闭线程池。通过interrupt() 方法终止所以线程,不会等待之前提交的任务执行完,但是会返回队列总未处理的任务。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值