Java多线程 相关概念总结

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/zx711166/article/details/84867042
什么是线程
  • 进程的最小单元
  • 线程自己不拥有系统资源
  • 一个线程可以创建和撤销另一个线程
  • 线程有就绪、阻塞、运行三种基本状态
  • 线程的两种实现方法
    • 继承 Thread 类,并重写run 函数
    • 实现 Runnable 接口,并重写 run 函数
线程、进程的区别
  • 进程:是资源(CPU、内存等)分配的基本单位,它是程序执行时的一个实例。
  • 线程:是程序执行时的最小单位,它是进程的一个执行流,是CPU调度和分派的基本单位,在多CPU环境下就允许多个线程同时运行。
线程安全、非线程安全的区别
类型 一致性 效率
线程安全 保证数据的一致性(对资源读写进行了控制) 单线程效率低(对资源读写进行了控制,增加了系统的开销)
非线程安全 不保证数据的一致性 单线程效率高
自旋锁、互斥锁的区别
类型 实现原理 区别
自旋锁 线程一直是running(加锁——>解锁),死循环检测锁的标志位 自旋锁是死循环检测,加锁全程消耗cpu,起始开销虽然低于互斥锁,但是随着持锁时间,加锁的开销是线性增长。
互斥锁 线程会从sleep(加锁)——>running(解锁),过程中有上下文的切换,cpu的抢占,信号的发送等开销。 互斥锁的起始原始开销要高于自旋锁,但是基本是一劳永逸,临界区持锁时间的大小并不会对互斥锁的开销造成影响。
什么是CAS
  • CAS(compare and swap,比较交换技术):是一种无锁的策略,用于鉴别线程冲突;一旦检测到冲突,就重试当前操作直到没有冲突为止
  • 基于这样的算法,CAS操作即使没有锁,也可以发现其他线程对当前线程的干扰(临界区值的修改),并进行恰当的处理。
乐观锁、悲观锁的区别
  • 乐观锁
    • 机制:数据进行提交更新的时候,才会正式对数据的冲突与否进行检测;若冲突,则宣布失败;否则更新数据。
    • 作用:避免使用长事务和锁机制,以免导致系统并发处理能力降低,保障系统生产效率。
  • 悲观锁
    • 机制:将在事务开始执行前或执行中申请锁定,执行完后再释放锁定。
    • 作用:对数据被意外修改持保守态度,依赖数据库原生支持的锁机制来保证当前事务处理的安全性,防止其他并发事务对目标数据的破坏或破坏其他并发事务数据。
什么是AQS
  • AQS(AbstractQueuedSynchronizer,高并发框架):是JDK下提供的一套用于实现基于FIFO等待队列的阻塞锁和相关的同步器的一个同步框架
  • 它为不同场景提供了实现锁及同步机制的基本框架,为同步状态的原子性管理、线程的阻塞、线程的解除阻塞及排队管理提供了一种通用的机制。
  • java.util.concurrent.locks 里面的锁机制就是基于AQS机制。
什么是原子操作
  • 原子操作:是指一个不受其他操作影响的操作任务单元。原子操作是在多线程环境下避免数据不一致必须的手段。
    • 示例:int++并不是一个原子操作,所以当一个线程读取它的值并加1时,另外一个线程有可能会读到之前的值,这就会引发错误。
什么是Executors框架?
  • 是一个根据一组执行策略调用、调度、执行和控制的异步任务的框架。
  • 无限制的创建线程会引起应用程序内存溢出。所以创建一个线程池是个更好的的解决方案,因为可以限制线程的数量并且可以回收再利用这些线程。利用Executors框架可以非常方便的创建一个线程池。
  • Executor框架同java.util.concurrent.Executor 接口在Java 5中被引入。
ThreadPool(线程池)用法与优势
  • java中的线程池是通过Executor框架实现的
  • ThreadPoolExecutor:线程池的具体实现类,一般用的各种线程池都是基于这个类实现的。
    • 第一:降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
    • 第二:提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。
    • 第三:提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控
什么是阻塞队列、如何使用阻塞队列来实现生产者-消费者模型
  • java.util.concurrent.BlockingQueue的特性是
    • 当队列是空,获取或删除元素,将会被阻塞
    • 当队列是满,添加元素,将会被阻塞。
    • 阻塞队列不接受空值,当你添加空值的时候,它会抛出NullPointerException。
    • 阻塞队列的实现都是线程安全的,所有的查询方法都是原子的并且使用了内部锁或者其他形式的并发控制。
  • BlockingQueue 接口是java collections框架的一部分,它主要用于实现生产者-消费者问题。
同步容器、并发容器
  • 同步容器:实现线程安全的方式就是将它们的状态封装起来,并在需要同步的方法上加上关键字synchronized。例如,Vector和HashTable是同步容器。
  • 并发容器:允许完全并发的读取,并且支持给定数量的并发更新。例如,ConcurrentHashMap是并发容器。
多线程的优缺点

优点:

  1. 在一个程序中,有很多的操作是非常耗时的,如数据库读写操作,IO操作等,如果使用单线程,那么程序就必须等待这些操作执行完成之后才能执行其他操作。使用多线程,可以在将耗时任务放在后台继续执行的同时,同时执行其他操作。
  2. 可以提高程序的效率。
  3. 在一些等待的任务上,如用户输入,文件读取等,多线程就非常有用了。

缺点:

  1. 使用太多线程,是很耗系统资源,因为线程需要开辟内存。更多线程需要更多内存。
  2. 影响系统性能,因为操作系统需要在线程之间来回切换。
  3. 需要考虑线程操作对程序的影响,如线程挂起,中止等操作对程序的影响。
  4. 线程使用不当会发生很多问题。
什么是多线程的上下文切换
  • 上下文切换:cpu通过时间片分配算法来循环执行任务,当前任务执行一个时间片后切换到下一个任务。但是,在切换前会保存上一个任务的状态,以便下次切换回这个任务时,可以再加载这个任务的状态。所以任务从保存到再加载的过程就是一次上下文切换
  • 多线程的上下文切换:多线程环境中,当一个线程的状态由Runnable转换为非Runnable(Blocked、Waiting、Timed_Waiting)时,相应线程的上下文信息(包括cpu的寄存器和程序计数器在某一时间点的内容等)需要被保存,以便相应线程稍后再次进入Runnable状态时能够在之前的执行进度的基础上继续前进。而一个线程从非Runnable状态进入Runnable状态可能涉及恢复之前保存的上下文信息。这个对线程的上下文进行保存和恢复的过程就被称为上下文切换
synchronized、reentrantLock的区别
  • synchronized:‘
    • 同步锁
    • JVM层面上实现
    • 在资源竞争不是很激烈的情况下,Synchronized的性能要优于ReetrantLock
    • 使用 synchronized ,A不释放,B将一直等下去,不能被中断
  • reentrantLock:
    • 可重入锁、可以指定公平锁还是非公平锁,具有锁等待,锁中断功能
    • JDK上实现的
    • 在资源竞争很激烈的情况下,Synchronized的性能要劣于ReetrantLock
    • 使用ReentrantLock,如果A不释放,可以使B在等待了足够长的时间以后,中断等待,而干别的事情
wait()、sleep()的区别
  • 在调用sleep()方法的过程中,线程不会释放对象锁。
  • 当调用wait()方法的时候,线程会放弃对象锁,进入等待此对象的等待锁定池,只有针对此对象调用notify()方法后本线程才进入对象锁定池准备。
线程的五个状态(五种状态,创建、就绪、运行、阻塞和死亡)

新建状态(New) —> 就绪状态(Runnable) —> 运行状态(Running) —> 阻塞状态(Blocked) —> 死亡状态(Dead)

Runnable接口、Callable接口的区别
Callable接口 Runnable接口
规定方法call() 规定方法run()
执行任务后可返回值 执行任务后不可返回值
call方法可以抛出异常 run方法不可以抛出异常
运行Callable任务可以拿到一个Future对象,表示异步计算的结果。
它提供了检查计算是否完成的方法,以等待计算的完成,并检索计算的结果。
通过Future对象可以了解任务执行情况,可取消任务的执行,还可获取执行结果
线程实现的四种方式
  1. 继承Thread,
  2. 实现Runable,
  3. 线程池框架使用ExecutorService、Callable、Future实现有返回结果的线程,
  4. 实现Callable接口通过FutureTask包装器来创建Thread线程
提交任务时,线程池队列已满,这时会发生什么

任务不能被调度执行那么ThreadPoolExecutor’s submit()方法将会抛出一个RejectedExecutionException异常。

锁的等级:方法锁、对象锁、类锁
  • 方法锁:修饰方法时,默认是当前对像作为锁的对象。
  • 对象锁:修饰代码块时,需要一个reference对象作为锁的对象。
  • 类锁:修饰类时,默认是当前类的Class对象作为锁的对象。

猜你喜欢

转载自blog.csdn.net/zx711166/article/details/84867042
今日推荐