并发编程之美
文章平均质量分 68
Java并发编程的基础
学而不思则忘
一切都可以
展开
-
AQS源码理解——ReentrantLock
公平锁策略ReentrantLock类中 FairSync 类源码解读:static final class FairSync extends Sync { private static final long serialVersionUID = -3000897897090466540L; // 公平锁入口 // 不响应中断的加锁 final void lock() { acquire(1); // (1) } /**原创 2021-12-12 17:50:10 · 281 阅读 · 0 评论 -
Java并发List
一. 并发场景下的List1. CopyOnWriteArrayListCopyOnWriteArrayList 是线程安全的 ArrayList。CopyOnWrite 意思为写的时候会将共享变量新复制一份出来。复制的好处在于读操作是无锁的(也就是无阻塞)。CopyOnWriteArrayList 仅适用于写操作非常少的场景,而且能够容忍读写的短暂不一致。如果读写比例均衡或者有大量写操作的话,使用 CopyOnWriteArrayList 的性能会非常糟糕。CopyOnWriteArrayList原创 2021-10-22 15:53:05 · 2172 阅读 · 0 评论 -
ArrayList线程不安全的原因
以下面代码为测试用例:public class ArrayListTest { private static final Logger LOGGER = LoggerFactory.getLogger(ArrayListTest.class); private static final ExecutorService threadPoolService = new ThreadPoolExecutor(100, 200, 5, TimeUnit.SECONDS,原创 2021-10-22 15:37:45 · 245 阅读 · 0 评论 -
单例模式的几种写法
1. 饿汉式(线程安全)容易产生垃圾对象。public class Singleton1 { private static final Singleton1 instance = new Singleton1(); private Singleton1() {} public static Singleton1 getInstance() { return instance; }}**2. 懒汉式(线程不安全) **public原创 2021-08-15 22:46:18 · 71 阅读 · 0 评论 -
基于AQS实现自定义同步器
下面是基于AQS实现的不可重入的独占锁:state为1表示锁已经被某一个线程持有,由于是不可重入锁,所以不需要记录持有锁的线程获取锁的次数。public class NonReentrantLock implements Lock, java.io.Serializable { private static class Sync extends AbstractQueuedSynchronizer { // 锁是否被持有 protected boolean is原创 2021-08-15 17:16:47 · 213 阅读 · 0 评论 -
抽象同步队列AQS概述
1. 抽象同步队列AQS概述AbstractQueuedSynchronizer(简称 AQS)是队列同步器,顾名思义,其主要作用是处理同步。它是并发锁和很多同步工具类的实现基石(如 ReentrantLock、ReentrantReadWriteLock、CountDownLatch、Semaphore、FutureTask 等)。1. AQS的数据结构AQS 继承自 AbstractOwnableSynchronize,是一个FIFO的双向队列。public abstract class A原创 2021-08-15 15:41:55 · 186 阅读 · 0 评论 -
synchronized不捕获异常会导致锁释放
运行下面代码,如果不对异常处进行捕获,则结果如下:// 线程t1会中断,线程t2会进入代码继续执行t1 start.t1 count = 1t1 count = 2t1 count = 3t1 count = 4t1 count = 5Exception in thread "t1" java.lang.ArithmeticException: / by zero at thread.Catch.m(Catch.java:30) at thread.Catch.lambda$main$0原创 2021-08-12 19:48:30 · 449 阅读 · 0 评论 -
一个Thread对象能不能调用两次start
执行下面这段程序:package main;/** * @author Floweryu * @date 2021/6/17 10:21 */public class One { public static void main(String[] args) { PrintTime printTime = new PrintTime(); printTime.start(); printTime.start(); } pu原创 2021-06-17 11:35:46 · 1206 阅读 · 0 评论 -
ThreadPoolExecutor核心设计与实现
1. 总体设计Java中的线程池核心实现类是ThreadPoolExecutor,本章基于JDK 1.8的源码来分析Java线程池的核心设计与实现。我们首先来看一下ThreadPoolExecutor的UML类图,了解下ThreadPoolExecutor的继承关系。ThreadPoolExecutor实现的顶层接口是Executor,顶层接口Executor提供了一种思想:将任务提交和任务执行进行解耦。用户无需关注如何创建线程,如何调度线程来执行任务,用户只需提供Runnable对象,将任务的运行逻转载 2021-06-08 21:15:51 · 348 阅读 · 0 评论 -
两个线程交替打印大小写字母
使用java的两个线程交替打印大小写字母,一个线程打印大写字母,一个线程打印小写字母。1. 只使用synchronized/** * @author Floweryu * @date 2021/6/8 15:46 */public class PrintAa { private static final Object lock = new Object(); private static volatile boolean flag = true; public s.原创 2021-06-08 16:49:48 · 948 阅读 · 0 评论 -
多线程顺序打印ABC
1. Lock锁方法1.1 基本思路通过ReentrantLock我们可以很方便的进行显式的锁操作,即获取锁和释放锁,对于同一个对象锁而言,统一时刻只可能有一个线程拿到了这个锁,此时其他线程通过lock.lock()来获取对象锁时都会被阻塞,直到这个线程通过lock.unlock()操作释放这个锁后,其他线程才能拿到这个锁。import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.ReentrantLock;转载 2021-04-20 13:34:11 · 743 阅读 · 0 评论 -
Java创建线程的方式
Java有四种创建线程的方式,分别为实现Runnable接口的run方法,继承Thread类并重写run方法,使用FutureTask方式,利用线程池ExecutorService、Callable、future来实现。1. 前三种创建方式对比采用实现Runnable、Callable接口的方式创见多线程时,优势是:线程类只是实现了Runnable接口或Callable接口,还可以继承其他类。在这种方式下,多个线程可以共享同一个target对象,所以非常适合多个相同线程来处理同一份资源的情况,从而原创 2021-04-20 12:43:31 · 77 阅读 · 0 评论 -
Java线程同步的方法
为什么需要线程同步?java允许多线程并发控制,当多个线程同时操作一个可共享的资源变量时将会导致数据不准确,相互之间产生冲突,因此加入同步锁以避免在该线程没有完成操作之前,被其他线程的调用,从而保证了该变量的唯一性和准确性。1. 同步方法即有synchronized关键字修饰的方法。由于java的每个对象都有一个内置锁,当用此关键字修饰方法时,内置锁会保护整个方法。在调用该方法前,需要获得内置锁,否则就处于阻塞状态。 public synchronized void save(){}synch原创 2021-04-05 20:50:46 · 142 阅读 · 0 评论 -
Java多线程中start和run方法的区别
start()和run()方法的主要区别在于:当程序调用start()方法,一个新线程将会被创建,并且在run()方法中的代码会在新线程上运行。当直接调用run()方法时,程序并不会创建新线程,run()方法内部的代码在当前线程上运行。【示例】public class DifferentBetweenStartAndRun { public static void main (String[] args) { ThreadTask startThread = ne原创 2021-04-05 16:27:57 · 382 阅读 · 0 评论 -
Java实现单例模式
双重锁实现线程安全的单例模式public class SingleInstance { private volatile static SingleInstance instance = null; private SingleInstance() { } public static SingleInstance getInstance() { if (instance == null) { synchronized(SingleInst原创 2021-04-05 16:04:01 · 86 阅读 · 0 评论 -
volatile关键字详解
volatile关键字volatile 的作用保证可见性: 保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个共享变量,另外一个线程能读到这个修改的值。禁止指令重排:不管怎么重排序,(单线程)程序的执行结果不会改变。由于编译器或 CPU 的优化,代码的实际执行顺序可能与我们编写的顺序是不同的,这在单线程的情况下是没问题的,但是一旦引入多线程,这种乱序就可能会导致严重的线程安全问题。用了 volatile 关键字就可以在一定程度上禁止这种重排序。**不保证原子性:**决定了它原创 2021-03-12 21:28:08 · 350 阅读 · 0 评论 -
ConcurrentHashMap和CopyOnWriteArrayList
一、并发容器简介并发容器对应的普通容器描述ConcurrentHashMapHashMapJava 1.8 之前采用分段锁机制细化锁粒度,降低阻塞,从而提高并发性;Java 1.8 之后基于 CAS 实现。ConcurrentSkipListMapSortedMap基于跳表实现的CopyOnWriteArrayListArrayListCopyOnWriteArraySetSet基于 CopyOnWriteArrayList实现。Concurr原创 2021-03-12 21:10:00 · 690 阅读 · 0 评论 -
深入理解CAS
1. CAS简介CAS:Compare And Swap,即比较再交换。它是一条 CPU 并发原语。原语的执行必须是连续的,在执行过程中不允许中断,也就是说CAS是一条原子指令,不会造成所谓的数据不一致的问题。它的实现借助了一个叫做Unsafe的类。Unsafe类是 Java 中用于直接操作内存数据的一个类(类似于C语言中的指针操作),其中包含很多的本地方法(native)。2. CAS算法理解CAS包含三个参数v, E, N,其中V表示要更新的变量,E表示预期值,N表示新值。仅当V = E时,才原创 2021-03-12 20:22:14 · 1112 阅读 · 0 评论 -
常用并发工具类介绍
它们分别是:信号量 Semaphore倒计时门栓 CountDownLatch屏障 CyclicBarrier所以,既然是工具类,那么必然是离不开特定的场景的,于是相互之间没有谁优谁劣,只有谁更合适。1. 信号量 Semaphore1.1 信号量原理Semaphore又称"信号量",也是一个非常有用的工具类,它相当于是一个并发控制器,用来控制同时访问某个特定资源的操作数量,或者同时执行某个指定操作的数量。Semaphore 内部维护了一组虚拟的许可,许可的数量可以通过构造函数的参数指定。访转载 2021-03-10 21:11:10 · 289 阅读 · 0 评论 -
ThreadLocal详解
ThreadLocal 是一个存储线程本地副本的工具类。要保证线程安全,不一定非要进行同步。同步只是保证共享数据争用时的正确性,如果一个方法本来就不涉及共享数据,那么自然无须同步。Java 中的 无同步方案 有:可重入代码 - 也叫纯代码。如果一个方法,它的 返回结果是可以预测的,即只要输入了相同的数据,就能返回相同的结果,那它就满足可重入性,当然也是线程安全的。线程本地存储 - 使用 ThreadLocal 为共享变量在每个线程中都创建了一个本地副本,这个副本只能被当前线程访问,其他线程无法访问转载 2021-03-10 20:17:42 · 383 阅读 · 0 评论 -
Thread类中join()、sleep()和、yield()方法详解
join()方法简介wait()、notify()/notifyAll()是Object类中的方法,join()方法是Thread类直接提供的。作用:在当前线程A中调用另外一个线程B的join()方法后,会让当前线程A阻塞,直到线程B的逻辑执行完成,A线程才会解阻塞,然后继续执行自己的业务逻辑。join()原理分析join()有三个重载的方法,通过Object类中的wait()、notify()/notifyAll()方法实现的。public final synchronized void joi原创 2021-03-04 20:31:24 · 454 阅读 · 1 评论 -
操作系统死锁产生的原因和解决方法
一、引起死锁的原因竞争不可抢占性资源引起死锁竞争可消耗资源引起死锁进程推进顺序不当引起死锁二、产生死锁的四个必要条件互斥条件:进程所分配到的资源只能被一个进程使用。请求和保持条件:进程已经保持了一个资源,但又提出新的资源请求。不可抢占条件:进程已获得的资源在未使用之前不能被抢占。循环等待条件:在发生死锁时,必然存在一个进程-资源循环链。即P0等待P1, P1等待P2, ..... Pn 等待P0三、处理死锁的方法1. 预防死锁只要破坏死锁的四个条件之一就可以预防死锁,其中第一个原创 2020-12-04 11:33:53 · 4013 阅读 · 0 评论 -
Java线程池ThreadPoolExecutor简介
1. 线程池状态含义RUNNING: 运行状态。接受新任务并且处理阻塞队列里面的任务.SHUTDOWN: 关闭状态。拒绝新任务但是处理阻塞队列里的任务.STOP: 停止状态。拒绝新任务并且抛弃阻塞队列里的任务, 同时会中断正在处理的任务.TIDYING: 整理状态。所有任务都执行完(包含阻塞队列里面的任务)后当前线程池活动线程数为0, 将要调用terminated方法.TERMINATED: 终止状态. terminated方法调用完成以后的状态.2. 线程池状态转换列举如下:RUNNI原创 2021-03-02 21:37:02 · 160 阅读 · 0 评论 -
深入理解Java并发锁
1. 并发锁简介1.1 乐观锁与悲观锁乐观锁与悲观锁不是指具体的什么类型的锁,而是处理并发同步的策略。乐观锁 - 乐观锁对于并发采取乐观的态度,认为:不加锁的并发操作也没什么问题。对于同一个数据的并发操作,是不会发生修改的。在更新数据的时候,会采用不断尝试更新的方式更新数据。乐观锁适合读多写少的场景。悲观锁 - 悲观锁对于并发采取悲观的态度,认为:不加锁的并发操作一定会出问题。悲观锁适合写操作频繁的场景。悲观锁与乐观锁在 Java 中的典型实现:悲观锁在 Java 中的应用就是通过使用 s原创 2021-03-02 12:59:25 · 335 阅读 · 0 评论 -
wait()、notify/notifyAll()的理解和生产者消费者举例
wait()、notify/notifyAll()方法是Object的本地final方法,无法被重写。wait()使当前线程阻塞,前提是必须先获得锁,一般配合synchronized 关键字使用。即,一般在synchronized 同步代码块里使用 wait()、notify/notifyAll()方法。由于 wait()、notify/notifyAll()在synchronized 代码块执行,说明当前线程一定是获取了锁的。当线程执行wait()方法时候,会释放当前的锁,然后让出CPU,进入等待.转载 2020-11-27 11:01:33 · 211 阅读 · 0 评论 -
synchronized关键字的理解以及和Lock,ReentrantLock的区别
一、synchronized关键字的了解synchronized解决的是多个线程之间访问资源的同步性,synchronized可以保证被它修饰的方法或代码块在任意时刻只能有一个线程执行。二、synchronized关键字主要的使用方式修饰实例方法:作用于当前对象实例加锁,进入同步代码前要获得当前对象实例的锁修饰静态方法:给当前类加锁,会作用于类的所有对象实例,因为静态成员不属于任何一个实例对象(static表明这是该类的一个静态资源,不管new了多少个对象,只有一份)。所以,如果一个线程A调用一个原创 2020-11-14 11:21:58 · 500 阅读 · 0 评论 -
从JVM到volatile关键字
参考文章文章转载自此处一、JVM内存与JMM内存模型(1) JVM内存结构Java代码是运行在虚拟机上的,.java文件首先会被编译成.class文件,接着被JVM虚拟机加载,并且根据不同的操作系统平台翻译成对应平台的机器码执行。堆数据共享区域存储实例对象以及数组,通常是占用内存最大的一块也是数据共享的,比如 new Object() 就会生成一个实例;而数组也是保存在堆上面的,因为在 Java 中,数组也是对象。垃圾收集器的主要作用区域。一个对象创建的时候,到底是在堆上分配,还是在栈上分配呢转载 2020-11-10 10:56:44 · 311 阅读 · 0 评论