太全了!多线程问题概述

目录

1.进程与线程的区别?

2.创建线程有几种方式?

3.Runnable与Callable的区别?

4.线程池有多少种?

5.手动创建线程池的时候ThreadPoolExcutor有多少个参数,以及参数的含义?

6.线程池中的拒绝策略有哪些?

7.线程池的工作流程是什么?

8.线程安全的问题有哪些?

9.怎么解决线程安全问题?

10.synchronized与volatile的作用与区别?

11.JMM特征

12.synchronized锁升级过程?

13.什么是偏向锁,轻量级锁,重量级锁?

14.介绍一下CAS,以及ABA问题?

15.Reentrantlock的特性以及和synchronized的区别?

16.JUC包下的工具类有哪些?

17.线程安全的集合类有哪些?

18.ConcurrentHashMap介绍一下?

19.死锁问题?

20.ThreadLocal介绍一下?


1.进程与线程的区别?

1.进程中包含线程,每一个进程都至少有一个线程(主线程)

2.进程是申请资源的最小单位,线程是cpu调度的最小单位

3.线程之间可以共享进程申请的系统资源

4.一个进程如果崩溃,就有可能影响到进程

2.创建线程有几种方式?

1.继承 Thread 类

2.实现 Runnable 接口

3.通过 ExecutorService 和 Callable\ 实现有返回值的线程

4.基于线程池的execute(),创建临时线程

其中1.2有三种扩展方法,详情可见

java线程初识(一)_北~笙的博客-CSDN博客今天开始,我开始分享一些自己在学习javaEE过程中对于知识的总结和看法,希望大家多多指正。https://blog.csdn.net/qq_53679373/article/details/129183742

3.Runnable与Callable的区别

1.Callable规定的方法是call(),Runnable规定的方法是run().

2.Callable的任务执行后可返回值,而Runnable的任务是不能返回值的

3.call方法可以抛出异常,run方法不可以

4.运行Callable任务可以拿到一个Future对象,Future 表示异步计算的结果。(PS: 特别注意,executorService.submit(Runnable task) 也会返回future, 但是没有future的效果 )

4.线程池有多少种?

 5.手动创建线程池的时候ThreadPoolExcutor有多少个参数,以及参数的含义?

上一个问题中我们展示了最常用的JDK自带的线程池,但是我们不建议使用,自己实现线程池

corePoolSize:核心线程的数量,决定了是否创建新的线程来执行任务 

maximumPoolSize:最大线程数量,线程池中允许创建线程地最大数量

keepAliveTime:临时线程存活的时间

unit:临时线程存活的时间单位

workQueue:用于存放或组织的阻塞队列

threadFactory:线程工厂,用于创建线程执行任务

handler:拒绝策略,当线程池处于饱和时,使用某种策略来拒绝任务提交
详细可见:java线程池过程理解_北~笙的博客-CSDN博客本文主要描述了线程池的相关概念以及如何使用线程池,还详细的介绍了线程池的工作流程,以及对于平常如何正确使用线程池做了基本的阐述https://blog.csdn.net/qq_53679373/article/details/129862352

 6.线程池中的拒绝策略有哪些?

AbortPolicy(中止策略):当触发拒绝策略时,直接抛出拒绝执行的异常,中止策略的意思也就是打断当前执行流程,ThreadPoolExecutor中默认策略就是AbortPolicy
CallerRunsPolicy(调用者运行策略):当触发拒绝策略时,只要线程池没有关闭,就由提交任务的当前线程(当前线程)处理
DiscardOldestPolicy(弃老策略):如果线程池未关闭,就弹出队列头部的元素,然后尝试执行(放弃最早等待的任务)
DiscardPolicy(丢弃策略):直接静悄悄的丢弃这个任务,不触发任何动作(放弃最新的任务)

7.线程池的工作流程是什么?

执行任务在被添加到线程池中后,首先会根据当前任务来判断创建几个核心线程去执行,如果任务过多,多余的部分会添加到队列中,队列不满的情况下会等待,如若队列添加满,会创建临时线程,而且会一次性创建到最大的线程数量来执行任务,如若线程数已经达到最大而且队列也已经添加满,任务过多的话就会执行拒绝策略
 

8.线程安全的问题有哪些?

 1.线程抢占式执行

 2.多个线程同时修改一个变量

 3.原子性

 4.内存可见性---JMM(java内存模型)

 5. 有序性--指令重排序

9.怎么解决线程安全问题?

方法一:使用synchronized与volatile关键字

方法二:使用Lock接口下的实现类

方法三:使用线程本地存储ThreadLocal

方法四:使用乐观锁机制

10.synchronized与volatile的作用与区别?

  • volatile不需要加锁,比synchronized更轻量级,不会阻塞线程;
  • 从内存可见性角度,volatile读相当于加锁,volatile写相当于解锁;
  • synchronized既能够保证可见性,又能保证原子性,而volatile只能保证可见性,无法保证原子性。

11.JMM特征

 每一个工作内存都是相互独立的,相互之间不可以访问,内存可见性就是要保证一个线程改变了一个变量的值要让其他线程可以感知到。由于多个线程之间目前不能感知其他线程对变量做出的修改,所以造成了线程不安全的问题。

 JMM规定

     ···所有线程不能直接修改主内存中的共享变量

     ···如果需要修改内存中的共享变量,就现需要把这个变量从主内存中复制到工作内存中,修改完成后再刷新回主内存

     ···各个线程之间不能相互通信,做到内存级别的线程隔离

     ···如果要通过某种方式实现线程间的相互通信,就叫内存可见性

12.synchronized锁升级过程?

13.什么是偏向锁,轻量级锁,重量级锁?

13题与14题详细解释见下:

面试官:说一下synchronized锁升级过程_北~笙的博客-CSDN博客一篇文章带你搞懂面试官最喜欢问的synchronized锁升级过程https://blog.csdn.net/qq_53679373/article/details/129927006

14.介绍一下CAS,以及ABA问题?

CAS(Compare-and-Swap),即比较并替换,是一种实现并发算法时常用到的技术,Java并发包中的很多类都使用了CAS技术。

   在这个机制中有三个核心的参数:

主内存中存放的共享变量的值:V(一般情况下这个V是内存的地址值,通过这个地址可以获得内存中的值)
工作内存中共享变量的副本值,也叫预期值:A
需要将共享变量更新到的最新值:B
CAS操作直接修改的就是内存中的值,每次都会去读,去比较,去修改指定内存地址的值,从而保证了原子性的操作。

什么是ABA问题?ABA问题怎么解决?

CAS 的使用流程通常如下:1)首先从地址 V 读取值 A;2)根据 A 计算目标值 B;3)通过 CAS 以原子的方式将地址 V 中的值从 A 修改为 B。

但是在第1步中读取的值是A,并且在第3步修改成功了,我们就能说它的值在第1步和第3步之间没有被其他线程改变过了吗?

如果在这段期间它的值曾经被改成了B,后来又被改回为A,那CAS操作就会误认为它从来没有被改变过。这个漏洞称为CAS操作的“ABA”问题。Java并发包为了解决这个问题,提供了一个带有标记的原子引用类“AtomicStampedReference”,它可以通过控制变量值的版本来保证CAS的正确性。因此,在使用CAS前要考虑清楚“ABA”问题是否会影响程序并发的正确性,如果需要解决ABA问题,改用传统的互斥同步可能会比原子类更高效。或者我们可以给预期值加一个属性,记录一下修改的次数(版本号)。

15.Reentrantlock的特性以及和synchronized的区别?

详情请看:java显式锁和隐式锁的区别_北~笙的博客-CSDN博客一篇文章带你彻底搞懂显式锁和隐式锁之间的区别https://blog.csdn.net/qq_53679373/article/details/129935791

16.JUC包下的工具类有哪些?

详细请看:JUC常用并发工具类_北~笙的博客-CSDN博客四个案例带你彻底搞懂java最常见的并发工具类,semaphore,countdownlatch,exchanger,cyclicbarrier。https://blog.csdn.net/qq_53679373/article/details/129944755

17.线程安全的集合类有哪些?

一:早期的线程安全集合类

1.1Vector
Vector和ArrayList类似,是长度可变的数组,与ArrayList不同的是,Vector是线程安全的,它几乎给所有的public方法都加上了sychronized关键字。由于加锁倒是性能降低,在不需要并发访问时,这种强制性的同步就显得多余,所以现在几乎没有什么人在使用。

1.2 HashTable
HashTable和HashMap类似,不同的是HashTable是线程安全的,它也是几乎的给所有的public方法都加上了sychronized关键字,还有一个不同点是:**HashTable的K, V都不能是null,但是HashMap可以。**现在HashTable也是因为性能的问题,几乎没有人使用了。

二、Collections包装方法

由于ArrayListHashMap性能好,但是不是线程安全,所以Collections工具类中提供了相应的包装方法将他们包装成相应的线程安全的集合:

List<E> synchronizedList = Collections.sychronizedList(new ArrayList<E>());

Set<E> sychronizedSet = Collections.sychronizedSet(new HashSet<E>());

Map<K, V> synchronizedMap = Collections.sychronizedMap(new HashMap<K, V>());

三、java.util.concurrent包中的集合

3.1. ConcurrentHashMap

ConcurrentHashMap和HashTable都是线程安全的集合,它们的不同主要是加锁粒度上的不同。HashTable的加锁方法是给每个方法加上synchronized关键字,这样锁的是整个对象。而ConcurrentHashMap是有更细粒度的锁。在JDK1.8之前,ConcurrentHashMap加的是分段锁,即Segment,每个Segment含有整个table的一部分,这样不同分段之间的并发操作就互不影响。
JDK1.8之后对此进行了进一步的改进,取消了Segment,直接在table元素上加锁,实现对每一行加锁,进一步减小了并发冲突的概率。

3.2 CopyOnWriteArrayList和CopyOnWriteArraySet

针对涉及到数据修改的部分,都会使用ReentrantLock锁住操作,并将修改或添加的元素,通过拷贝的方式,加入数组中,最后修改数组的引用为新复制的数组。读取时直接读取数组,不需要获取锁。

18.ConcurrentHashMap介绍一下?

ConcurrentHashMapHashMap一样,是一个存放键值对的容器。使用hash算法来获取值的地址,因此时间复杂度是O(1)。查询非常快。
同时,ConcurrentHashMap是线程安全的HashMap。专门用于多线程环境。

既然谈到了ConcurrentHashMap,那ConcurrentHashMap与Hashmap与Hashtable有什么区别呢

1. HashMap
HashMap是线程不安全的,因为HashMap中操作都没有加锁,因此在多线程环境下会导致数据覆盖之类的问题,所以,在多线程中使用HashMap是会抛出异常的。

2. HashTable
HashTable是线程安全的,但是HashTable只是单纯的在put()方法上加上synchronized。保证插入时阻塞其他线程的插入操作。虽然安全,但因为设计简单,所以性能低下。

3. ConcurrentHashMap
ConcurrentHashMap是线程安全的,ConcurrentHashMap并非锁住整个方法,而是通过原子操作和局部加锁的方法保证了多线程的线程安全,且尽可能减少了性能损耗。

4.ConcurrentHashMap对于写操作put加锁,对于读操作get不加锁

5.ConcurrentHashMap对于共享变量大量运用的volatile关键字去修饰

6.ConcurrentHashMap对扩容做了优化

19.死锁问题?

首先我们应该了解造成死锁的原因是什么?

1.互斥访问:例如线程1拿到了锁A,那么线程2就不能同时拿到锁

2.不可抢占:获取到锁的线程,除非它自己释放掉锁,不然其他线程不能抢占过来

3.保持与请求:例如线程1已经获取到了锁A,还要在这个基础上去获取锁B

4.循环等待:例如线程1等待线程2释放锁,线程2等待线程3释放锁,线程3等待线程1释放锁

那么我们应该如何解决死锁问题呢?

1.互斥访问:锁的基本特征,不能打破

2.不可抢占:锁的基本特征,不能打破

3.保持与请求:从代码实现和设计的角度来看可以改变保持与请求的顺序,也是获取锁的顺序

4.循环等待:破坏循环等待条件,需要对资源进行排序,然后按序申请资源。

20.ThreadLocal介绍一下?

1. ThreadLocal是什么?

从名字我们就可以看到ThreadLocal 叫做本地线程变量,意思是说,ThreadLocal 中填充的的是当前线程的变量,该变量对其他线程而言是封闭且隔离的,ThreadLocal 为变量在每个线程中创建了一个副本,这样每个线程都可以访问自己内部的副本变量。

2. ThreadLocal怎么用?

下面让我们来看一个例子:

public class ThreadLocalDemo {
    public static void main(String[] args) {
        ThreadLocal<Integer> threadLocal = new ThreadLocal<>();
        Thread thread0 = new Thread(() -> {
            int count = 0;
            threadLocal.set(count);
            System.out.println(Thread.currentThread().getName() + ":" + count);
        });
        thread0.start();
        Thread thread1 = new Thread(() -> {
            int count = 1;
            threadLocal.set(count);
            System.out.println(Thread.currentThread().getName() + ":" + count);
        });
        thread1.start();
        Thread thread2 = new Thread(() -> {
            int count = 2;
            threadLocal.set(count);
            System.out.println(Thread.currentThread().getName() + ":" + count);
        });
        thread2.start();
        Thread thread3 = new Thread(() -> {
            int count = 3;
            threadLocal.set(count);
            System.out.println(Thread.currentThread().getName() + ":" + count);
        });
        thread3.start();
    }
}

从结果可以看到,每一个线程都有自己的local 值,这就是TheadLocal的基本使用 。

3.看看set(T value)get()方法的源码

一句话总结:每个线程都有一个 ThreadLocalMap(ThreadLocal内部类),Map 中元素的键为 ThreadLocal,而值对应线程的变量副本。Map 是数组实现,使用线性探测解决hash冲突,需要手动调用set、get、remove防止内存泄漏。ThreadLoal 变量,线程局部变量,同一个 ThreadLocal 所包含的对象,在不同的 Thread 中有不同的副本。ThreadLocal 变量通常被private static修饰。当一个线程结束时,它所使用的所有 ThreadLocal 相对的实例副本都可被回收。

一个线程内可以存在多个 ThreadLocal 对象,所以其实是 ThreadLocal 内部维护了一个 Map ,这个 Map 不是直接使用的 HashMap ,而是 ThreadLocal 实现的一个叫做 ThreadLocalMap 的静态内部类。而我们使用的 get()、set() 方法其实都是调用了这个ThreadLocalMap类对应的 get()、set() 方法。

4.ThreadLocalMap 和 HashMap 区别

ThreadLocalMap 和HashMap的功能类似,但是实现上却有很大的不同:

HashMap 的数据结构是数组+链表,HashMap 是通过链地址法解决hash 冲突的问题,HashMap 里面的Entry 内部类的引用都是强引用。

ThreadLocalMap的数据结构仅仅是数组,ThreadLocalMap 是通过开放地址法来解决hash 冲突的问题,ThreadLocalMap里面的Entry 内部类中的key 是弱引用,value 是强引用

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

北~笙

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值