运行发现结果随机,所以非线程安全
======================================================================
当多个线程访问某个类时,不管运行时环境采用何种调度方式
或者这些进程将如何交替执行,并且在主调代码中不需要任何额外的同步或协同
,这个类都能表现出正确的行为
,那么就称这个类是线程安全的
并非一气呵成,岂能无懈可击
4.2.1 Atomic 包
- AtomicXXX:CAS,Unsafe.compareAndSwapInt
提供了互斥访问,同一时刻只能有一个线程来对它进行操作
package com.mmall.concurrency.example.atomic;
import com.mmall.concurrency.annoations.ThreadSafe;
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.atomic.AtomicLong;
/**
- @author JavaEdge
*/
@Slf4j
@ThreadSafe
public class AtomicExample2 {
/**
- 请求总数
*/
public static int clientTotal = 5000;
/**
- 同时并发执行的线程数
*/
public static int threadTotal = 200;
/**
- 工作内存
*/
public static AtomicLong count = new AtomicLong(0);
public static void main(String[] args) throws Exception {
ExecutorService executorService = Executors.newCachedThreadPool();
final Semaphore semaphore = new Semaphore(threadTotal);
final CountDownLatch countDownLatch = new CountDownLatch(clientTotal);
for (int i = 0; i < clientTotal ; i++) {
executorService.execute(() -> {
try {
System.out.println();
semaphore.acquire();
add();
semaphore.release();
} catch (Exception e) {
log.error(“exception”, e);
}
countDownLatch.countDown();
});
}
countDownLatch.await();
executorService.shutdown();
//主内存
log.info(“count:{}”, count.get());
}
private static void add() {
count.incrementAndGet();
// count.getAndIncrement();
}
}
@Slf4j
@ThreadSafe
public class AtomicExample4 {
private static AtomicReference count = new AtomicReference<>(0);
public static void main(String[] args) {
// 2
count.compareAndSet(0, 2);
// no
count.compareAndSet(0, 1);
// no
count.compareAndSet(1, 3);
// 4
count.compareAndSet(2, 4);
// no
count.compareAndSet(3, 5);
log.info(“count:{}”, count.get());
}
}
- AtomicReference,AtomicReferenceFieldUpdater
- AtomicBoolean
- AtomicStampReference : CAS的 ABA 问题
4.2.2 锁
synchronized:依赖 JVM
-
修饰代码块:大括号括起来的代码,作用于调用的对象
-
修饰方法: 整个方法,作用于调用的对象
- 修饰静态方法:整个静态方法,作用于所有对象
package com.mmall.concurrency.example.count;
import com.mmall.concurrency.annoations.ThreadSafe;
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;
/**
- @author JavaEdge
*/
@Slf4j
@ThreadSafe
public class CountExample3 {
/**
- 请求总数
*/
public static int clientTotal = 5000;
/**
- 同时并发执行的线程数
*/
public static int threadTotal = 200;
public static int count = 0;
public static void main(String[] args) throws Exception {
ExecutorService executorService = Executors.newCachedThreadPool();
final Semaphore semaphore = new Semaphore(threadTotal);
final CountDownLatch countDownLatch = new CountDownLatch(clientTotal);
for (int i = 0; i < clientTotal ; i++) {
executorService.execute(() -> {
try {
semaphore.acquire();
add();
semaphore.release();
} catch (Exception e) {
log.error(“exception”, e);
}
countDownLatch.countDown();
});
}
countDownLatch.await();
executorService.shutdown();
log.info(“count:{}”, count);
}
private synchronized static void add() {
count++;
}
}
synchronized 修正计数类方法
- 修饰类:括号括起来的部分,作用于所有对象
子类继承父类的被 synchronized 修饰方法时,是没有 synchronized 修饰的!!!
Lock: 依赖特殊的 CPU 指令,代码实现
4.2.3 对比
-
synchronized: 不可中断锁,适合竞争不激烈,可读性好
-
Lock: 可中断锁,多样化同步,竞争激烈时能维持常态
-
Atomic: 竞争激烈时能维持常态,比Lock性能好; 只能同步一
个值
你做的改变,别人看不见。
一个线程对主内存的修改可以及时的被其他线程观察到
4.3.1 导致共享变量在线程间不可见的原因
-
线程交叉执行
-
重排序结合线程交叉执行
-
共享变量更新后的值没有在工作内存与主存间及时更新
4.3.2 可见性之synchronized
JMM关于synchronized的规定
-
线程解锁前,必须把共享变量的最新值刷新到主内存
-
线程加锁时,将清空工作内存中共享变量的值,从而使
用共享变量时需要从主内存中重新读取最新的值(加锁与解锁是同一把锁
)
4.3.3 可见性之volatile
通过加入内存屏障和禁止重排序优化来实现
- 对volatile变量写操作时,会在写操作后加入一条store
屏障指令,将本地内存中的共享变量值刷新到主内存
- 对volatile变量读操作时,会在读操作前加入一条load
屏障指令,从主内存中读取共享变量
- volatile使用
volatile boolean inited = false;
//线程1:
context = loadContext();
inited= true;
// 线程2:
while( !inited ){
sleep();
}
doSomethingWithConfig(context)
不按套路出牌。
一个线程观察其他线程中的指令执行顺序,由于指令重排序的存在,该观察结果一般杂乱无序
JMM允许编译器和处理器对指令进行重排序,但是重排序过程不会影响到单线程程序的执行,却会影响到多线程并发执行的正确性
4.4.1 happens-before 规则
====================================================================
- 发布对象
使一个对象能够被当前范围之外的代码所使用
- 对象逸出
一种错误的发布。当-个对象还没有构造完成时,就使它被其他线程所见
-
在静态初始化函数中初始化一个对象引用
-
将对象的引用保存到volatile类型域或者AtomicReference对象中
-
将对象的引用保存到某个正确构造对象的final类型域中
-
将对象的引用保存到一个由锁保护的域中
package com.mmall.concurrency.example.singleton;
import com.mmall.concurrency.annoations.NotThreadSafe;
/**
-
懒汉模式 -》 双重同步锁单例模式
-
单例实例在第一次使用时进行创建
-
@author JavaEdge
*/
@NotThreadSafe
public class SingletonExample4 {
/**
- 私有构造函数
*/
private SingletonExample4() {
}
// 1、memory = allocate() 分配对象的内存空间
// 2、ctorInstance() 初始化对象
// 3、instance = memory 设置instance指向刚分配的内存
// JVM和cpu优化,发生了指令重排
// 1、memory = allocate() 分配对象的内存空间
// 3、instance = memory 设置instance指向刚分配的内存
// 2、ctorInstance() 初始化对象
/**
- 单例对象
*/
private static SingletonExample4 instance = null;
/**
-
静态的工厂方法
-
@return
*/
public static SingletonExample4 getInstance() {
// 双重检测机制 // B
if (instance == null) {
// 同步锁
synchronized (SingletonExample4.class) {
if (instance == null) {
// A - 3
instance = new SingletonExample4();
}
}
}
return instance;
}
}
====================================================================
-
使用Node实现FIFO队列,可以用于构建锁或者其他同步装置的基础框架
-
利用了一个int类型表示状态
-
使用方法是继承
-
子类通过继承并通过实现它的方法管理其状态{acquire 和release} 的方法操纵状态
-
可以同时实现排它锁和共享锁模式(独占、共享)
同步组件
CountDownLatch
package com.mmall.concurrency.example.aqs;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
- @author JavaEdge
*/
@Slf4j
public class CountDownLatchExample1 {
private final static int threadCount = 200;
public static void main(String[] args) throws Exception {
ExecutorService exec = Executors.newCachedThreadPool();
final CountDownLatch countDownLatch = new CountDownLatch(threadCount);
for (int i = 0; i < threadCount; i++) {
final int threadNum = i;
exec.execute(() -> {
try {
test(threadNum);
} catch (Exception e) {
log.error(“exception”, e);
} finally {
countDownLatch.countDown();
}
});
}
countDownLatch.await();
log.info(“finish”);
exec.shutdown();
}
private static void test(int threadNum) throws Exception {
Thread.sleep(100);
log.info(“{}”, threadNum);
Thread.sleep(100);
}
}
package com.mmall.concurrency.example.aqs;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
/**
-
指定时间内处理任务
-
@author JavaEdge
*/
@Slf4j
public class CountDownLatchExample2 {
private final static int threadCount = 200;
public static void main(String[] args) throws Exception {
ExecutorService exec = Executors.newCachedThreadPool();
final CountDownLatch countDownLatch = new CountDownLatch(threadCount);
for (int i = 0; i < threadCount; i++) {
final int threadNum = i;
exec.execute(() -> {
try {
test(threadNum);
} catch (Exception e) {
log.error(“exception”, e);
} finally {
countDownLatch.countDown();
}
});
}
countDownLatch.await(10, TimeUnit.MILLISECONDS);
log.info(“finish”);
exec.shutdown();
}
private static void test(int threadNum) throws Exception {
Thread.sleep(100);
log.info(“{}”, threadNum);
}
}
##Semaphore用法
package com.mmall.concurrency.example.aqs;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
- @author JavaEdge
*/
@Slf4j
public class CyclicBarrierExample1 {
private static CyclicBarrier barrier = new CyclicBarrier(5);
public static void main(String[] args) throws Exception {
ExecutorService executor = Executors.newCachedThreadPool();
for (int i = 0; i < 10; i++) {
final int threadNum = i;
Thread.sleep(1000);
executor.execute(() -> {
try {
race(threadNum);
} catch (Exception e) {
log.error(“exception”, e);
}
});
}
executor.shutdown();
}
private static void race(int threadNum) throws Exception {
Thread.sleep(1000);
log.info(“{} is ready”, threadNum);
barrier.await();
log.info(“{} continue”, threadNum);
}
}
package com.mmall.concurrency.example.aqs;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
/**
- @author JavaEdge
*/
@Slf4j
public class CyclicBarrierExample2 {
private static CyclicBarrier barrier = new CyclicBarrier(5);
public static void main(String[] args) throws Exception {
ExecutorService executor = Executors.newCachedThreadPool();
for (int i = 0; i < 10; i++) {
final int threadNum = i;
Thread.sleep(1000);
executor.execute(() -> {
try {
race(threadNum);
} catch (Exception e) {
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)
![img](https://i-blog.csdnimg.cn/blog_migrate/f628e453d6d9d99c3670dc3b1a73f79b.jpeg)
面试资料整理汇总
这些面试题是我朋友进阿里前狂刷七遍以上的面试资料,由于面试文档很多,内容更多,没有办法一一为大家展示出来,所以只好为大家节选出来了一部分供大家参考。
面试的本质不是考试,而是告诉面试官你会做什么,所以,这些面试资料中提到的技术也是要学会的,不然稍微改动一下你就凉凉了
在这里祝大家能够拿到心仪的offer!
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!
{
try {
race(threadNum);
} catch (Exception e) {
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。[外链图片转存中…(img-TVWPwB8U-1712739529157)]
[外链图片转存中…(img-54eCoQhz-1712739529158)]
[外链图片转存中…(img-7SApANeW-1712739529158)]
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)
![img](https://i-blog.csdnimg.cn/blog_migrate/f628e453d6d9d99c3670dc3b1a73f79b.jpeg)
面试资料整理汇总
[外链图片转存中…(img-Us2xGUD8-1712739529158)]
[外链图片转存中…(img-LBJNWyrP-1712739529158)]
这些面试题是我朋友进阿里前狂刷七遍以上的面试资料,由于面试文档很多,内容更多,没有办法一一为大家展示出来,所以只好为大家节选出来了一部分供大家参考。
面试的本质不是考试,而是告诉面试官你会做什么,所以,这些面试资料中提到的技术也是要学会的,不然稍微改动一下你就凉凉了
在这里祝大家能够拿到心仪的offer!
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!