Java-JUC(七)

1.Java中实现多线程有几种方法

创建线程的常用的几种方式:

继承Thread类
实现Runnable接口 (重写run方法,无返回值)
实现Callable接口( JDK1.5>=,重写call方法,可以自定义返回值 )
线程池方式创建

2.线程的几个基本状态

新建状态(New):新创建了一个线程对象。

就绪状态(Runnable):线程对象创建后,其他线程调用了该对象的start()方法。该状态的线程位于可运行线程池中,变得可运行,等待获取CPU的使用权。

运行状态(Running):就绪状态的线程获取了CPU,执行程序代码。

阻塞状态(Blocked):阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。阻塞的情况分三种:

等待阻塞:运行的线程执行wait()方法,JVM会把该线程放入等待池中。(wait会释放持有的锁)
同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池中。
其他阻塞:运行的线程执行sleep()或join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。(注意,sleep是不会释放持有的锁)

死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。

3.如何停止一个正在运行的线程

使用退出标志,使线程正常退出,也就是当run方法完成后线程终止。

使用stop方法强行终止,但是不推荐这个方法,因为stop和suspend及resume一样都是过期作 废的方法。

使用interrupt方法中断线程。

4.notify()和notifyAll()有什么区别?

notify可能会导致死锁,而notifyAll则不会 任何时候只有一个线程可以获得锁,也就是说只有一个线程可以运行synchronized 中的代码 使用notifyall,可以唤醒 所有处于wait状态的线程,使其重新进入锁的争夺队列中,而notify只能唤醒一个。
wait() 应配合while循环使用,不应使用if,务必在wait()调用前后都检查条件,如果不满足,必须调 用notify()唤醒另外的线程来处理,自己继续wait()直至条件满足再往下执行。
notify() 是对notifyAll()的一个优化,但它有很精确的应用场景,并且要求正确使用。不然可能导致死锁。正确的场景应该是 WaitSet中等待的是相同的条件,唤醒任一个都能正确处理接下来的事 项,如果唤醒的线程无法正确处理,务必确保继续notify()下一个线程,并且自身需要重新回到 WaitSet中.

5.sleep()和wait() 有什么区别?

对于sleep()方法,我们首先要知道该方法是属于Thread类中的。而wait()方法,则是属于Object类 中的。
sleep()方法导致了程序暂停执行指定的时间,让出cpu该其他线程,但是他的监控状态依然保持着,当指定的时间到了又会自动恢复运行状态。在调用sleep()方法的过程中,线程不会释放对象 锁。
当调用wait()方法的时候,线程会放弃对象锁,进入等待此对象的等待锁定池,只有针对此对象调用 notify()方法后本线程才进入对象锁定池准备,获取对象锁进入运行状态。

6.Thread 类中的start() 和 run() 方法有什么区别?

start()方法被用来启动新创建的线程,而且start()内部调用了run()方法,这和直接调用run()方法的 效果不一样。当你调用run()方法的时候,只会是在原来的线程中调用,没有新的线程启动,start() 方法才会启动新线程。

7.为什么wait, notify 和 notifyAll这些方法不在thread类里面?

明显的原因是JAVA提供的锁是对象级的而不是线程级的,每个对象都有锁,通过线程获得。如果线程需要等待某些锁那么调用对象中的wait()方法就有意义了。如果wait()方法定义在Thread类中,线 程正在等待的是哪个锁就不明显了。简单的说,由于wait,notify和notifyAll都是锁级别的操作,所 以把他们定义在Object类中因为锁属于对象。
synchronized 和 ReentrantLock 有什么不同?

用法不同:synchronized 可以用来修饰普通方法、静态方法和代码块,而 ReentrantLock 只能用于代码块。

获取锁和释放锁的机制不同:synchronized 是自动加锁和释放锁的,而 ReentrantLock 需要手动加锁和释放锁。

锁类型不同:synchronized 是非公平锁,而 ReentrantLock 默认为非公平锁,也可以手动指定为公平锁。

响应中断不同:ReentrantLock 可以响应中断,解决死锁的问题,而 synchronized 不能响应中断。

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;public class ReentrantLockInterrupt {static Lock lockA = new ReentrantLock();static Lock lockB = new ReentrantLock();public static void main(String[] args) throws InterruptedException {// 线程 1:先获取 lockA 再获取 lockBThread t1 = new Thread(() -> {try {// 先获取 LockAlockA.lockInterruptibly();// 休眠 10 毫秒TimeUnit.MILLISECONDS.sleep(100);// 获取 LockBlockB.lockInterruptibly();} catch (InterruptedException e) {System.out.println("响应中断指令");} finally {// 释放锁lockA.unlock();lockB.unlock();System.out.println("线程 1 执行完成。");}});// 线程 2:先获取 lockB 再获取 lockAThread t2 = new Thread(() -> {try {// 先获取 LockBlockB.lockInterruptibly();// 休眠 10 毫秒TimeUnit.MILLISECONDS.sleep(100);// 获取 LockAlockA.lockInterruptibly();} catch (InterruptedException e) {System.out.println("响应中断指令");} finally {// 释放锁lockB.unlock();lockA.unlock();System.out.println("线程 2 执行完成。");}});t1.start();t2.start();TimeUnit.SECONDS.sleep(1);// 线程1:执行中断t1.interrupt();}
}

底层实现不同:synchronized 是 JVM 层面通过监视器实现的,而 ReentrantLock 是基于 AQS 实现的。

8.谈谈Lock锁底层实现原理

底层基于AQS+CAS+LockSupport锁实现

9.AQS是什么

AQS抽象的队列同步器,是整个JUC体系的基石,通过内置的FIFO队列来完成资源获取线程的排队工作,并通过一个int类型的变量表示持有锁的状态;当有线程获取不到锁时,就将线程加入该队列中,通过CAS自旋和LockSupport.part()的方式,维护state变量,达到并发同步的效果。

10.AQS的核心原理

AQS使用一个violatile的int类型的成员变量来表示同步状态,通过内置的FIFO队列来完成资源获取的排队工作,将每条要抢占资源的线程封装成一个Node节点来完成锁的分配,通过CAS完成对state值的修改;核心就是state+CLH带头节点的双端队列

11.ReentrantLock获取锁的过程

公平锁:当一个线程在尝试获得锁时,如果锁的state=0,且等待队列为空则获得锁,否则进入队列尾,当持有资源的线程释放锁时唤醒队首线程
非公平锁:当一个线程在尝试获得锁时,直接尝试CAS,成功则占有锁,否则进入队列尾,当持有资源的线程释放锁时唤醒队首线程,如果队首线程获得锁成功则弹出,否则不弹出

12.有三个线程T1,T2,T3,如何保证顺序执行

在多线程中有多种方法让线程按特定顺序执行,你可以用线程类的join()方法在一个线程中启动另一个线程,另外一个线程完成该线程继续执行。为了确保三个线程的顺序你应该先启动最后一个(T3调用T2,T2调用T1),这样T1就会先完成而T3最后完成。

13.Thread类中的yield方法有什么作用?

Yield方法可以暂停当前正在执行的线程对象,让其它有相同优先级的线程执行。它是一个静态方法 而且只保证当前线程放弃CPU占用而不能保证使其它线程一定能占用CPU,执行yield()的线程有可 能在进入到暂停状态后马上又被执行。

14.线程池创建需要的那几个核心参数的含义

ThreadPoolExecutor 最多包含以下七个参数:

corePoolSize:线程池中的核心线程数
maximumPoolSize:线程池中最大线程数
keepAliveTime:闲置超时时间
unit:keepAliveTime 超时时间的单位(时/分/秒等)
workQueue:线程池中的任务队列
threadFactory:为线程池提供创建新线程的线程工厂
rejectedExecutionHandler:线程池任务队列超过最大值之后的拒绝策略

15.ThreadPoolExecutor 有哪些常用的方法?

ThreadPoolExecutor有如下常用方法:

submit()/execute():执行线程池
shutdown()/shutdownNow():终止线程池
isShutdown():判断线程是否终止
getActiveCount():正在运行的线程数
getCorePoolSize():获取核心线程数
getMaximumPoolSize():获取最大线程数
getQueue():获取线程池中的任务队列
allowCoreThreadTimeOut(boolean):设置空闲时是否回收核心线程这些方法可以用来终止线程池、线程池监控等。

16.Java线程池中submit() 和 execute()方法有什么区别?

两个方法都可以向线程池提交任务,execute()方法的返回类型是void,它定义在Executor接口中, 而submit()方法可以返回持有计算结果的Future对象,它定义在ExecutorService接口中,它扩展了 Executor接口,其它线程池类像ThreadPoolExecutor和ScheduledThreadPoolExecutor都有这些 方法。

17.常用的线程池有哪些?

newSingleThreadExecutor:创建一个单线程的线程池,此线程池保证所有任务的执行顺序按照任务的提交顺序执行。
newFixedThreadPool:创建固定大小的线程池,每次提交一个任务就创建一个线程,直到线程达到线程池的最大大小。
newCachedThreadPool:创建一个可缓存的线程池,此线程池不会对线程池大小做限制,线程池大小完全依赖于操作系统(或者说JVM)能够创建的最大线程大小。
newScheduledThreadPool:创建一个大小无限的线程池,此线程池支持定时以及周期性执行任务的需求。
newSingleThreadExecutor:创建一个单线程的线程池。此线程池支持定时以及周期性执行任务的需求。

18.线程池中的workQueue有哪几种

1、LinkedBlockingQueue,一个无界缓存等待队列。当前执行的线程数量达到corePoolSize的数量时,剩余的元素会在阻塞队列里等待。(所以在使用此阻塞队列时maximumPoolSizes就相当于无效了),每个线程完全独立于其他线程。生产者和消费者使用独立的锁来控制数据的同步,即在高并发的情况下可以并行操作队列中的数据。
2、SynchronousQueue,无缓冲等待队列,是一个不存储元素的阻塞队列,会直接将任务交给消费者,必须等队列中的添加元素被消费后才能继续添加新的元素。拥有公平(FIFO)和非公平(LIFO)策略,使用SynchronousQueue阻塞队列一般要求maximumPoolSizes为无界(Integer.MAX_VALUE),避免线程拒绝执行操作。
3、ArrayBlockingQueue,一个有界缓存等待队列,可以指定缓存队列的大小,当正在执行的线程数等于corePoolSize时,多余的元素缓存在ArrayBlockingQueue队列中等待有空闲的线程时继续执行,当ArrayBlockingQueue已满时,加入ArrayBlockingQueue失败,会开启新的线程去执行,当线程数已经达到最大的maximumPoolSizes时,再有新的元素尝试加入ArrayBlockingQueue时会报错。
4、DelayedWorkQueue,其特点是内部元素并不是按照放入的时间排序,而是会按照延迟的时间长短对任务进行排序,内部采用的是“堆”的数据结构。之所以线程池 ScheduledThreadPool 和 SingleThreadScheduledExecutor 选择 DelayedWorkQueue,是因为它们本身正是基于时间执行任务的,而延迟队列正好可以把任务按时间进行排序,方便任务的执行。

17.shutdownNow() 和 shutdown() 两个方法有什么区别?

shutdownNow() 和 shutdown() 都是用来终止线程池的,它们的区别是,使用 shutdown() 程序不会报错,也不会立即终止线程,它会等待线程池中的缓存任务执行完之后再退出,执行了 shutdown() 之后就不能给线程池添加新任务了;shutdownNow() 会试图立马停止任务,如果线程池中还有缓存任务正在执行,则会抛出 java.lang.InterruptedException: sleep interrupted 异常。

18.线程池的工作原理

提交任务后,先判断当前池中线程数是否小于 corePoolSize,如果小于,则创建新线程执行这个任务。
否则,判断线程池任务队列是否已满,如果没有满,则添加任务到任务队列。
否则,判断当前池中线程数是否大于 maximumPoolSize,如果大于则执行预设拒绝策略。
否则,创建一个线程执行该任务,直至线程数达到maximumPoolSize,达到后执行预设拒绝策略。

18.线程池创建之后,会立即创建核心线程么

并不会立即创建核心线程,而是等到有任务提交时才会开始创建线程,除非调用了prestartCoreThread/prestartAllCoreThreads 事先启动核心线程。
prestartCoreThread: 启动一个核心线程,使其空闲等待工作。这会覆盖仅在执行新任务时启动核心线程的默认策略。如果所有核心线程都已启动,此方法将返回 false。如果线程已启动,则返回 true。
csharp复制代码public boolean prestartCoreThread() {
return workerCountOf(ctl.get()) < corePoolSize &&
addWorker(null, true);
}

prestartAllCoreThreads:启动所有核心线程,使它们空闲等待工作。这会覆盖仅在执行新任务时启动核心线程的默认策略。返回启动的线程数。
csharp复制代码public int prestartAllCoreThreads() {
int n = 0;
while (addWorker(null, true))
++n;
return n;
}

19.核心线程永远不会销毁么

其实核心线程只是一个动态概念,在jdk中并没有给线程打上"core"标记。而在jdk1.6之前,线程池会尽量保证会有corePoolSize个线程存活,即使这些线程已经闲置了很长的时间,这样会造成一部分资源浪费;于是在1.6开始,jdk提供了一个allowCoreThreadTimeOut方法用于控制核心线程是否被销毁。
注意: 这种策略和corePoolSize=0是有区别的

corePoolSize=0:在一般情况下只使用一个线程消费任务,只有当并发请求特别多、等待队列都满了之后,才开始用多线程。
allowsCoreThreadTimeOut=true && corePoolSize>1:在一般情况下就开始使用多线程(corePoolSize 个),当并发请求特别多,等待队列都满了之后,继续加大线程数。但是当请求没有的时候,允许核心线程也终止。

综合来看,其实corePoolSize=0的效果基本等同于allowsCoreThreadTimeOut=true&&corePoolSize=1,只是实现细节不同。

20.线程池的优点,作用

降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。
提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。

21.线程池核心线程数怎么设置呢?

分为CPU密集型和IO密集型

CPU这种任务消耗的主要是 CPU 资源,可以将线程数设置为 N(CPU 核心数)+1,比 CPU 核心数多出 来的一个线程是为了防止线程偶发的缺页中断,或者其它原因导致的任务暂停而带来的影响。一旦任务暂停,CPU 就会处于空闲状态,而在这种情况下多出来的一个线程就可以充分利用 CPU 的空闲时间。
IO密集型 这种任务应用起来,系统会用大部分的时间来处理 I/O 交互,而线程在处理 I/O 的时间段内不会占 用 CPU 来处理,这时就可以将 CPU 交出给其它线程使用。因此在 I/O 密集型任务的应用中,我们 可以多配置一些线程,具体的计算方法是 : 核心线程数=CPU核心数量*2。

22.线程池为什么需要使用(阻塞)队列?

因为线程若是无限制的创建,可能会导致内存占用过多而产生OOM,并且会造成cpu过度切换。
创建线程池的消耗较高。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/24746.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

python#django数据库一对一/一对多/多对多

一对一OneToOneField 用户和用户信息 搭建 # 一对一 class TestUser(models.Model): usernamemodels.CharField(max_length32) password models.CharField(max_length32) class TestInfo(models.Model): mick_namemodels.CharField(max_length32) usermode…

企业计算机服务器中了locked勒索病毒怎么办,如何预防勒索病毒攻击

计算机服务器是企业的关键信息基础设备&#xff0c;随着计算机技术的不断发展&#xff0c;企业的计算机服务器也成为了众多勒索者的攻击目标&#xff0c;勒索病毒成为当下计算机服务器的主要攻击目标。近期&#xff0c;我们收到很多企业的求助&#xff0c;企业的服务器被locked…

【网络编程】TCP带外数据总结

文章目录 一、带外数据基本知识二、带外数据的读写三、检测带外数据是否到达3.1、select上的异常事件3.2、SIGURG信号 四、带外标记 一、带外数据基本知识 带外数据&#xff08;Out Of Band&#xff0c;OOB&#xff09;&#xff0c;用于迅速通告对方本端发生的重要事件&#xf…

leaflet-uniapp 缩放地图的同时 显示当前缩放层级

记录实现过程&#xff1a; 需求为移动端用户在使用地图时&#xff0c;缩放地图的同时&#xff0c;可以获知地图此时缩放的级别。 效果图如下&#xff1a;此时缩放地图级别为13 map.on() 有对应的诸多行为 查看官网即可&#xff0c;这里根据需要为--zoomstart zoom zoomend 代…

Crowd-Robot Interaction 论文阅读

论文信息 题目&#xff1a;Crowd-Robot Interaction:Crowd-aware Robot Navigation with Attention-based Deep Reinforcement Learning 作者&#xff1a;Changan Chen, Y uejiang Liu 代码地址&#xff1a;https://github.com/vita-epfl/CrowdNav 来源&#xff1a;arXiv 时间…

【Linux】网络套接字知识点补足

目录 1 地址转换函数 1.1 字符串转in_addr的函数: 1.2 in_addr转字符串的函数: 1.3 关于inet_ntoa 2 TCP协议通讯流程 1 地址转换函数 本节只介绍基于IPv4的socket网络编程,sockaddr_in中的成员struct in_addr sin_addr表示32位 的IP 地址但是我们通常用点分十进制的字符串…

无涯教程-Perl - bind函数

描述 此功能将网络地址绑定到由SOCKET标识的文件句柄。地址应该是用于打开的Socket的适当类型的打包地址。 语法 以下是此函数的简单语法- bind SOCKET, ADDRESS 返回值 如果失败,此函数返回0,如果成功,则返回1。 Perl 中的 bind函数 - 无涯教程网无涯教程网提供描述此功能…

day52-Redis

Redis 1.Redis 1.1 RESP连接Redis 1.2 定义&#xff1a;是一个高性能的key-value数据库&#xff08;非关系型数据库&#xff09; 1.3 数据类型&#xff1a; key键的类型是字符串类型&#xff1b; 值的类型有五种&#xff1a;字符串String&#xff0c;哈希hash&#xff0…

pygame贪吃蛇游戏

pygame贪吃蛇游戏 贪吃蛇游戏通过enter键启动&#xff0c;贪吃蛇通过WSAD进行上下左右移动&#xff0c;每次在游戏区域中随机生成一个食物&#xff0c;每次吃完食物后&#xff0c;蛇变长并且获得积分&#xff1b;按空格键暂停。 贪吃蛇 import random, sys, time, pygame from …

React 用来解析html 标签的方法

在React中&#xff0c;解析HTML标签通常是使用JSX&#xff08;JavaScript XML&#xff09;语法的一部分。JSX允许您在JavaScript代码中编写类似HTML的标记&#xff0c;然后通过React进行解析和渲染。 以下是React中解析HTML标签的几种常见方式&#xff1a; 直接在JSX中使用标…

AR远程专家指导在汽车改装上的应用有哪些?

随着科技的不断发展&#xff0c;AR增强现实技术逐渐走进了我们的生活。加上商贸国际化&#xff0c;远程协同纵深到制造生产的更多环节&#xff0c;研发协同、工艺优化等场景复杂、跨层级、需要频繁沟通确认的流程正通过AR应用实现全面远程化的过渡&#xff0c;在汽车行业&#…

Kuibernetes 如何根据不同业务场景调节 HPA 扩缩容灵敏度

背景 在 K8s 1.18 之前&#xff0c;HPA 扩容是无法调整灵敏度的: 对于缩容&#xff0c;由 kube-controller-manager 的 --horizontal-pod-autoscaler-downscale-stabilization-window 参数控制缩容时间窗口&#xff0c;默认 5 分钟&#xff0c;即负载减小后至少需要等 5 分钟才…

JVM入门到精通

一、JVM概念 1.1、什么是JVM Java Virtual Machine&#xff1a;Java虚拟机&#xff0c;用来保证Java语言跨平台 Java虚拟机可以看做是一台抽象的计算机&#xff0c;如同真实的计算机那样&#xff0c;它有自己的指令集以及各种运行时内存区域 Java虚拟机与Java语言并没有必然…

【数据结构】常见的排序算法

常见的排序算法 常见的排序算法插入排序之直接插入排序时间复杂度特性总结 插入排序之希尔排序时间复杂度 选择排序之直接选择排序特性总结 选择排序之堆排序时间复杂度特性总结 交换排序之冒泡排序特性总结 交换排序之快速排序hoare版本挖坑法双指针法快速排序的优化1&#xf…

【Python基础教程】super()函数的正确使用方法

前言 大家早好、午好、晚好吖 ❤ ~欢迎光临本文章 1.super(本身类名,self).方法名(参数)这样就可以调用父类的方法和参数了,super()内也可不加参数 2.规律是super是按调用的次序执行&#xff0c;super后面的语句是逆向执行的。 有2段示例代码&#xff0c;不同的在于value有没…

MySQL 主从搭建

文章目录 前言一、MySQL 主从是什么&#xff1f;二、通过 Docker 部署三、配置主从关系四、实际情况分析&解决方案五、常见问题处理1、CLONE需要版本不同2、CLONE需要参数相同 总结 前言 MySQL 主从搭建 操作系统&#xff1a;CentOS Linux release 7.9.2009 (Core) 操作系…

NB-IOT 和蜂窝通信(2/3/4/5G)的区别和特点是什么?

NB-IOT 和蜂窝通信(2/3/4/5G)的区别和特点是什么? 参考链接:https://www.sohu.com/a/221664826_472880 NB IOT是窄带物联网技术,主要解决的是低速率数据传输,可使用GSM900或DCS1800频段,在频段使用上比较灵活,可以和GSM,UMTS或LTE共存,具备优异的MCL(最小耦合损耗…

Vue3 条件渲染简单应用

去官网学习-》条件渲染 | Vue.js 运行示例&#xff1a; 代码&#xff1a;HelloWorld.vue <template><div class"hello"><h1>Vue 条件渲染</h1><h2 v-if"flag">true显示内容</h2><h2 v-if"flag2">fal…

python进阶编程

lambda匿名函数 python使用lambda表达式来创建匿名函数 语法 // lambda 参数们&#xff1a;对参数的处理 lambda x : 2 * x // x 是参数&#xff0c; 2*x 是返回值 ​ //使用lambda实现求和 sum lambda arg1, arg2 : agr1 arg2 print(sum(10,20)) ​ // 将匿名函数封装在一…

我的创作5周年纪念日

机缘 CSDN在 SEO 方面做得很好。所以容易接触到。 然后就尝试使用了。没想到已经5年了。 收获 写blog其实是对知识的总结&#xff0c;能让自己更好的分享交流&#xff0c;让自己能和其他技术者一起交流迭代&#xff0c;并且把技术内容不断做好&#xff0c;让更多人通过技术…