首页 > 其他分享 >多线程5

多线程5

时间:2024-09-05 12:25:13浏览次数:9  
标签:多线程 Thread 中断 主线 线程 执行

102.final如果修饰的是集合,则没有办法保证线程安全

103.通过Executors返回的线程池对象存在的弊端:
· FixedThreadPool和SingleThreadPool:允许的请求队列长度为Integer.MAX_VALUE,可能会堆积大量的请求,从而导致OOM
· CachedThreadPool 和 ScheduledThreadPool:允许的创建线程数量为Integer.MAX_VALUE,可能会创建大量的线程,从而导致OOM

104.对线程安全的类和线程不安全的类新的理解:
· 线程安全的类指的是在程序中多个线程对该类的同个实例同时进行读写时(本质上是多个线程同时读写对象的状态),不需要进行额外的同步操作,程序可以得到正确的结果
· 线程不安全的类指的是在程序中多个线程对该类的同个实例同时进行读写时,需要进行额外的同步操作来保证程序结果的正确性,如果对该实例的读写不进行协同操作,线程有可能会得到不正确的结果

105.一个单线程程序执行效率很低,那可以通过启动多个线程来执行该程序,如果启动多线程之后对该程序的执行不需要执行同步操作,那么很大程度上可以提高该程序的执行效率,从而提升性能;如果启动多线程之后对该程序的执行需要执行同步操作,那么恐怕对程序的执行效率的提升有限,因为多线程同步之后本质上还是串行执行的,此时可以采用一些技术来提升此场景下多线程的执行效率,例如使用volatile和CAS等手段,也可以使用java.util.concurrent包下的一些现成的类,例如ConcurrentHashMap等,这些类的本质作用是在保证线程安全的前提下可以提升程序的性能

106.同步锁是线程间通信的主要协调者,即线程间对同一资源的操作需要同步锁来进行协调,从而保证对资源的访问可以得到正确的结果

107.对wait方法和notify方法的理解:当线程执行到wait()方法时,该线程就会进入等待状态并释放同步锁,然后需要其他的线程调用notify()方法来唤醒,因为同步锁是线程间通信的协调者,因此wait()方法和notify()方法的调用者只能是同步锁。即执行完obj.wait()方法之后,该线程就会进入对象obj的等待集中,当其他线程执行完obj.notify()方法之后,就会被唤醒对象obj的等待集中的任意一个线程。(wait/notify机制通常会结合一个布尔类型的成员变量来使用)

108.并发编程实战中提到的活跃性问题指的是死锁、饥饿、活锁以及丢失信号

109.多线程带来的两个好处:提高程序的吞吐量(Socket中的BIO+多线程模型)以及更快的响应速度

110.线程安全问题和线程活跃性问题是多线程会带来的两大问题

111.使用多线程引入的额外开销包括:线程的创建和销毁、线程之间的协调、线程的上下文切换以及线程的调度等

112.线程进入Runnable状态之后,如果有分配到CPU的时间片,则就处于运行状态;如果没有分配CPU的时间片,就处于就绪状态。即Runnable状态包含两个子状态:一个是运行状态一个是就绪状态

113.用户线程可以在主线程中创建和启动,当主线程执行到t.start()时,对于主线程来说,只是单纯的执行一条程序语句,该语句执行完之后就继续执行后面的程序流程,但是对于jvm进程来说,这条语句执行完毕,就相当于在jvm进程中启动了一条新的程序执行路径;用户线程也可以在其它用户线程中创建和启动,例如可以在线程任务task1中:Thread t2 = new Thread(new ThreadTask2()),这样一来,线程t2就不是在主线程中启动的,而是在另外一个用户线程中启动的。其实主线程和用户线程并没有什么区别,主线程本质上也是一个用户线程,只不过主线程是程序执行的入口

114.在做订单推送的接口中使用到了多线程,从底层的角度剖析一下,web容器的main方法启动,客户端请求过来之后main线程中开启一个新的线程t1去处理请求,业务代码的执行就处于t1线程的执行路径中,然后又在t1线程中开启了12个新的线程去处理订单推送。结合此场景要理解的一点是,在一个线程中创建和开启另外一个线程是非常常见的,在主线程中创建和开启了t1线程,而自己又在t1线程中创建和开启了12个新的线程,这个业务的执行一共涉及到了14个用户线程,主线程开启的代码位于jvm中,t1线程开启的代码由于web容器中,12个新的线程开启的代码位于自己写的业务代码中

115.要塑造起在一个线程中创建和开启其他线程的概念,主线程中开启线程A,线程A中开启线程B,线程B中开启线程C,线程C中开启线程D......(在主线程中拿到线程对象a调用start方法就表示在主线程中执行对线程A的启动)

116.关于interrupt方法的理解,该方法是Thread类中的一个方法,因此该方法必然是通过Thread对象去调用的。从面向对象的角度理解,Thread对象t代表一个线程,该方法最常见的使用方式是在主线程中(或者其他线程任务中)拿到对象t调用interrupt方法,当主线程执行到该方法时,对于主线程来说仅仅是执行一条程序语句,但是对于线程t来说,它收到了一个中断信号,线程t的中断标志位被设置,所谓的中断标志位被设置指的就是Thread.currentThread().isInterrupted()由false变为true,因此线程t可以对中断标志位是否被设置来决定程序下一步的走向

117.要塑造起在一个线程中中断其他线程的概念,主线程中中断线程A,线程A中中断线程B,线程B中中断线程C,线程C中中断线程D......(在主线程中拿到线程对象a调用interrupt方法就表示在主线程中执行对线程A的中断操作)

118.对一个处于sleep、wait、join状态的线程t发起中断请求,首先线程t的中断标志位先被设置,然后抛出中断异常并对中断标志位复位

119.线程中断标志位的理解,顾名思义,就是用来标记线程是否收到中断请求。中断标志位的初始值为false,表示线程未收到中断请求,当线程收到中断请求后,中断标志位被设置为true
·中断标志位被设置:由false被设置为true(中断状态被设置)
·中断标志位被复位:由true被重置为false(中断状态被清除)

120.塑造“线程生线程”的意识非常重要。对于JVM进程来说,主线程是第一个启动的线程,然后可以在主线程中创建并启动其他线程,然后可以在“其他线程”中创建并启动其他线程,以此类推,这就是“线程生线程”

121.重排序:指的是在单线程环境并且不改变程序执行结果的前提下对程序的执行顺序进行重新排序,重排序的目的在于性能上的提升,但是在多线程环境下重排序会带来各种问题,例如可见性问题

122.可见性问题指的是多个线程访问同一个资源。其中一个线程对资源进行了修改,其他线程对该资源的读取结果不是修改后的值。造成这种现象主要有两个因素,一个是高速缓存(JMM),一个是重排序

123.happens-before指的是一个操作执行的结果需要对另外一个操作可见,这两个操作既可以是在一个线程之内,也可以是在不同线程之间。例如A happens-before B,指的就是A的执行结果需要对B可见(B可以比A先执行,但是A的执行结果必须对B可见)

124.内存屏障分为两大类:编译器级别的内存屏障(语言级别的内存屏障)、CPU层面的内存屏障
· 编译器级别的四种内存屏障:loadload()、storestore()、loadstore()、storeload()
· loadload() load1的结果对load2可见
· storestore() store1的结果对store2可见
· loadstore() load的结果对store可见
· storeload() store的结果对load可见
· CPU层面提供了三种内存屏障:写屏障、读屏障、全屏障

125.重排序导致可见性问题的代码示例:
boolean flag = true;
int i = 3;

new Thread( () -> {
while(flag) {
i = 10;
flag = false;
}
}, "t0").start();

new Thread( () -> {
while(!flag) {
System.out.println(i);
}
}, "t1").start();

分析:由于重排序,flag = false先于i = 10,所以有可能导致打印出i的值为3而不是10,即线程t0中对i修改后的值对t1线程不可见

126.juc包指的是java.util.concurrent包,并发工具包

127.AQS是AbstractQueuedSynchronizer的简称,核心作用是对线程进行同步,没有获得锁的线程阻塞。如果能熟练掌握AQS的原理,则juc包其它的工具类也基本能轻松掌握
(AbstractQueuedSynchronizer底层数据结构是一个双向链表)

128.对ReentrantLock各种锁的描述:互斥锁、可重入锁、公平锁/非公平锁

129.ReentrantReadWriteLock为读写锁
1)使用方法为:“读线程”加读锁、“写线程”加写锁
2)产生的效果:读线程和读线程可以共享锁、读线程和写线程互斥、写线程写线程互斥
3)该锁的适用场景为读多写少的场景

130.ReentrantLock和ReentrantReadWriteLock并没有继承关系
· ReentrantLock实现了Lock接口
· ReentrantReadWriteLock实现了ReadWriteLock接口,ReadLock和WriteLock为ReentrantReadWriteLock的静态内部类

131.CAS指的是CompareAndSwap,比较并交换,其底层是一种CPU指令。CAS的典型使用模式是:首先从V中读取值A,并根据A计算新值B,然后再通过CAS以原子方式将V中的值由A变成B(只要在这期间没有任何线程将V的值修改为其他值)。由于CAS能检测到来自其他线程的干扰,因此即使不使用锁也能够实现原子的读-改-写操作序列

132.interrupt()方法不论是在哪个线程任务中调用,该方法由哪个线程对象调用中断的就是哪个线程
· 在main线程中中断线程t:Thread t = new Thread(); t.interrupt();
· 线程自己中断自己:Thread.currentThread().interrupt();

133.java.util.Lock的加锁原理就是基于AbstractQueuedSynchronizer来实现的
(ReentrantLock定义了一个静态内部类Sync继承AbstractQueuedSynchronizer。实际上,juc包中的大部分工具类都使用这种方式,如:ReentrantReadWriteLock、CountDownLatch、Semaphore)

134.juc包中的常见并发工具类:CountDownLatch、Semaphore、CyclicBarrier

135.await()、signal()方法定义在java.util.concurrent.locks.Condition中
· wait()和notify()是通过对象锁调用
· await()和signal()是通过Condition对象调用

136.阻塞队列BlockingQueue是java.util.concurrent包下的类,该类间接继承了Collection。工作原理:一个生产者线程往队列中写数据,对应add()方法;一个消费者线程从队列中取数据,对应take()方法

137.java.util.concurrent.ThreadPoolExecutor类是实现各种线程池的核心类,该类提供了各种构造方法

138.在使用ThreadPoolExecutor创建线程池时,这几个参数需要重点关注:
· 核心线程数(int corePoolSize)
· 最大线程数(int maximumPoolSize)
· 非核心线程数的存活时间(long keepAliveTime)
· 存活时间的单位(TimeUnit unit)
· 阻塞队列(BlockingQueue queue)
· 线程工厂(ThreadFactory threadFactory)
· 拒绝策略(RejectedExecutionHandler handler)
(最大线程数 - 核心线程数 = 非核心线程数。当:任务数 > 核心线程数 + 阻塞队列大小,会启动非核心线程)

139.一个线程的执行结果可以被其他线程获得。实际业务中可能有这样的场景:一个线程需要等待其他线程的处理结果,该线程需要拿到这些结果之后继续后续的业务处理

140.多线程中的Lambda表达式写法:() -> { "线程任务代码" },可以把[() -> { "线程任务代码" }]看成是Runnable或者Callable的实现类的对象

141.当调用sleep()、wait()方法时,会调用底层方法park()方法让线程挂起,当外界触发要求恢复线程执行时,会继续调用底层的unpark()方法让线程恢复(自己猜测,有待进一步考证)

142.关于创建线程和启动线程,创建线程指的是在jvm进程中开辟一条新的程序执行路径;启动线程指的是在这条程序执行路径中开始执行程序;这两者可以分别对应new Thread()和t.start()

143.“executorService.execute(() -> {});”本质上是一条程序语句,其中“() -> {}”本质上是一个线程任务对象

144.为什么ArrayList不是线程安全的,关于这一点需要根据ArrayList的源码来分析,如果脱离了源码,那么肯定无法很好地理解其为什么不是线程安全的

145.java.util.concurrent.CompletionService中的泛型为线程任务的返回值类型

146.假设有一个web应用,其web容器支持同时1000个并发(可以理解为web容器的线程池最大线程数为1000,第1000个之后的请求会被暂时阻塞,直到前面有请求任务执行完毕之后才会陆续接入被阻塞的请求),有一个业务创建了8个线程,对于该业务而言理论上服务器最多会同时创建(1+8)*1000 = 9000个线程

147.线程安全性的确切定义:当多个线程访问某个类时,这个类始终都能表现出正确的行为,那么就称这个类是线程安全的

148.多线程中的原子性指的是:一组程序语句作为一个不可分割的单元被执行

149.异常是针对于线程来说的而不是针对于jvm进程来说的,假设一段程序有多个线程在运行,如果某个线程发生异常,则其他线程是不会受到干扰的。可以通过异常栈轨迹的相关信息看到异常是哪个线程发生的

150.线程池大小的设置理论上取决于两个因素:要看业务的类型是IO密集型还是CPU密集型(计算密集型)
·如果是IO密集型,会存在IO阻塞的情况,为了更充分地利用CPU的资源,应该尽可能地调大线程池的线程数量
·如果是CPU密集型,可以从“2*CPU”和“CPU+1”两个维度去调试线程数的大小
·小结:具体的线程池参数设置还是要结合具体的业务场景去调试

标签:多线程,Thread,中断,主线,线程,执行
From: https://www.cnblogs.com/chaoshang8/p/18398166

相关文章

  • 【python】socket 入门以及多线程tcp链接
    Socket入门及多线程tcp链接网络基础知识三要素Socket是套接字的意思,是网络编程的核心对象,通信两端都独有自己的Socket对象,数据在两个Socket之间通过字节流(TCP协议)或者数据报包(UDP协议)的形式进行传输.本文主要针对tcp流程进行讲解socket-tcp流程图1.创建......
  • SQLServer事务复制延迟优化之多并行(多线程)复制
    事务复制的延迟在数据库的主从复制过程中,包括MySQL的主从复制,SQLServer的事务复制等等,鉴于主节点往往是并发写入的,而从节点(SQLServer中叫做订阅节点)在重放主节点的写操作的时候,往往会产生一定时间的延迟,如何降低这种复制延迟,并行复制或者说多线程复制是其中手段之一。 SQLServ......
  • 【Linux修行路】多线程——互斥量
    目录⛳️推荐一、多线程模拟抢票二、加锁——互斥量2.1pthread_mutex_init——初始化互斥量2.2pthread_mutext_destroy——销毁一个互斥量2.3pthread_mutex_lock——加锁2.4pthread_mutex_trylock——非阻塞的申请锁2.4pthread_mutex_unlock——解锁2.5定义一个......
  • Linux C++ 多线程高并发服务器实战项目一
    1、项目介绍1、按照包头+包体的格式收发数据包,解决粘包的问题2、非常完整的多线程高并发服务器3、根据收到数据包执行,不同的业务逻辑函数用到的技术:epoll高并发通讯技术,用的是水平触发【LT】水平触发模式通过线程池技术处理业务逻辑多线程、之间同步技术使用,互斥量、和条件变......
  • 二、并发编程与多线程-2.1、J.U.C和锁(下篇)
    2.1、J.U.C和锁(下篇)2.1.8、什么是可重入锁?它的作用是什么?答:在Java中,可重入锁是一种同步机制,它允许同一个线程多次获取同一个锁。当一个线程持有该锁时,它可以反复进入被该锁保护的代码块,而不会被阻塞。这种机制也被称为递归锁。比如synchronized锁和ReentrantLock锁都是可......
  • 多线程、任务、异步的区别
    Task和Thread的区别这是一个高频,深刻的问题,无论去哪都逃不过被询问这个问题。Task是基于Thread的,这是众所周知的。但是Task和Thread的联系如此简单和纯粹确实我没想到的。甚至只需要几十行代码就能呈现其原理。一个简单的模拟实例说明Task及其调度问题,这真是一篇好文章。任务体......
  • 【多线程】 - 实现方法以及自定义线程池
    概念进程进程是程序的基本执行实体线程线程是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。多个线程组成了多线程多线程应用场景软件中的耗时操作拷贝、迁移大文件加载大量的资源文件想让多个事情同时运行就需要多线程并发和并行并发在同一时刻......
  • 二、并发编程与多线程-2.1、J.U.C和锁(中篇)
    2.1、J.U.C和锁(中篇)2.1.4、什么是CAS?答:CAS是Java中Unsafe类里面的方法,全称是CompareAndSwap,是比较并交换的意思。作用就是保证在多线程环境下,对于修改共享变量操作的原子性。扩展:CAS保证修改共享变量操作原子性的实现逻辑:CAS方法里有三个参数,依次分别是共享变量的内......
  • flask多线程下数据库操作(简单示例)
    前言背景:开了两个线程操作数据库插入但是获取不到db的信息,自己摸索的方法不一定是最佳的,有更好的可以评论或私信,感谢大佬话不多说,直接上代码 #模型里面的多线程新增操作@staticmethoddefadd_users_by_thread(username,password,session):user=U......
  • 为什么多线程会带来性能问题?
    为什么多线程会带来性能问题?什么是性能问题在上一篇中,我们已经学习了多线程带来的线程安全问题,但对于多线程而言,它不仅可能会带来线程安全问题,还有可能会带来性能问题,也许你会奇怪,我们使用多线程的最大目的不就是为了提高性能吗?让多个线程同时工作,加快程序运行速度,为什么反而会带来......