首页 > 其他分享 >必知必会:多线程

必知必会:多线程

时间:2023-06-04 20:22:46浏览次数:40  
标签:调用 获取 必知 队列 任务 线程 必会 多线程 等待

  • 1.线程的6种状态

(1)New:初始状态,线程被创建,但是还没调用start方法。
(2)Running:就绪状态和运行状态,统称为运行状态
(3)Blocked:阻塞状态
(4)Waiting:等待状态,需要等待其他线程做出特定的动作(通知或中断)。
(5)Time-Waiting:超时等待状态,表示可以在指定的时间自行返回。
(6)Terminated:终止状态,表示当前线程已经执行完毕。

  • 2.线程间的通信方式

(1)使用volatile修饰字段,保证所有线程对变量访问的可见性,程序对该变量的访问需要从共享内存中获取,变更必须同步刷新共享内存。
(2)使用Synchronized关键字修饰方法或代码块,保证多线程环境下只有一个线程处于方法或代码块中,保证线程对变量访问的可见性和排他性。
(3)Java内置了等待 wait() /通知 notify() 机制,实现一个线程修改一个对象的值,另一个线程感知到变化,进行相应的操作。
(4)使用Thread.join方法,来保证另一个线程需等待调用join方法的线程终止。
(5)使用ThreadLocal线程变量,来保证线程间变量的隔离性。
(6)使用管道输入/输出流,用于线程之间的数据传输,传输的媒介为内存。具体实现:PipedOutputStream、PipedInputStream、PipedReader和PipedWriter,前两种面向字节,而后两种面向字符。

  • 3.ThreadLocal理解 ThreadLocal<T>

(1)Thread类中有一个类型为ThreadLocal.ThreadLocalMap的实例变量,每个线程都有一个属于自己的ThreadLocalMap。
(2)ThreadLocalMap内部维护着Entry数组,每个Entry代表的是一个完整的对象。
(3)每个线程往ThreadLocal设置值时,ThreadLocal本身不存储值,其实都是往自己的ThreadLocalMap存储,读取也是在自己的map里找对应的key。
(4)key为弱引用,value是强引用;如果使用不当会存在内存泄漏,所以需要用完后调用remove方法。

  • 4.锁的使用

(1)Synchronized:
修饰实例方法:作用于当前对象实例加锁,进入同步代码前要获取当前对象实例的锁。
修饰静态方法:作用于所有类的实例对象,是给当前类加锁,会进入同步代码块前要先获取当前class的锁。
修饰代码块:给给定对象加锁,如果参数是this或某个对象实例,则是实例锁;如果是类.class,则为类锁。

示例说明:
 public synchronized void handle() 表示要获取当前对象实例的锁才能进入该同步方法。
 public static synchronized void handle() 表示要获取当前calss的锁才能进入该同步静态方法。
 synchronized(this||object) 表示要获取给定对象实例的锁才能进入同步代码块。
 synchronized(类.class) 表示要获取给定class的锁才能进入同步代码块。

锁优化:1.6之前是重量级锁;之后进行了锁优化。
锁升级方向:无锁->偏向锁->轻量级锁->重量级锁。
synchronized与wait和notify方法结合实现等待 wait() /通知 notify()/notifyAll() 机制。

(2)Lock接口实现类ReentrantLock:可重入锁,可以使用构造函数设置参数来指定是公平锁还是非公平锁。
常用方法: lock() 、 unock() 。
ReentrantLock借助于Condition接口和newCondition方法实现等待 await() /通知 signal() 机制。

非公平锁:调用CAS进行抢锁,如果锁没有被占用,则直接占用锁。
公平锁:先判断等待队列是否有线程处理等待状态,如果有就不去抢锁,否则就调用CAS进行抢锁。

(3)ReadWriteLock接口实现类ReentrantReadWriteLock:读写锁
原则上读读不互斥、读写互斥、写写互斥,提供了 readLock() 、 writeLock() 分别用于获取读锁、写锁。

ReadWriteLock lock = new ReentrantReadWriteLock();
Lock readLock = lock.readLock();// 读锁 
Lock writeLock = lock.writeLock();// 写锁 
  • 5.notify()和notifyAll()区别

notify():随机唤醒一个在该共享变量上由于调用wait方法后被挂起的线程。
notifyAll():唤醒所有在该共享变量上由于调用wait方法后被挂起的线程。

  • 6.对AQS的了解

抽象同步队列,简称AQS,是一个先进先出(FIFO)的双向链表队列。
用于在多线程环境下,通过共享资源状态(state)和先进先出的等待队列来实现多线程访问共享资源的同步框架。
应用如下:
Semaphore信号量,state指的就是剩余的许可证数量;
ReentrantLock可重入锁,state值的是锁占有情况和重入次数计数;
CountdownLatch计数器,倒计时剩余值。

  • 7.CAS了解

CAS是一种实现线程安全的算法,可以表述为“如果预期值等于内存值,则修改为新值;否则继续循环进入下一次判断设置”。
CAS的操作底层是通过调用sun.misc.Unsafe类中的CompareAndSwap的方法来保证线程安全的。

上述过程,可能出现ABA问题,怎么解决?

ABA问题是两个线程中有一个线程1更新了多次,A->B->A,在这个过程中,另一个线程2可能第一次在A还没有更新为B时拿到了A,后面比对时候又看到了内存中的是多次更新后的A。

这种情况可以使用版本号控制来解决。

  • 8.用过线程池吗?

ThreadPoolTaskExecutor ThreadPoolExecutor
线程池的核心参数:
(1)核心线程数:CPU核数*2
(2)线程池等待队列,使用LinkedBlockingDeque
(3)最大线程数:核心线程数*2
(4)非核心闲置线程存活时间
(5)拒绝策略,使用ThreadPoolExecutor$AbortPolicy,直接丢弃任务,并抛出RejectedExecutionException异常

线程池的工作流程:
(1)线程池刚创建的时候,里面没有一个线程。
(2)调用execute方法添加一个任务时,线程池会计算正在运行的线程数。
(3)如果正在运行的线程数小于核心线程数,则会立即创建一个核心线程执行任务。
(4)如果大于核心线程数,则判断等待队列是否已满,如果没有满,则会加入等待队列。
(5)如果等待队列已满,则需判断正在运行的线程数是否大于最大线程数。
(6)如果不大于最大线程数,则创建非核心线程执行任务。
(7)如果当前等于最大线程数,且会根据拒绝策略做相应处理。
(8)如果后面线程闲置了,且大于设置的存活时间,则该线程会被停掉。
(9)当所有任务被完成后,线程数会缩小到设置的核心线程数。

  • 9.线程池异常怎么处理

常见的有try-catch捕获异常、submit执行,Future.get()获取异常。

  • 10.创建线程的几种方式

(1)直接使用Thread,或继承Thread类
(2)FutureTask实现Callable或Runnable接口
(3)ComplatableFuture,默认使用的ForkJoinPool线程池,可指定线程池ThreadPoolExecutor

  • 11.CountDownLatch

倒计时计数器, new CountDownLatch(int size) 初始化共享锁
(1)每个子线程执行完在finally代码块调用 countDown() 方法,使计数递减,每执行一次减1,直到0为止。
(2)主线程调用 await() 阻塞线程,当count=0时,唤醒正在等待的线程,可用来确保主线程等待子线程都完成后再执行。

应用场景:非常适合于对任务进行拆分,使其并行执行,总的执行时间取决于执行最慢的任务线程耗时。

  • 12.FutureTask

实现Callable或Runnable接口,搭配new Thread或线程池使用。

public class FutureTaskRunnable implements Runnable {
    @Override
    public void run() {
        System.out.println("FutureTask.run()");
    }
}

public class FutureTaskCallable implements Callable<Integer> {
    @Override
    public Integer call() throws Exception {
        System.out.println("FutureTask.call()");
        return 1;
    }
} 

(1) new Thread(new FutureTaskRunnable()).start(); 

(2) Executors.newFixedThreadPool(1).execute(new FutureTaskRunnable()); 
(3) Future<Integer> future = Executors.newFixedThreadPool(1).submit(new FutureTaskCallable()); 
 future .get() 方法获取返回值, get() 方法会阻塞主线程,如果异步任务非常耗时,会导致程序吞吐量下降。

  • 13.Fork/Join

(1)采用的是分而治之的思想,将一个规模为N的问题分解为K个规模较小的子问题,并对子问题的结果进行汇总。
(2)充分利用多核CPU,每个工作线程有自己的任务队列,如果它处理完自己的队列后,会去帮助其他工作线程处理任务。
(3)每个工作线程的任务队列是双端队列,自己是从头部获取任务,其他工作线程帮忙处理任务时,则是从尾部获取任务。

  • 14.ComplatableFuture

原理:异步执行主任务,将依赖任务加入到链表中,主任务执行完从链表中获取依赖任务执行。
其中依赖任务在加入链表时,会判断主任务是否执行完,如果执行完则立即执行依赖任务,不需要添加到链表。

可创建有返回值或无返回值的任务:
(1) CompletableFuture<U> supplyAsync(Supplier<U> supplier):有返回值; 
(2) CompletableFuture<Void> runAsync(Runnable runnable):无返回值; 
可以组合执行多个任务
(1) CompletableFuture<V> thenCombine(CompletionStage<? extends U> other, BiFunction<? super T, ? super U, ? extends V> fn) 组合两个 future,获取两个 future 的返回结果,并返回当前任务的返回值
(2) CompletableFuture.allOf(future1, future2).join() 等待指定的所有任务都完成。

  • 15.Exchanger:在两个线程运行的过程中交换数据。

标签:调用,获取,必知,队列,任务,线程,必会,多线程,等待
From: https://www.cnblogs.com/scorpio-cat/p/17456234.html

相关文章

  • 记录一次QT5下多线程使用Qxlsx操作写EXCEL表文件问题
    问题表述:一个主线程和两个子线程,两个子线程进行写EXCEL表格文件,线程1写demo_1.xlsx,线程2写demo_2.xlsx,运行一段时间后程序异常退出?。代码如下://两个线程代码一样,只是写入的文件名不同QXlsx::Documentdocument("demo_x.xlsx");introwLen=document.dimension()......
  • 【python】多线程
     在Python3中,通过threading模块提供线程的功能。原来的thread模块已废弃。但是threading模块中有个Thread类(大写的T,类名),是模块中最主要的线程类,一定要分清楚了,千万不要搞混了。threading模块提供了一些比较实用的方法或者属性,例如:方法与属性描述current_thread()返......
  • 集合,多线程,面向对象,方法覆盖
    集合:“父亲”collection"儿子":list:有序,有下标,查set:无序,无下标,修改(底层:内存存储方式)列表,不方便map:键值对,key(标号)-value(真实的值,储放的是分散的物品,小型数据库)多线程:进程(可以索取计算机运行资源)=多线程(不可以索取,只能进程的资源)oop面向对象=封装,继承,多态面向过程:按照......
  • 多线程的未捕获异常类 UncaughtExceptionHandler 的使用
    一、需要UncaughtExceptionHandler的原因1.主线程可轻松的发现异常,子线程的异常比较隐蔽,难以发现程序运行时,子线程发生了异常,并不影响主线程,也不会终止主线程的程序,主线程将继续执行,这时候子线程的异常可能就不会被察觉,就使得子线程的功能出了问题,但没发现。代码展示:/***......
  • 多线程安全的案例展示与解决方案
    一、概念1.什么是线程安全当多个线程访问一个对象时,如果不用考虑这些线程在运行时环境下的调度和交替执行,也不需要进行额外的同步,或者在调用方进行任何其他的协调操作,调用这个对象的行为都可以获得正确的结果,那这个对象是线程安全的。通俗来说就是:不管业务中遇到怎么的多个线......
  • 多线程-线程池与java内存模型
    多线程-线程池与java内存模型线程池的使用(思路:什么是线程池->他的基本构造以及参数含义->如何使用,使用过程中需要注意什么->有哪些好用的工具类)线程池的基笨概念:首先看一下的继承关系,其次看他的状态,它是利用int的高三位表示状态,比如111表示能接受任务,具体看下面第二章图接下来看......
  • SpringCloud大文件分片上传/多线程上传
    ​ 我们平时经常做的是上传文件,上传文件夹与上传文件类似,但也有一些不同之处,这次做了上传文件夹就记录下以备后用。这次项目的需求:支持大文件的上传和续传,要求续传支持所有浏览器,包括ie6,ie7,ie8,ie9,Chrome,Firefox,360安全浏览器,并且刷新浏览器后仍然能够续传,重启浏览器(关闭......
  • 不使用第三方框架编写的多线程断线续传功能
    一、背景最近需要个断线续传功能,但是觉得一些框架不太适合,所以基于原理编写了一个多线程断线续传功能支持技术分享,但是复制和转发我的博客时候请标明出处,谢谢 javascript:void(0)二、断线续传的个人理解:1、断线续传在个人理解,其实就是在出现正常下载流程之外的事情的时候,保存好当......
  • SpringBoot大文件分片上传/多线程上传
    ​ 这里只写后端的代码,基本的思想就是,前端将文件分片,然后每次访问上传接口的时候,向后端传入参数:当前为第几块文件,和分片总数下面直接贴代码吧,一些难懂的我大部分都加上注释了:上传文件实体类:看得出来,实体类中已经有很多我们需要的功能了,还有实用的属性。如MD5秒传的信息。pub......
  • SpringMVC大文件分片上传/多线程上传
    ​ javaweb上传文件上传文件的jsp中的部分上传文件同样可以使用form表单向后端发请求,也可以使用ajax向后端发请求    1.通过form表单向后端发送请求         <formid="postForm"action="${pageContext.request.contextPath}/UploadServlet"method="post"e......