并发编程知识

本文介绍了Java中创建线程的多种方式,包括线程池的使用和参数,线程的生命周期状态,线程池的拒绝策略以及工作原理。还讨论了MySQL事务的特性,消息队列的应用场景,以及Java并发编程中的锁机制、线程安全和同步原语。此外,文章提到了线程间的通信、线程池的管理和优化,以及如何避免死锁等问题。
摘要由CSDN通过智能技术生成

1.创建线程的方法

1.继承Thread类
2.实现Runnable接口
3.通过Callable和Future接口
4.基于线程池的方式(节约资源,避免频繁的创建和销毁线程)
上面三种方式最终都是实现Runnable接口

2.创建线程池的方式

Java 里面线程池的顶级接口是 Executor,但是严格意义上讲 Executor 并不是一个线程池,而只是一个执行线程的工具。真正的线程池接口是 ExecutorService

newCachedThreadPool 可缓存线程池
newFixedThreadPool 定长线程池
newScheduledThreadPool 可执行延迟任务的线程池
newSingleThreadExecutor 单个线程数的线程池
newSingleScheaduledThreadPool 单个线程可以执行延迟任务的线程池

3.线程池的7个参数

corePoolSize 核心线程大小
maximunPoolSize 线程池最大线程数
keepAliveTime 空闲线程存活时间
unit 空闲线程存活时间单位
workQuene工作队列
threadFactory 线程工厂
handler拒绝策略

4.线程的生命周期(状态)

新建,就绪,运行,阻塞,销毁

5.线程池的拒绝策略

ThreadPoolExecutor.AbortPolicy 丢弃任务并抛出RejectedExecutionException(默认拒绝策略)
ThreadPoolExecutor.DiscardPolicy:丢弃任务,但是不抛出异常
DiscardOldestPolicy:丢弃任务最前面的任务,然后重新提交被拒绝的任务
CallerRunsPolicy:由调用线程处理该任务

6.线程池工作原理

当前线程数<corePoolSize,创建新线程执行任务
当前线程>=corePoolSize,将任务放入BlockingQuene
如果阻塞队列已满,当前线程数<maximunPoolSize,创建新线程执行任务
如果阻塞队列已满,当前线程数>=maximunPoolSize,抛出异常
RejectedExecutionException,告诉调用者无法再接受任务了。

7.Mysql事物的特性

原子性
一致性/串行性
隔离性
持久性

8.消息队列的使用场景

应用耦合
异步处理
限流削峰:例如秒杀活动
消息驱动的系统

9.sleep与wait的区别

sleep()属于Thread类,wait()属于Object类
调用sleep()方法线程不会释放对象锁;调用 wait()方法的时候,线程会放弃对象锁

10.Java后台线程

1.守护线程–也称“服务线程”,他是后台线程,它有一个特性,即为用户线程 提供 公共服务,在没有用户线程可服务时会自动离开。
2.守护线程的优先级比较低
3.通过 setDaemon(true)来设置线程为“守护线程”;在 Daemon 线程中产生的新线程也是 Daemon 的;
4.线程则是 JVM 级别的;当垃圾回收线程是 JVM 上仅剩的线
程时,垃圾回收线程会自动离开。
5.与系统“同生共死”

11.Java锁

悲观锁:先上锁再处理。例如:传统关系型数据库中的行锁,表锁,读锁,写锁,synchronized关键字的实现原理。
乐观锁:不上锁。每次更新数据都先查,通过版本号等进行标记。适用于多读的场景

12.synchronized 和 ReentrantLock 的区别

1.ReentrantLock 是 API 级别的,synchronized 是 JVM 级别的;
2.ReentrantLock是JDK的一个类,而 synchronized 是 Java 中的关键字;
3.synchronized 会自动加锁与释放锁;而 Lock 需要手动加锁与释放锁;
4.Lock公平锁与非公平锁,synchronized 非公平锁
5.synchronized
在这里插入图片描述

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

1.使用退出标志,使线程正常退出,也就是run()方法完成后线程终止
2.使用stop()方法强行终止,但是不推荐这个方法,因为stop,suspend及resume一样都是过期作废方法。
3.使用interrupt方法中断线程。

class MyThread extends Thread {
 volatile boolean stop = false;
 public void run() {
 while (!stop) {
 System.out.println(getName() + " is running");
 try {
 sleep(1000);
 } catch (InterruptedException e) {
 System.out.println("week up from blcok...");
 stop = true; // 在异常处理代码中修改共享变量的状态
 }
 }
 System.out.println(getName() + " is exiting...");
 }
}
class InterruptThreadDemo3 {
 public static void main(String[] args) throws InterruptedException {
 MyThread m1 = new MyThread();
 System.out.println("Starting thread...");
 m1.start();
 Thread.sleep(3000);
 System.out.println("Interrupt thread...: " + m1.getName());
 m1.stop = true; // 设置共享变量为true
 m1.interrupt(); // 阻塞时退出阻塞状态
 Thread.sleep(3000); // 主线程休眠3秒以便观察线程m1的中断情况
 System.out.println("Stopping application...");
 }

}

14.notify()和notifyAll()有什么区别

1.notify()可能会导致死锁,而notifyAll()不会;
2.notify()任何时候只有一个线程可以获得锁,也就是说只有一个线程可以运行sychronized中的代码,使用notifyAll()可以唤醒所有处于wait状态的线程,使其重新进入锁争夺的队列中;
3.wait()应配合while循环使用,不应使用if,务必在wait()调用前后都检查条件,如果不满足,必须使用notify()唤醒另外的线程来处理,自己继续wait()直至条件满足再往下执行。

15.谈谈你对线程池的理解

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

16.Java程序是如何执行的

在这里插入图片描述

16.锁优化机制

自旋锁
自适应锁
锁消除
锁粗化
偏向锁
轻量级锁

17.进程和线程的区别

1.进程是操作系统进行资源分配和调度的基本单位。而线程是进程的一部分,是 CPU 调度的基本单位,共享进程的内存空间和其他资源。
2.进程之间是相互独立的,每个进程都有自己的地址空间,不能直接访问其他进程的数据;而线程之间共享同一进程的地址空间,可以直接访问进程中的所有数据
3.创建或撤销进程时,系统需要为之分配或回收相应的资源,代价较高;而创建或撤销线程则代价较小,因为它们共享了进程的资源。
4.多进程程序更加稳定,一个进程崩溃不会影响其他进程的运行;但多线程程序的稳定性要求更高,一个线程崩溃可能会导致整个进程崩溃。

18.产生死锁的四个必要条件

  1. 互斥条件:一个资源每次只能被一个线程使用
  2. 请求与保持条件:一个线程因请求资源而阻塞时,对已获得的资源保持不放
  3. 不剥夺条件:进程已经获得的资源,在未使用完之前,不能强行剥夺
  4. 循环等待条件:若干线程之间形成一种头尾相接的循环等待资源关系

18.线程安全需要保证几个基本特征

原子性
可见性
有序性

19.线程之间是如何通信的

线程之间可以通过共享内存或消息传递的方式进行通信。

共享内存:多个线程可以同时访问同一块内存区域,通过读写该内存区域来实现数据的交换和共享。在使用共享内存时需要注意同步问题,避免出现竞态条件等问题。

消息传递:不同的线程之间通过发送和接收消息来进行通信。发送线程将消息发送到消息队列中,接收线程从消息队列中获取消息并进行处理。在使用消息传递时需要考虑到消息的格式、大小和传输方式等问题。

无论是共享内存还是消息传递,都需要对线程的操作进行同步控制,以避免出现死锁、竞态条件等问题

19.说说ThreadLocal原理

ThreadLocal 是 Java 中的一个线程局部变量,它可以为每个线程单独维护一份变量副本。这意味着多个线程同时访问某个 ThreadLocal 变量时,实际上每个线程都会得到自己所对应的变量副本,并且彼此之间互不影响。

ThreadLocal 的实现原理是,它在每个线程中都创建了一个 Map 对象,用于存储该线程需要独立维护的变量副本。当调用 ThreadLocal 的 set() 方法设置变量值时,实际上是将该变量值存储到当前线程的 Map 对象中;而调用 get() 方法获取变量值时,实际上是从当前线程的 Map 对象中获取对应的变量值。这样就保证了每个线程对 ThreadLocal 变量的访问都是线程安全的。

需要注意的是,由于 ThreadLocal 维护的变量副本是与线程相关的,因此在使用完毕后一定要记得及时清理,以免造成内存泄漏。通常情况下,在每个线程结束时,应该显式地调用 ThreadLocal 的 remove() 方法,以清除该线程中保存的变量副本。

20.说说CyclicBarrier和CountDownLatch的区别

CyclicBarrier和CountDownLatch都是Java中用来协调多个线程之间同步的工具类,但它们在实现方式和使用场景上存在一些不同。

CountDownLatch适用于等待一组线程完成某些操作后再执行下一步操作的情况。它的作用是让一个或多个线程等待其他线程执行完任务再执行。在CountDownLatch中,线程等待的条件是计数器的值为0,当计数器的值减到0时,被等待的线程才会继续执行。CountDownLatch只能使用一次,计数器的值不能重置。

CyclicBarrier也是多线程同步的一种机制,但它允许一组线程互相等待,直到所有线程都到达一个公共的屏障点,然后再继续执行。与CountDownLatch不同,CyclicBarrier的计数器可以重复使用。每当一个线程到达屏障点时,计数器的值就会减1,直到计数器的值减至0时,所有线程就会同时继续执行。

总之,CountDownLatch适用于等待其他线程执行完毕再执行某些操作的情况,而CyclicBarrier则适用于需要所有线程都到达一个屏障点之后再继续执行的情况。

21.线程池提交一个任务的流程

在这里插入图片描述 在这里插入图片描述

22.线程池有几种状态,分别是如何变化的

在这里插入图片描述
在这里插入图片描述

23.如何优雅的停止一个线程

在这里插入图片描述
通过interupt()方法来中断

24.线程的核心线程数,最大线程数如何设置

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
根据业务合理设置

25.并发三大特性(可见性,有序性,原子性)

并发可见性
在这里插入图片描述
并发原子性
在这里插入图片描述
在这里插入图片描述
并发有序性
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

26.为什么不建议使用Executors创建线程池

在这里插入图片描述
在这里插入图片描述

27.sychronized锁升级过程

在这里插入图片描述
在这里插入图片描述

29.线程池中阻塞队列的作用?为什么是先添加队列而不是先创建最大线程?

在这里插入图片描述

30.并发、并行、串行的区别

在这里插入图片描述

31.什么是死锁?如何避免死锁

死锁是指两个或者两个以上的进程(线程)在执行的过程中,由于竞争资源而造成的阻塞问题,若无外力的作用下会无法继续推进,此时系统称之为死锁状态。

为了避免死锁,可以采取以下几种方法:

  1. 避免一个线程同时获取多个锁,并且其他线程也需要这些锁的情况,可以通过对锁的获取顺序进行统一,降低死锁的概率。
  2. 尽量减小锁的作用域,即只在必要的部分进行加锁,这样可以减少锁的竞争,降低死锁的概率。
  3. 使用定时锁,即在获取锁的时候设置超时时间,如果超时则放弃获取,避免长时间等待。
  4. 使用可重入锁,即同一个线程可以重复获取同一把锁,这样可以避免死锁问题。
  5. 尽量避免嵌套锁的使用,如果必须使用嵌套锁,可以采用锁的升级和降级策略,即先获取较低级别的锁,再获取较高级别的锁,避免死锁的发生。

总之,避免死锁需要谨慎地使用锁,并采取适当的策略来减少并发问题。在设计并发程序时,需要注意锁的使用方法和策略,以避免出现死锁和其他并发问题。

32.什么是阻塞队列和非阻塞队列?Java中提供了哪些阻塞队列和非阻塞队列

阻塞队列和非阻塞队列是并发编程中常用的两种队列类型。

阻塞队列是一种当队列为空时,从队列中获取元素的操作将会被阻塞,或者当队列已满时,向队列中添加元素的操作将会被阻塞,直到队列非空或非满时才能继续执行。Java中提供了许多阻塞队列,例如ArrayBlockingQueue、LinkedBlockingQueue、SynchronousQueue等。这些阻塞队列在多线程并发编程中非常有用,可以避免多个线程同时访问共享资源引起的并发问题。

非阻塞队列是一种当队列为空时,从队列中获取元素的操作将会返回null,或者当队列已满时,向队列中添加元素的操作将会返回false,而不是阻塞队列的操作。Java中提供了许多非阻塞队列,例如ConcurrentLinkedQueue、CopyOnWriteArrayList等。这些非阻塞队列在多线程并发编程中也非常有用,可以避免阻塞队列带来的阻塞问题,提高程序的性能和响应性。

总之,阻塞队列和非阻塞队列是并发编程中常用的两种队列类型,它们各自具有不同的特点和适用场景。在选择使用哪种类型的队列时,需要根据具体的需求和情况来决定。

33.解释一下synchronized、volatile和Lock的区别?

synchronized、volatile和Lock是Java中常见的三种线程同步机制,它们之间有一些区别:

  1. synchronized:synchronized是Java中的关键字,它可以用来修饰方法或代码块。当一个线程进入synchronized修饰的方法或代码块时,它会获取一个锁,其他线程如果要进入这个方法或代码块,则需要等待这个锁被释放。synchronized的实现是基于JVM的内置锁,因此它是非公平锁,无法响应中断。另外,synchronized只支持单一对象的锁,如果要锁多个对象,需要使用多个synchronized修饰符。
  2. volatile:volatile是Java中的关键字,它可以用来修饰变量。当一个变量被volatile修饰时,它会保证变量的可见性,即每次读取该变量的值都会从主内存中获取最新的值,而不是从本地缓存中读取。但是,volatile并不能保证原子性,即多个线程同时修改同一个volatile变量时,可能会出现数据竞争的情况。另外,volatile的实现是基于JVM的内存模型,因此它是非阻塞锁。
  3. Lock:Lock是一个接口,它是Java中提供的另一种线程同步机制。与synchronized不同,Lock可以通过响应中断方法让等待锁的中断,而synchronized不行,会让等待的线程一直等待下去。另外,Lock可以支持多对象监控器控制线程,效率高效。Lock的实现不是基于JVM的内置锁,因此它可以在不同的JVM实现中表现出不同的性能和行为。

总之,synchronized、volatile和Lock是三种不同的线程同步机制,它们各自具有不同的特点和适用场景。在使用时需要根据具体的需求和情况来选择合适的机制。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值