千家信息网

多线程与高并发的知识点总结

发表于:2026-11-03 作者:千家信息网编辑
千家信息网最后更新 2026年11月03日,这篇文章主要介绍"多线程与高并发的知识点总结",在日常操作中,相信很多人在多线程与高并发的知识点总结问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答"多线程与高并发的知识点
千家信息网最后更新 2026年11月03日多线程与高并发的知识点总结

这篇文章主要介绍"多线程与高并发的知识点总结",在日常操作中,相信很多人在多线程与高并发的知识点总结问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答"多线程与高并发的知识点总结"的疑惑有所帮助!接下来,请跟着小编一起来学习吧!

1. 创建线程的四种方式

  • 实现Runnable 重写run方法

  • 继承Thread 重写run方法

  • 线程池创建 Executors.newCachedThreadPool()

  • 实现Callable接口

2. Thread线程操作方法

当前线程睡眠指定mills毫秒

  • Thread.sleep([mills])

当前线程优雅让出执行权

  • Thread.yield()

例如Thread t1, t2,在t2的run方法中调用t1.join(),线程t2将等待t1完成后执行

  • join

3. Thread状态

状态使用场景
NEWThread被创建之后,未start之前
RUNNABLE在调用start()方法之后,这也是线程进入运行状态的唯一一种方式。
具体分为ready跟running,当线程被挂起或者调用Thread.yield()的时候为ready
WAITING当一个线程执行了Object.wait()的时候,它一定在等待另一个线程执行Object.notify()或者Object.notifyAll()。
或者一个线程thread,其在主线程中被执行了thread.join()的时候,主线程即会等待该线程执行完成。当一个线程执行了LockSupport.park()的时候,其在等待执行LockSupport.unpark(thread)。当该线程处于这种等待的时候,其状态即为WAITING。需要关注的是,这边的等待是没有时间限制的,当发现有这种状态的线程的时候,若其长时间处于这种状态,也需要关注下程序内部有无逻辑异常。

TIMED_WAITING

这个状态和WAITING状态的区别就是,这个状态的等待是有一定时效的
Thread.sleep(long)
Object.wait(long)
Thread.join(long)
LockSupport.parkNanos()
LockSupport.parkUntil()
BLOCKED在进入synchronized关键字修饰的方法或代码块(获取锁)时的状态
TERMINATED线程执行结束之后的状态。
线程一旦终止了,就不能复生。
在一个终止的线程上调用start()方法,会抛出java.lang.IllegalThreadStateException异常

4. synchronized

  • 锁住的是对象而不是代码

  • this 等价于 当前类.class

  • 锁定方法,非锁定方法同时进行

  • 锁在执行过程中发生异常会自动释放锁

  • synchronized获得的锁是可重入的

  • 锁升级 偏向锁-自旋锁-重量级锁

  • synchronized(object)不能用String常量/Integer,Long等基本数据类型

  • 锁定对象的时候要保证对象不能被重写,最好加final定义

4. volatile

  • 保证线程可见性

  • 禁止指令重排序

  • volatile并不能保证多个线程修改的一致性,要保持一致性还是需要synchronized关键字

  • volatile 引用类型(包括数组)只能保证引用本身的可见性,不能保证内部字段的可见性 volatile关 键字只能用于变量而不可以修饰方法以及代码块

5. synchronized与AtomicLong以及LongAdder的效率对比

Synchronized 是需要加锁的,效率偏低;AtomicLong 不需要申请锁,使用CAS机制;LongAdder 使用分段锁,所以效率好,在并发数量特别高的时候,LongAdder最合适

6. ConcurrentHashMap的分段锁原理

分段锁就是将数据分段上锁,把锁进一步细粒度化,有助于提升并发效率。HashTable容器在竞争激烈的并发环境下表现出效率低下的原因是所有访问HashTable的线程都必须竞争同一把锁,假如容器里有多把锁,每一把锁用于锁容器其中一部分数据,那么当多线程访问容器里不同数据段的数据时,线程间就不会存在锁竞争,从而可以有效提高并发访问效率,这就是ConcurrentHashMap所使用的锁分段技术。首先将数据分成一段一段地存储,然后给每一段数据配一把锁,当一个线程占用锁访问其中一个段数据的时候,其他段的数据也能被其他线程访问。

7. ReentrantLock

ReentrantLock可以替代synchronized 但是ReentrantLock必须手动开启锁/关闭锁,synchronized遇到异常会自动释放锁,ReentrantLock需要手动关闭,一般都是放在finally中关闭 定义锁 Lock lock = new ReentrantLock(); 开启 lock.lock(); 关闭 lock.unlock(); 使用Reentrantlock可以进行"尝试锁定"tryLock,这样无法锁定,或者在指定时间内无法锁定,线程可以决定是否继续等待。使用tryLock进行尝试锁定,不管锁定与否,方法都将继续执行 可以根据tryLock的返回值来判定是否锁定 也可以指定tryLock的时间,由于tryLock(time)抛出异常,所以要注意unclock的处理,必须放到finally中,如果tryLock未锁定,则不需要unlock 使用ReentrantLock还可以调用lockInterruptibly方法,可以对线程interrupt方法做出响应,在一个线程等待锁的过程中,可以被打断 new ReentrantLock(true) 表示公平锁,不带参数默认为false,非公平锁

8. CountDownLatch

countDownLatch这个类可以使一个线程等待其他线程各自执行完毕后再执行。是通过一个计数器来实现的,计数器的初始值是线程的数量。当调用countDown()方法后,每当一个线程执行完毕后,计数器的值就-1,当计数器的值为0时,表示所有线程都执行完毕,然后在闭锁上等待的线程就可以恢复工作了。

线程中调用countDown()方法开始计数;在调用await()方法的线程中,当计数器为0后续才会继续执行,否则一直等待;也可以使用latch.await(timeout, unit)在等待timeout时间后如果计数器不为0,线程仍将继续。countDown()之后的代码不受计数器控制 与join区别,使用join的线程将被阻塞,使用countDown的线程不受影响,只有调用await的时候才会阻塞

8. CyclicBarrier

作用就是会让指定数量的(数量由构造函数指定)所有线程都等待完成后才会继续下一步行动。构造函数:public CyclicBarrier(int parties)

public CyclicBarrier(int parties)  public CyclicBarrier(int parties, Runnable barrierAction)

parties 是线程的个数;barrierAction为最后一个到达线程要做的任务

所有线程会等待全部线程到达栅栏之后才会继续执行,并且最后到达的线程会完成 Runnable 的任务。

实现原理:在CyclicBarrier的内部定义了一个Lock对象,每当一个线程调用await方法时,将拦截的线程数减1,然后判断剩余拦截数是否为初始值parties,如果不是,进入Lock对象的条件队列等待。如果是,执行barrierAction对象的Runnable方法,然后将锁的条件队列中的所有线程放入锁等待队列中,这些线程会依次的获取锁、释放锁。

9. Phaser

可重复使用的同步屏障,功能类似于CyclicBarrier和CountDownLatch,但支持更灵活的使用。

Phaser使我们能够建立在逻辑线程需要才去执行下一步的障碍等。

我们可以协调多个执行阶段,为每个程序阶段重用Phaser实例。每个阶段可以有不同数量的线程等待前进到另一个阶段。我们稍后会看一个使用阶段的示例。

要参与协调,线程需要使用Phaser实例 register() 本身。请注意:这只会增加注册方的数量,我们无法检查当前线程是否已注册 - 我们必须将实现子类化以支持此操作。

线程通过调用 arriAndAwaitAdvance() 来阻止它到达屏障,这是一种阻塞方法。当数量到达等于注册的数量时,程序的执行将继续,并且数量将增加。我们可以通过调用getPhase()方法获取当前数量。

10. ReadWriteLock

ReadWriteLock的具体实现是ReentrantReadWriteLock

ReadWriteLock允许分别创建读锁跟写锁

ReadWriteLock readWriteLock = new ReentrantReadWriteLock();  Lock readLock = readWriteLock.readLock();  Lock writeLock = readWriteLock.writeLock();

使用ReadWriteLock时,适用条件是同一个数据,有大量线程读取,但仅有少数线程修改。ReadWriteLock可以保证:

只允许一个线程写入(其他线程既不能写入也不能读取);

没有写入时,多个线程允许同时读(提高性能)

读写分离锁可以有效地帮助减少锁竞争,以提高系统性能,读写锁读读之间不互斥,读写,写写都是互斥的

11. Semaphore

Semaphore 是一个计数信号量,必须由获取它的线程释放。常用于限制可以访问某些资源的线程数量,例如通过 Semaphore 限流。

对于Semaphore来说,它要保证的是资源的互斥而不是资源的同步,在同一时刻是无法保证同步的,但是却可以保证资源的互斥。只是限制了访问某些资源的线程数,其实并没有实现同步。

常用方法:

1、acquire(int permits)

从此信号量获取给定数目的许可,在提供这些许可前一直将线程阻塞,或者线程已被中断。就好比是一个学生占两个窗口。这同时也对应了相应的release方法。

2、release(int permits)

释放给定数目的许可,将其返回到信号量。这个是对应于上面的方法,一个学生占几个窗口完事之后还要释放多少

3、availablePermits()

返回此信号量中当前可用的许可数。也就是返回当前还有多少个窗口可用。

4、reducePermits(int reduction)

根据指定的缩减量减小可用许可的数目。

5、hasQueuedThreads()

查询是否有线程正在等待获取资源。

6、getQueueLength()

返回正在等待获取的线程的估计数目。该值仅是估计的数字。

7、tryAcquire(int permits, long timeout, TimeUnit unit)

如果在给定的等待时间内此信号量有可用的所有许可,并且当前线程未被中断,则从此信号量获取给定数目的许可。

8、acquireUninterruptibly(int permits)

从此信号量获取给定数目的许可,在提供这些许可前一直将线程阻塞。

12. Exchanger

用于两个工作线程之间交换数据的封装工具类,简单说就是一个线程在完成一定的事务后想与另一个线程交换数据,则第一个先拿出数据的线程会一直等待第二个线程,直到第二个线程拿着数据到来时才能彼此交换对应数据。其定义为 Exchanger泛型类型,其中 V 表示可交换的数据类型,对外提供的接口很简单,具体如下:

Exchanger():无参构造方法。

V exchange(V v):等待另一个线程到达此交换点(除非当前线程被中断),然后将给定的对象传送给该线程,并接收该线程的对象。

V exchange(V v, long timeout, TimeUnit unit):等待另一个线程到达此交换点(除非当前线程被中断或超出了指定的等待时间),然后将给定的对象传送给该线程,并接收该线程的对象。

13. LockSupport

LockSupport 是一个非常方便实用的线程阻塞工具,他可以在任意位置让线程阻塞。

LockSupport 的静态方法 park()可以阻塞当前线程,类似的还有 parkNanos(),parkUntil()等,他们实现了一个限时的等待。

方法描述
void park():阻塞当前线程,如果调用unpark方法或者当前线程被中断,从能从park()方法中返回
void park(Object blocker)功能同方法1,入参增加一个Object对象,用来记录导致线程阻塞的阻塞对象,方便进行问题排查;
void parkNanos(long nanos)阻塞当前线程,最长不超过nanos纳秒,增加了超时返回的特性;
void parkNanos(Object blocker, long nanos)功能同方法3,入参增加一个Object对象,用来记录导致线程阻塞的阻塞对象,方便进行问题排查;
void parkUntil(long deadline)阻塞当前线程,直到deadline;
void parkUntil(Object blocker, long deadline)功能同方法5,入参增加一个Object对象,用来记录导致线程阻塞的阻塞对象,方便进行问题排查;

同样的,有阻塞的方法,当然有唤醒的方法,什么呢?unpark(Thread) 方法。该方法可以将指定线程唤醒。

需要注意的是:park 方法和 unpark 方法执行顺序不是那么的严格。比如我们在 Thread 类中提到的 suspend 方法 和resume 方法,如果顺序错误,将导致永远无法唤醒,但 park 方法和 unpark 方法则不会,因为 LockSupport 使用了类似信号量的机制。他为每一个线程准备了一个许可(默认不可用),如果许可能用,那么 park 函数会立即返回,并且消费这个许可(也就是将许可变为不可用),如果许可不可用,将会阻塞。而 unpark 方法则使得一个许可变为可用

14. AQS

AQS 为 AbstractQueuedSynchronizer 的简称

AQS是JDK下提供的一套用于实现基于FIFO等待队列的阻塞锁和相关的同步器的一个同步框架。这个抽象类被设计为作为一些可用原子int值来表示状态的同步器的基类。AQS管理一个关于状态信息的单一整数,该整数可以表现任何状态。比如 Semaphore 用它来表现剩余的许可数, ReentrantLock 用它来表现拥有它的线程已经请求了多少次锁;FutureTask 用它来表现任务的状态(尚未开始、运行、完成和取消)

使用须知:

Usage

To use this class as the basis of a synchronizer, redefine the following methods, as applicable, by inspecting and/or modifying the synchronization state using {@link #getState}, {@link #setState} and/or {@link #compareAndSetState}: {@link #tryAcquire} {@link #tryRelease} {@link #tryAcquireShared} {@link #tryReleaseShared}> {@link #isHeldExclusively}

以上方法不需要全部实现,根据获取的锁的种类可以选择实现不同的方法: 支持独占(排他)获取锁的同步器应该实现tryAcquire、 tryRelease、isHeldExclusively; 支持共享获取锁的同步器应该实现tryAcquireShared、tryReleaseShared、isHeldExclusively。

  • AQS浅析

AQS的实现主要在于维护一个"volatile int state"(代表共享资源)和 一个FIFO线程等待队列(多线程争用资源被阻塞时会进入此队列)。队列中的每个节点是对线程的一个封装,包含线程基本信息,状态,等待的资源类型等。

state的访问方式有三种:

getState() setState() compareAndSetState()

AQS定义两种资源共享方式

Exclusive(独占,只有一个线程能执行,如ReentrantLock) Share(共享,多个线程可同时执行,如Semaphore/CountDownLatch) 不同的自定义同步器争用共享资源的方式也不同。自定义同步器在实现时只需要实现共享资源state的获取与释放方式即可, 至于具体线程等待队列的维护(如获取资源失败入队/唤醒出队等),AQS已经在顶层实现好了。自定义同步器实现时主要实现以下几种方法:

isHeldExclusively():该线程是否正在独占资源。只有用到condition才需要去实现它。

tryAcquire(int):独占方式。尝试获取资源,成功则返回true,失败则返回false。

tryRelease(int):独占方式。尝试释放资源,成功则返回true,失败则返回false。tryAcquireShared(int):共享方式。尝试获取资源。负数表示失败;0表示成功,但没有剩余可用资源;正数表示成功,且有剩余资源。

tryReleaseShared(int):共享方式。尝试释放资源,如果释放后允许唤醒后续等待结点返回true,否则返回false。

以ReentrantLock为例

state初始化为0,表示未锁定状态。

A线程lock()时,会调用tryAcquire()独占该锁并将state+1。

此后,其他线程再tryAcquire()时就会失败,直到A线程unlock()到state=0(即释放锁)为止,其它线程才有机会获取该锁。

当然,释放锁之前,A线程自己是可以重复获取此锁的(state会累加),这就是可重入的概念。

但要注意,获取多少次就要释放多么次,这样才能保证state是能回到零态的。

以CountDownLatch为例

任务分为N个子线程去执行,state也初始化为N(注意N要与线程个数一致)。

这N个子线程是并行执行的,每个子线程执行完后countDown()一次,state会CAS减1。

等到所有子线程都执行完后(即state=0),会unpark()主调用线程,然后主调用线程就会从await()函数返回,继续后余动作。

一般来说,自定义同步器要么是独占方法,要么是共享方式,

他们也只需实现tryAcquire-tryRelease、tryAcquireShared-tryReleaseShared中的一种即可。

但AQS也支持自定义同步器同时实现独占和共享两种方式,如"ReentrantReadWriteLock"。

15. 锁基本概念

公平锁/非公平锁

可重入锁

独享锁/共享锁

互斥锁/读写锁

乐观锁/悲观锁

分段锁

偏向锁/轻量级锁/重量级锁

自旋锁

  • 公平锁/非公平锁

公平锁是指多个线程按照申请锁的顺序来获取锁。

非公平锁是指多个线程获取锁的顺序并不是按照申请锁的顺序, 有可能后申请的线程比先申请的线程优先获取锁; 有可能会造成优先级反转或者饥饿现象。

对于Java ReentrantLock而言,通过构造函数指定该锁是否是公平锁,默认是非公平锁。

非公平锁的优点在于吞吐量比公平锁大。

对于Synchronized而言,也是一种非公平锁。由于其并不像ReentrantLock是通过AQS的来实现线程调度, 所以并没有任何办法使其变成公平锁。

  • 可重入锁

可重入锁又名递归锁,是指在同一个线程在外层方法获取锁的时候,在进入内层方法会自动获取锁。

ReentrantLock, Synchronized都是可重入锁。

可重入锁的一个好处是可一定程度避免死锁

  • 独享(排他)锁/共享锁

独享锁是指该锁一次只能被一个线程所持有。

共享锁是指该锁可被多个线程所持有。

对于ReentrantLock而言,其是独享锁。

但是对于Lock的另一个实现类ReadWriteLock,其读锁是共享锁,其写锁是独享锁。读锁的共享锁可保证并发读是非常高效的,读写,写读 ,写写的过程是互斥的。独享锁与共享锁也是通过AQS来实现的,通过实现不同的方法,来实现独享或者共享。

对于Synchronized而言,当然是独享锁。

  • 互斥锁/读写锁

上面讲的独享锁/共享锁就是一种广义的说法,互斥锁/读写锁就是具体的实现。

互斥锁在Java中的具体实现就是ReentrantLock

读写锁在Java中的具体实现就是ReadWriteLock

  • 乐观锁/悲观锁

乐观锁与悲观锁不是指具体的什么类型的锁,而是指看待并发同步的角度。

悲观锁(Synchronized 和 ReentrantLock)

认为对于同一个数据的并发操作,一定是会发生修改的,哪怕没有修改,也会认为修改。

因此对于同一个数据的并发操作,悲观锁采取加锁的形式。悲观的认为,不加锁的并发操作一定会出问题。

  • 乐观锁(java.util.concurrent.atomic包)

认为对于同一个数据的并发操作,是不会发生修改的。在更新数据的时候,会采用尝试更新,不断重新的方式更新数据。乐观的认为,不加锁的并发操作是没有事情的。

悲观锁适合写操作非常多的场景,乐观锁适合读操作非常多的场景,

不加锁会带来大量的性能提升。

悲观锁在Java中的使用,就是利用各种锁。

乐观锁在Java中的使用,是无锁编程,常常采用的是CAS算法。典型的例子就是原子类,通过CAS自旋实现原子操作的更新。

  • 分段锁

分段锁其实是一种锁的设计,并不是具体的一种锁,ConcurrentHashMap并发的实现就是通过分段锁的形式来实现高效的并发操作。

ConcurrentHashMap中的分段锁称为Segment, 它类似于HashMap(JDK7与JDK8中HashMap的实现)的结构, 即内部拥有一个Entry数组,数组中的每个元素又是一个链表;同时又是一个ReentrantLock(Segment继承了ReentrantLock)。当需要put元素的时候,并不是对整个hashmap进行加锁, 而是先通过hashcode来知道他要放在那一个分段中,然后对这个分段进行加锁, 所以当多线程put的时候,只要不是放在一个分段中,就实现了真正的并行的插入。但是,在统计size的时候,可就是获取hashmap全局信息的时候,就需要获取所有的分段锁才能统计。

分段锁的设计目的是细化锁的粒度,当操作不需要更新整个数组的时候, 就仅仅针对数组中的一项进行加锁操作。

  • 偏向锁/轻量级锁/重量级锁

这三种锁是指锁的状态,并且是针对Synchronized。在Java 5通过引入锁升级的机制来实现高效Synchronized。这三种锁的状态是通过对象监视器在对象头中的字段来表明的。

  • 偏向锁

是指一段同步代码一直被一个线程所访问,那么该线程会自动获取锁。降低获取锁的代价。

  • 轻量级锁

是指当锁是偏向锁的时候,被另一个线程所访问,偏向锁就会升级为轻量级锁, 其他线程会通过自旋的形式尝试获取锁,不会阻塞,提高性能。

  • 重量级锁

是指当锁为轻量级锁的时候,另一个线程虽然是自旋,但自旋不会一直持续下去, 当自旋一定次数的时候,还没有获取到锁,就会进入阻塞,该锁膨胀为重量级锁。重量级锁会让其他申请的线程进入阻塞,性能降低。

  • 自旋锁

在Java中,自旋锁是指尝试获取锁的线程不会立即阻塞,而是采用循环的方式去尝试获取锁, 这样的好处是减少线程上下文切换的消耗,缺点是循环会消耗CPU。典型的自旋锁实现的例子,可以参考自旋锁的实现

到此,关于"多线程与高并发的知识点总结"的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注网站,小编会继续努力为大家带来更多实用的文章!

线程 方法 阻塞 数据 时候 资源 状态 对象 同步 就是 方式 数量 保证 尝试 悲观 信号 队列 信号量 乐观 多个 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 互联网科技园 上海挚品互联网科技有限公司 喔噻互联网科技有限公司 哈尔滨大商圈互联网科技有限公司 互联网科技公司经营范围 科技股互联网 北京互联网科技 万易互联网科技 美国互联网科技 壕鑫互联网络科技 深圳期待互联网科技 国外互联网科技网站 团飞科技与伯堂互联网服务平台 宁夏众和广鑫互联网科技有限公司 洛阳昕诺飞互联网科技有限公司 济南小医互联网科技有限公司招聘 艺电能源互联网科技有限公司招聘 上海万顺科技互联网有限公司招聘 互联网的科技体现在哪里 互联网科技对化妆品行业 互联网最新的科技有限公司 善班互联网科技 成都互联网科技公司 互联网科技logo设计 黑科技闪耀互联网之光 链环科技传统企业怎么转型互联网 天津腾达互联网科技有限公司 互联网科技资金流入 云南互联网科技学费 互联网时代高科技布局
0