自我成长 - 多线程
type: channel
link: https://yanxizhu.com/index.php/category/Multithreading/
description: 多线程
items: ReentrantLock、StampedLock、ReentrantReadWriteLock、Condition
items: J.U.C之AQS
items: 线程安全-并发容器J.U.C
items: 线程安全-同步容器
items: 线程不安全的类与写法
items: 线程封闭
items: 不可变对象
items: 安全发布对象，详解各种单例模式
items: 可见性和有序性
items: Synchronized

ReentrantLock、StampedLock、ReentrantReadWriteLock、Condition
type: item
date: 2022-04-25T16:07:45+00:00
description: 锁分为Synchronized和锁Lock。ReentrantLock与锁ReentrantLock(可重入锁)与Synchronized(同步锁)的区别：可重入性：区别不大，都是通过计数器，当计数器下降为0时，锁释放。锁的实现：Synchronized依赖JVM，ReentrantLock基于JDK实现。(类似Synchronized是操作系统实现，ReentrantLock是敲代码实现),ReentrantLock可通过看源码明白实现，Synchronized依赖JVM就看不到实现原理。性能的区别：优化之前Synchronized比ReentrantLock性能差很多，优化后Synchronized引入偏向锁、轻量级锁也就是自旋锁后，2者性能差不多了。差不多时，官方推荐Synchronized。功能的区别：Synchronized的使用方便简洁，隐式获取锁释放锁，ReentrantLock需要手动获取锁，释放锁，如果忘记容易出现死锁，所以最好再finally中释放锁。锁的颗粒度和灵活度很明显ReentrantLock优于Synchronized。ReentrantLock独有的功能：ReentrantLock可指定公平锁、非公平锁，而Synchronized只能时非公平锁。公平锁：先等待的先获取锁，反之亦然。提供了一个Condition类，可以分组唤醒需要唤醒的线程，Synchronized要么随机唤醒，要么全部唤醒。提供能够中断等待锁的线程的机制，lock.lockInterruptibly()，lockInterruptibly是一种自旋锁，通过循环来调用CAS来加锁，性能比较好，就是因为不会进入内核阻塞状态。使用场景：必须实现上面3个功能时，就必须用了。代码示例一ReentrantLock实现累加，注意与synchronized的不用用法package com.yanxizhu.demo.concurrency.lock; import lombok.extern.slf4j.Slf4j; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Semaphore; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * @description: ReentrantLock锁实现计数，注意使用synchroized怎么实现的 * @author: 清风 * @date: 2022/4/25 23:09 * @version: 1.0 */ @Slf4j public class ReentrantLockDemo { //定义锁 private static Lock lock = new ReentrantLock(); //定义锁用户数 private static final int totalThread = 5000; //定义并发线程数 private static final int concurrentThread = 200; //定义总和 private static int count = 0; public static void main(String[] args) throws Exception{ //定义线程池 ExecutorService executorService = Executors.newCachedThreadPool(); //定义信号量 final Semaphore semaphore = new Semaphore(concurrentThread); //定义闭锁 final CountDownLatch countDownLatch = new CountDownLatch(totalThread); for (int i = 0; i < totalThread; i++) { executorService.execute(() -> { try { semaphore.acquire(); sum(); semaphore.release(); } catch (InterruptedException e) { log.error("错误信息:【{}】", e.getMessage()); } countDownLatch.countDown(); }); } …

J.U.C之AQS
type: item
date: 2022-04-25T14:47:12+00:00
description: AQSJ.U.C之AQS，底层由线程队列实现。AbstractQueuedSynchronizer简称AQS，JUC的核心。使用Node实现FIFO队列，可以用于构建锁或者其他同步装置的基础框架。利用了一个int类型表示状态。使用方法是继承。子类通过继承并通过实现它的方法管理其状态{acquire和release}的方法操纵状态。可以同时实现排它锁和共享锁模式（独占、共享）。AQS主要同步组件CountDownLatchSemaphoreCyclicBarrierReentrantLockConditionFutureTask依次介绍1、CountDownLatch-闭锁可以完成类似阻塞的功能。一个线程或多个线程等待，直到其它线程完成。使用场景，程序执行需要某个条件执行完后，才执行。示例一package com.yanxizhu.demo.concurrency.aqs; import com.yanxizhu.demo.concurrency.annotation.ThreadSafety; import lombok.extern.slf4j.Slf4j; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; /** * @description: CountDownLatch,实现线程阻塞，等待执行完再执行程序打印 * @author: 清风 * @date: 2022/4/25 20:24 * @version: 1.0 */ @Slf4j @ThreadSafety public class CountDownLatchDemo { private static final int threadNum = 500; public static void main(String[] args) throws InterruptedException { ExecutorService executorService = Executors.newCachedThreadPool(); CountDownLatch countDownLatch = new CountDownLatch(threadNum); for (int i = 0; i <threadNum; i++) { final int num = i; executorService.execute(() -> { try{ pringNum(num); }catch (Exception e){ log.error("执行错误【{}】", e.getMessage()); }finally { countDownLatch.countDown(); } }); } countDownLatch.await(); executorService.shutdown(); log.info("finash .."); } public static void pringNum(int num) throws InterruptedException { Thread.sleep(100); log.info("num=【{}】", num); Thread.sleep(100); } }输出结果：。。。。。 20:36:34.598 [pool-1-thread-249] INFO com.yanxizhu.demo.concurrency.aqs.CountDownLatchDemo - num=【248】 20:36:34.608 [pool-1-thread-259] INFO com.yanxizhu.demo.concurrency.aqs.CountDownLatchDemo - num=【258】 20:36:34.597 [pool-1-thread-237] INFO com.yanxizhu.demo.concurrency.aqs.CountDownLatchDemo - num=【236】 20:36:34.597 [pool-1-thread-206] INFO com.yanxizhu.demo.concurrency.aqs.CountDownLatchDemo - num=【205】 20:36:34.608 [pool-1-thread-256] INFO com.yanxizhu.demo.concurrency.aqs.CountDownLatchDemo - num=【255】 20:36:34.608 [pool-1-…

线程安全-并发容器J.U.C
type: item
date: 2022-04-24T08:12:31+00:00
description: 线程安全-并发容器J.U.CArrayList -> CopyOnWriteArrayListHashSet、TreeSet -> CopyOnWriteArraySet 、ConcurrentSkipListSetHashMap、TreeMap -> ConcurrentHashMap 、ConcurrentSkipListMapCopyOnWriteArrayList适合读多写少的场景。读的时候再原数组读不需要加锁，写的时候会copy一份会单独加锁。代码示例：package com.yanxizhu.demo.concurrency.concurrent; import com.yanxizhu.demo.concurrency.annotation.ThreadSafety; import com.yanxizhu.demo.concurrency.annotation.UnThreadSafety; import lombok.extern.slf4j.Slf4j; import java.util.ArrayList; import java.util.List; import java.util.concurrent.*; /** * @description: 线程安全容器：CopyOnWriteArrayList * @author: 清风 * @date: 2022/4/24 9:52 * @version: 1.0 */ @Slf4j @ThreadSafety public class DemoCopyOnWriteArrayList { // 请求总数 public static int clientTotal = 5000; // 同时并发执行的线程数 public static int threadTotal = 200; private static List<Integer> list = new CopyOnWriteArrayList<>(); public static void main(String[] args) throws InterruptedException { ExecutorService executorService = Executors.newCachedThreadPool(); final Semaphore semaphore = new Semaphore(threadTotal); final CountDownLatch countDownLatch = new CountDownLatch(clientTotal); for (int i = 0; i < clientTotal; i++) { final int count = i; executorService.execute(() -> { try { semaphore.acquire(); update(count); semaphore.release(); } catch (Exception e) { log.error("exception", e); } countDownLatch.countDown(); }); } countDownLatch.await(); executorService.shutdown(); log.info("size:{}", list.size()); } private static void update(int i) { list.add(i); } }输出结果：5000CopyOnWriteArraySet线程安全，代码示例package com.yanxizhu.demo.concurrency.concurrent; import com.yanxizhu.demo.concurrency.annotation.ThreadSafety; import lombok.extern.slf4j.Slf4j; import java.util.Set; import java.util.concurrent.*; /** * @description: 线程安全容器：CopyOnWriteArraySet * @author: 清风 * @date: 2022/4/24 9:52 * @version: 1.0 */ @Slf4j @ThreadSafety public class DemoCopyOnWriteArraySet { // 请求总数 public static int clientTotal = 5000; // 同时并发执行的线程数 public static int threadTotal = 200; pr…

线程安全-同步容器
type: item
date: 2022-04-24T04:21:00+00:00
description: 线程安全-同步容器主要包括ArrayList -> Vector，Stack HashMap->HashTable（key、value不能为null）Collections.synchronizedXXX（List、Set、Map）都是使用Synchronized进行修饰的，性能不是特别好。可以使用并发容器代替。注意：同步容器也可能是线程步安全的。同步容器线程步安全，代码示例：package com.yanxizhu.demo.concurrency.synContainer; import com.yanxizhu.demo.concurrency.annotation.UnThreadSafety; import java.util.Vector; /** * @description: 同步容器，也可能出现线程不安全情况 * @author: 清风 * @date: 2022/4/24 10:32 * @version: 1.0 */ @UnThreadSafety public class DemoVecotNo { private static final Vector<Integer> vector = new Vector<>(); public static void main(String[] args) { //一直循环 while(true) { //向vector容器放入值 for(int i=0; i <10; i++){ vector.add(i); } //线程1，向vector容器移除值 new Thread(()->{ for(int i=0; i <vector.size(); i++){ vector.remove(i); } }).start(); //线程2，向vector中获取值 new Thread(()->{ for(int i=0; i <vector.size(); i++){ vector.get(i); } }).start(); } } }输出结果：Exception in thread "Thread-478" Exception in thread "Thread-1724" Exception in thread "Thread-1918" Exception in thread "Thread-1769" java.lang.ArrayIndexOutOfBoundsException: Array index out of range: 10 at java.base/java.util.Vector.remove(Vector.java:875) at com.yanxizhu.demo.concurrency.synContainer.DemoVecotNo.lambda$main$0(DemoVecotNo.java:27) at java.base/java.lang.Thread.run(Thread.java:834) java.lang.ArrayIndexOutOfBoundsException: Array index out of range: 62 at java.base/java.util.Vector.remove(Vector.java:875) at com.yanxizhu.demo.concurrency.synContainer.DemoVecotNo.lambda$main$0(DemoVecotNo.java:27) at java.base/java.lang.Thread.run(Thread.java:834) java.lang.ArrayIndexOutOfBoundsException: Array index out of range: 2 at java.base/java.util.Vector.get(Vector.java:781) at com.yanxizhu.demo.concurrency.synContainer.DemoVecotNo.lambda$main$1(DemoVecotNo.java:34) at java.base/java.lang.Thread.run(Thread.java:834) java.lang.ArrayIndexOutOfBoundsException: Array index out of range: 10 at java.base/java.util.Vector.remove(Vector.java:875) at com.yanxizhu.demo.concurrency.synContainer.DemoVecotNo.lambda$main…

线程不安全的类与写法
type: item
date: 2022-04-24T02:11:00+00:00
description: 线程不安全的类与写法线程不安全的类：类的对象可以同时被多个线程访问，比如抛出异常，逻辑错误。下面介绍一些常见的类。StringBuilderStringBuilder线程不安全的类代码示例package com.yanxizhu.demo.concurrency.threadUnSafetyClass; import com.yanxizhu.demo.concurrency.annotation.UnThreadSafety; import lombok.extern.slf4j.Slf4j; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Semaphore; /** * @description: 线程不安全的类：StringBuilder * @author: 清风 * @date: 2022/4/24 9:01 * @version: 1.0 */ @Slf4j @UnThreadSafety public class DemoStringBuilder { //用户数量 private static final int clientsTotal = 5000; //并发数量 private static final int concurrencyTotal = 200; //累加总和 private static StringBuilder stringBuilder = new StringBuilder(); public static void main(String[] args) throws InterruptedException { ExecutorService executorService = Executors.newCachedThreadPool(); //信号量 final Semaphore semaphore = new Semaphore(concurrencyTotal); //闭锁 final CountDownLatch countDownLatch = new CountDownLatch(clientsTotal); for (int i = 0; i < clientsTotal; i++) { executorService.execute(()->{ try { semaphore.acquire(); update(); semaphore.release(); } catch (InterruptedException e) { log.error("出现错误:【{}】", e.getMessage()); } countDownLatch.countDown(); }); } countDownLatch.await(); executorService.shutdown(); log.info("字符长度Length=【{}】",stringBuilder.length()); } /** * 累加 */ private static void update(){ stringBuilder.append(1); } }每次输出字符长度不一样，也不等于5000StringBuffer线程安全的类代码示例package com.yanxizhu.demo.concurrency.threadUnSafetyClass; import com.yanxizhu.demo.concurrency.annotation.ThreadSafety; import lombok.extern.slf4j.Slf4j; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Semaphore; /** * @description: 线程安全的类：StringBuffer * @author: 清风 * @date: 2022/4/24 9:06 * @version: 1.0 */ @Slf4j @ThreadSafety public class DemoStringBuffer { //用户数量 private static final int cli…

线程封闭
type: item
date: 2022-04-22T16:09:00+00:00
description: 线程封闭当访问共享数据时，通常是要使用同步。如果要避免使用同步，就是不提供共享数据。如果仅在单线程中访问数据，就不需要同步，这种技术就叫做线程封闭，它是实现线程安全最简单的方式之一。当某个对象封闭在一个线程当中时将自动实现线程安全性，即使被封闭的对象本身它并不是安全的，实现线程主要有三种方式。实现线程封闭的三种方式：1、Ad-hoc线程封闭：程序控制实现，最糟糕，忽略2、堆栈封闭：局部变量，无并发问题：栈封闭简单理解就是通过局部变量来实现线程封闭，多个线程访问对象的同一个方法，方法内部的局部变量会拷贝到每个线程的线程栈当中，只有当前线程才能访问到，互不干扰。所以局部变量是不被多个线程所共享的。尽量少用全局变量(不是全局常量)。3、ThreadLocal线程封闭：特别好的封闭方法。维护线程封闭一种更规范的方法就是使用ThreadLocal。ThreadLocal底层是一个map，可以就是线程名字，value就是我们封装的对象。ThreadLocal提供get、set方法为每个使用该变量的线程都存有一份独立的副本，因此get总是返回的是当前线程在调用set时设置的值。ThreadLocal一般用于防止对可变的单实例变量或者全局变量进行共享。ThreadLocal使用：代码示例1、自定义ThreadLoca工具package com.yanxizhu.demo.concurrency.DemoThrodLocal.ThreadLock; /** * @description: 使用ThrodLock的线程封闭，实现在一个线程种数据共享 * @author: 清风 * @date: 2022/4/22 22:56 * @version: 1.0 */ public class MyThroldLock { private final static ThreadLocal<Long> requestholder = new ThreadLocal<>(); //用ThreadLock提供的set方法，向ThreadLock中放入共享数据id //一般在实际接口处理前调用 public static void add(Long id) { requestholder.set(id); } //用ThreadLock提供的get方法获取ThreadLock中共享的数据 public static Long get() { return requestholder.get(); } //用ThreadLock提供的remove方法移除共享数据，不然容易导致内存溢出 //一般在接口真正处理完后调用 public static void remove() { requestholder.remove(); } }2、自定义过滤器Filterpackage com.yanxizhu.demo.concurrency.DemoThrodLocal.filter; import com.yanxizhu.demo.concurrency.DemoThrodLocal.ThreadLock.MyThroldLock; import lombok.extern.slf4j.Slf4j; import javax.servlet.*; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; /** * @description: 自定一自己的filter，实现filter接口，注意是servlet中的 * 说明： * Servlet中的过滤器Filter是实现了javax.servlet.Filter接口的服务器端程序， * 主要的用途是设置字符集、控制权限、控制转向、做一些业务逻辑判断等。其工作原理是， * 只要你在web.xml文件配置好要拦截的客户端请求，它都会帮你拦截到请求， * 此时你就可以对请求或响应(Request、Response)统一设置编码，简化操作； * 同时还可进行逻辑判断，如用户是否已经登陆、有没有权限访问该页面等等工作。 * 它是随你的web应用启动而启动的，只初始化一次，以后就可以拦截相关请求，只有当你的web应用停止或重新部署的时候才销毁。 * @author: 清风 * @date: 2022/4/22 23:02 * @version: 1.0 */ @Slf4j public class HttpFilter implements Filter { //初始化的 public void init(FilterConfig filterCo…

不可变对象
type: item
date: 2022-04-22T14:03:00+00:00
description: 线程安全策略不可变对象不可变对象需要满足的条件：对象创建以后其状态就不能修改对象所有域都是final类型对象是正确创建的（在对象创建期间，this引用没有逸出）一、final关键字final关键字：可修饰类、方法、变量。修饰类：不能被继承修饰方法：锁定方法不被继承类修改；效率修饰变量：基本数据类型变量、引用类型变量代码示例package com.yanxizhu.demo.concurrency.finals; import com.google.common.collect.Maps; import com.yanxizhu.demo.concurrency.annotation.UnThreadSafety; import lombok.extern.slf4j.Slf4j; import java.util.Map; /** * @description: 不可变对象 final修饰 * @author: 清风 * @date: 2022/4/22 20:35 * @version: 1.0 */ @Slf4j @UnThreadSafety public class DemoFinal { //普通类型，final修饰后，修改直接提示错误 private final static Integer a =1; //普通类型，final修饰后，修改直接提示错误 private final static String b = "hello"; //final修饰后不能在执行新的对象 private final static Map<Integer, Integer> map = Maps.newHashMap(); static { map.put(1, 2); map.put(2, 3); map.put(4, 5); } public static void main(String[] args) { //指向新的对象，直接报错 // map = new Maps.newHashMap(); //值可以修改，但是线程不安全 map.put(1, 6); log.info("{}", map.get(1)); // 输出结果：- 6 } //fina修饰形参，传入后也不能修改 public void test(final int a) { //报错 // a =2; } }输出结果： - 6二、Collections.unmodifiableXXXXX代码示例package com.yanxizhu.demo.concurrency.finals; import com.google.common.collect.Maps; import com.yanxizhu.demo.concurrency.annotation.UnThreadSafety; import lombok.extern.slf4j.Slf4j; import java.util.Collections; import java.util.Map; /** * @description: Collections.unmodifiableXXXXX,值是不能修改的，但是是线程安全的 * @author: 清风 * @date: 2022/4/22 21:12 * @version: 1.0 */ @Slf4j @UnThreadSafety public class DemoCollections { private static Map<Integer, Integer> map = Maps.newHashMap(); static { map.put(1, 2); map.put(3, 4); map.put(5, 6); map = Collections.unmodifiableMap(map); } public static void main(String[] args) { map.put(1, 8); log.info("{}", map.get(1)); } }输出结果：Exception in thread "main" java.lang.UnsupportedOperationException at java.base/java.util.Collections$UnmodifiableMap.put(Collections.java:1457) at com.yanxizhu.demo.concurrency.finals.DemoCollections.main(DemoCollections.java:28)Collections.unmodifiableXXXXX创建的不能修改，修改提示"UnsupportedOpe…

安全发布对象，详解各种单例模式
type: item
date: 2022-04-21T15:25:00+00:00
description: 安全发布对象什么是安全发布？发布对象：使一个对象能够被当前范围之外的代码所使用代码示例package com.yanxizhu.demo.concurrency.unSafePublish; import com.yanxizhu.demo.concurrency.annotation.UnThreadSafety; import lombok.extern.slf4j.Slf4j; import java.util.Arrays; /** * @description: 安全发布,这个例子是线程不安全的 * @author: 清风 * @date: 2022/4/21 21:45 * @version: 1.0 */ @Slf4j @UnThreadSafety public class UnSafePublish { private String[] states = {"a", "b", "c"}; public String[] getStates() { return states; } public static void main(String[] args) { UnSafePublish unSafePublish = new UnSafePublish(); log.info("{}", Arrays.toString(unSafePublish.getStates())); unSafePublish.getStates()[0] = "d"; log.info("{}", Arrays.toString(unSafePublish.getStates())); } }运行结果：21:48:00.633 [main] INFO com.yanxizhu.demo.concurrency.unSafePublish.UnSafePublish - [a, b, c] 21:48:00.636 [main] INFO com.yanxizhu.demo.concurrency.unSafePublish.UnSafePublish - [d, b, c]说明：上面代码，可以通过对象，调用getStates()方法获取数据，并可以外部直接修改里面的值。这样states的值是不安全的。对象溢出对象逸出：一种错误的发布。当一个对象还没有构造完成时，就使它被其他线程所见代码示例package com.yanxizhu.demo.concurrency.unSafePublish; import lombok.extern.slf4j.Slf4j; /** * @description: 对象溢出 * @author: 清风 * @date: 2022/4/21 21:53 * @version: 1.0 */ @Slf4j public class Escape { private int thisCanBeEascpe = 0; public Escape() { new InnerClass(); } private class InnerClass { public InnerClass() { log.info("{}", Escape.this.thisCanBeEascpe); } } public static void main(String[] args) { new Escape(); } }输出结果：21:55:01.416 [main] INFO com.yanxizhu.demo.concurrency.unSafePublish.Escape - 0说明：Escape对象还没构造完，就被new InnerClass()线程使用Escape.this.thisCanBeEascpe。安全发布对象4种方法在静态初始化函数中初始化一个对象引用将对象的引用保存到volatile类型域或者AtomicReference对象中将对象的引用保存到某个正确构造对象的final类型域中将对象的引用保存到一个由锁保护的域中单例-懒汉模式代码实例，线程不按钮package com.yanxizhu.demo.concurrency.singleton; import com.yanxizhu.demo.concurrency.annotation.UnThreadSafety; /** * @description: 单例-懒汉模式 * 在第一次使用时创建 * 单线程下没问题，多线程下可能出现问题 * @author: 清风 * @date: 2022/4/21 22:05 * @version: 1.0 */ @UnThreadSafety public class SingLetonLazyMode { /…

可见性和有序性
type: item
date: 2022-04-21T13:39:00+00:00
description: 可见性可见性：一个线程对主内存的修改可以及时的被其它线程观察到。导致共享变量在线程间不可见的原因：线程交叉执行重排序结合线程交叉执行共享变量更新后的值没有在工作内存与主存间及时更新可见性-synchronizedJMM关于synchronized的两条规定：线程解锁前，必须把共享变量的最新值刷新到主内存线程加锁时，将清空工作内存中共享变量的值，从而使用共享变量时需要从主内存中重新读取最新的值（注意，加锁与解锁是同一把锁）可见性-volatilevolatile，通过加入内存屏障和禁止重排序优化来实现。对volatile变量写操作时，会在写操作后加入一条store屏障指令，将本地内存中的共享变量值刷新到主内存对volatile变量读操作时，会在读操作前加入一条load屏障指令，从主内存中读取共享变量volatile写：volatile读：有序性Java内存模型中，允许编译器和处理器对指令进行重排序，但是重排序过程不会影响到单线程程序的执行，却会影响到多线程并发执行的正确性.可通过volatile、synchronized、Lock保证有序性。有序性-happens-before原则程序次序规则：一个线程内，按照代码顺序，书写在前面的操作先行发生于书写在后面的操作锁定规则：一个unLock操作先行发生于后面对同一个锁的lock操作volatile变量规则：对一个变量的写操作先行发生于后面对这个变量的读操作传递规则：如果操作A先行发生于操作B，而操作B又先行发生于操作C，则可以得出操作A先行发生于操作C线程启动规则：Thread对象的start（）方法先行发生于此线程的每一个动作线程中断规则：对线程interrupt（）方法的调用先行发生于被中断线程的代码检测到中断事件的发生线程终结规则：线程中所有的操作都先行发生于线程的终止检测，我们可以通过Thread.join（）方法结束、Thread.isAlive（）的返回值手段检测到线程已经终止执行对象终结规则：一个对象的初始化完成先行发生于他的finalize（）方法的开始线程安全性-总结原子性：Atomic包、CAS算法、synchronized、Lock可见性：synchronized、volatile有序性：happens-before

Synchronized
type: item
date: 2022-04-20T16:04:00+00:00
description: Atomic原子性提供了互斥访问。同一时刻只能由一个线程来操作。除了Atomic原子性，还有锁。jdk提供的锁主要分2类1、Synchronized：一种依赖JVM去实现锁的关键字，Synchronized，在这个对象作用的范围内，都是同一时刻，只能有一个线程去操作。注意是，作用对象的作用范围内。2、另一种jdk代码层面的锁，jdk里面提供了一个Lock的接口，主要是依赖特殊的CPU指令，实现类中比较有代表性的是ReentrantLock。Synchronized一种同步锁，修改的对象主要有4种：修饰代码块：大括号括起来的代码，也就是同步语句块，它的作用范围是大括号括起来的代码，作用对象是调用代码块的对象。修饰方法：被称为同步方法，作用范围为整个方法，作用对象是也是调用该方法的对象。修饰静态方法：作用范围整个静态方法，作用对象是这个类的所有对象。修饰类：修饰内容是后面Synchronized括号括起来的部分，作用对象也是这个类的所有对象。代码说明:一、启用不同进程，同一对象调用，调用同一方法。package com.yanxizhu.demo.concurrency.sync; import lombok.extern.slf4j.Slf4j; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; /** * @description: Synchronized修饰代码块、修改方法，作用对象为调用对象 * 这里测试，同一对象，启动不同进程，调同一方法 * @author: 清风 * @date: 2022/4/20 22:44 * @version: 1.0 */ @Slf4j public class DemoSynchronizedOne { /** * 修饰代码块，大括号被称为 同步代码块， * 作用范围：代码块内 * 作用对象：调用代码块的对象 */ public void test1() { synchronized (this) { for (int i = 0; i < 10 ;i ++){ log.info("test1 ...i={}",i); } } } /** * 修饰方法，该方法被称为同步方 * 作为范围：整个test2方法 * 作用对象：调用代码块的对象 */ public synchronized void test2() { for (int i = 0; i < 10 ;i ++){ log.info("test2 ...i={}",i); } } public static void main(String[] args) { DemoSynchronizedOne demoSynchronized = new DemoSynchronizedOne(); //线程池 ExecutorService executorService = Executors.newCachedThreadPool(); /** * 说明: 为什么执行2次 * 如果不用线程池，下面2次调用都是同一线程，不能验证。 * 使用线程池，相当于用了2各进程去执行test1，不用等一个进程执行完，在执行第二个线程， * 这样才能看到同一个对象的不同进程的执行情况。 */ //-----------------------------------测试同一对象的方法test1------------------------------------------- // 测试1： executorService.execute(() -> { demoSynchronized.test1(); }); executorService.execute(() -> { demoSynchronized.test1(); }); /* 输出结果： 23:33:52.670 [pool-1-thread-1] INFO com.yanxizhu.demo.concurrency.sync.DemoSynchronizedOne - test1 ...i=0 23:33:52.674 [pool-1-thread-1] INFO com.yanxizhu.demo.concurrency.sync.DemoSynchronizedOne - test1 ...i=1 23:33:52.674 [pool-1-thread-1] INFO com.yanxizhu.demo.concurrency.sync.DemoSynchronizedOne - test1 ...i=2 23:33:52.674 [pool-1-thread-…