当前位置:网站首页>JUC学习
JUC学习
2022-07-01 03:04:00 【光头小小强007】
文章目录
️ 基础概念
并发(concurrent): 在同一台处理器上,处理多个任务。
并行(parallel): 在多台处理器上同时处理多个任务。
进程: 系统中运行的应用程序就是一个进程,每一个进程都有他自己的内存空间和系统资源。
线程: 又称为 轻量级进程,在同一个进程内会有一个或者多个的线程,是大多数操作系统进行时序调度的基本单元。
管程:Monitor(监视器) ,即我们平时所说的锁。他是一个同步机制,在同一时间内,只有一个线程可以访问被保护的数据和代码。
用户线程:user thread,系统的工作线程,它会完成这个程序要完成的业务操作。
守护线程:daemon thread,是一种特殊的线程,为其他线程进行服务的,在后台默认的完成一系列的系统任务。( gc 回收机制)
判断一个线程是否为守护线程的方法:
/** * Tests if this thread is a daemon thread. * * @return <code>true</code> if this thread is a daemon thread; * <code>false</code> otherwise. * @see #setDaemon(boolean) */
public final boolean isDaemon() {
return daemon;
}
用户线程代码演示和总结
默认情况下创建的线程,都是用户线程!
栗子:
// 用户线程
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"\t当前线程开始运行\t"+(Thread.currentThread().isDaemon()?"守护":"用户"));
while (true){
}
}
},"t1").start();
try {
TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) {
e.printStackTrace(); }
System.out.println(Thread.currentThread().getName()+"\t当前线程开始运行 ---- end 主线程");
}
// 守护线程
public static void main(String[] args) {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "\t当前线程开始运行\t" + (Thread.currentThread().isDaemon() ? "守护" : "用户"));
while (true) {
}
}
}, "t1");
thread.setDaemon(true);
thread.start();
try {
TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) {
e.printStackTrace(); }
System.out.println(Thread.currentThread().getName()+"\t当前线程开始运行 ---- end 主线程");
}
SUM:如果用户线程全部结束,那么程序需要完成的业务操作已经结束了。守护线程随着 JVM 一同结束工作。
️ CompletableFuture
️ Future 接口
Future 接口(FutureTask 实现类),定义了操作异步任务执行的方法,如获取异步任务的执行结果,取消任务的执行,判断任务是否被取消,判断任务是否执行完毕。也就是 Future 接口可以为主线程开一个分支任务,专门为主线程处理耗时和费力的复杂业务。
作用:Future 接口是 java5 中新增的接口,它提供了一种 异步并行计算 的功能。如果主线程需要执行一个很耗时的计算任务,我们可以通过 Future 把这个任务放到异步线程中执行。主线程会继续处理其他任务或先行结束,在通过 Future 获取计算结果。
️ FutureTask
异步多线程任务执行且返回结果有三个特点:
- 多线程
- 又返回
- 异步任务
Runnable 接口VSCallable 接口
- 重写方法名称不一样,
Callable——call,Runnable——run。 Runnable没有返回值没有抛出异常,Callable有返回值,并且会抛出异常。
使用示例 :
public class CompletableFutureDemo {
public static void main(String[] args) throws ExecutionException, InterruptedException {
FutureTask<String> task = new FutureTask<>(new MyThread2());
Thread thread = new Thread(task, "t1");
thread.start();
// 获取任务的返回结果
System.out.println(task.get());
}
}
class MyThread2 implements Callable<String> {
@Override
public String call() throws Exception {
return null;
}
}
优点:future + 线程池异步多线程任务配合,能显著的提高程序的执行效率。
示例:
public static void main(String[] args) throws ExecutionException, InterruptedException {
// 3 个任务,目前开启多个异步任务来处理,请问耗时多少
ExecutorService pool = Executors.newFixedThreadPool(3);
long start = System.currentTimeMillis();
FutureTask<String> task1 = new FutureTask<String>(()->{
try {
TimeUnit.MILLISECONDS.sleep(500); } catch (InterruptedException e) {
e.printStackTrace(); }
return "task1 over";
});
pool.submit(task1);
FutureTask<String> task2 = new FutureTask<String>(()->{
try {
TimeUnit.MILLISECONDS.sleep(300); } catch (InterruptedException e) {
e.printStackTrace(); }
return "task2 over";
});
pool.submit(task2);
task1.get();
task2.get();
try {
TimeUnit.MILLISECONDS.sleep(300); } catch (InterruptedException e) {
e.printStackTrace(); }
long end = System.currentTimeMillis();
pool.shutdown();
System.out.println("耗时时间为: " + (end-start));
System.out.println(Thread.currentThread().getName()+"-------- end");
}
public static void m1(){
// 3 个任务,目前只有一个线程 main 来处理,请问耗时多少
long start = System.currentTimeMillis();
try {
TimeUnit.MILLISECONDS.sleep(500); } catch (InterruptedException e) {
e.printStackTrace(); }
try {
TimeUnit.MILLISECONDS.sleep(300); } catch (InterruptedException e) {
e.printStackTrace(); }
try {
TimeUnit.MILLISECONDS.sleep(300); } catch (InterruptedException e) {
e.printStackTrace(); }
long end = System.currentTimeMillis();
System.out.println("耗时时间为: " + (end-start));
System.out.println(Thread.currentThread().getName()+"-------- end");
}
缺点:
get()方法,容易导致程序阻塞,一般建议放在程序的后面,get(long, TimeUnit)方法,超过等待多少秒之后就不再继续阻塞。
public static void main(String[] args) throws ExecutionException, InterruptedException, TimeoutException {
FutureTask<String> task = new FutureTask<String>(()->{
System.out.println(Thread.currentThread().getName()+"---- come in");
TimeUnit.SECONDS.sleep(5);
return "task over";
});
new Thread(task,"t1").start();
// System.out.println(task.get());'
System.out.println(task.get(6,TimeUnit.SECONDS));
System.out.println(Thread.currentThread().getName()+"----- 去做其他事情");
}
isDown()轮询,会耗费无畏的cpu资源,也不见得能及时的获得结果。如果想要异步的获取结果,通常会以轮询的方式获取结果,尽量不要阻塞。
public static void main(String[] args) throws ExecutionException, InterruptedException, TimeoutException {
FutureTask<String> task = new FutureTask<String>(() -> {
System.out.println(Thread.currentThread().getName() + "---- come in");
TimeUnit.SECONDS.sleep(5);
return "task over";
});
new Thread(task, "t1").start();
// System.out.println(task.get(6,TimeUnit.SECONDS));
System.out.println(Thread.currentThread().getName() + "----- 去做其他事情");
while (true) {
if (task.isDone()) {
System.out.println(task.get());
break;
}else {
System.out.println("四月打弟弟,四月欺负弟弟!!!");
}
}
}
SUM:Future 对于结果的获取很不友好,只能通过阻塞或者轮询的方式去获取结果。
️ CompletableFuture
提供了一种观察者模式相类似的机制,可以让任务执行完成之后去通知监听的一方。

CompletionStage 接口:CompletionStage 代表异步计算过程中的某一个阶段,一个阶段完成以后可以出发另一个阶段。一个阶段可以被单个阶段触发,也有可能被多个阶段触发。
创建 CompletableFuture 四种静态方法:
runAsync(Runnable) 静态方法,无返回值,使用默认的线程池 ForkJoinPool。
runAsync(Runnable ,Executor ) 静态方法,无返回值,传入线程池。
supplyAsync(Supplier<U>) 静态方法,又返回值,使用默认的线程池 ForkJoinPool。
public static void main(String[] args) throws ExecutionException, InterruptedException {
ExecutorService pool = Executors.newFixedThreadPool(3);
// CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
// CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
// CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
System.out.println(Thread.currentThread().getName());
// 暂停几分钟
try {
TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) {
e.printStackTrace(); }
return "hello world";
},pool);
// });
System.out.println(future.get());
}
代码演示:
public static void main(String[] args) {
ExecutorService pool = Executors.newFixedThreadPool(3);
try {
CompletableFuture.supplyAsync(() -> {
System.out.println(Thread.currentThread().getName() + "---- come in");
Integer result = ThreadLocalRandom.current().nextInt(10);
if (result>5){
Integer num =result/0;
}
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
return result;
}, pool).whenComplete((v, e) -> {
if (Objects.isNull(e)) {
System.out.println("----- 计算完成,更新系统 updateValue: " + v);
}
}).exceptionally((e) -> {
e.printStackTrace();
System.out.println("异常情况:" + e.getCause() + "\t" + e.getMessage());
return null;
});
System.out.println(Thread.currentThread().getName() + "线程先去忙其他的任务去了!");
}catch (Exception e){
e.printStackTrace();
}finally {
pool.shutdown();
}
}
- 异步任务结束之后,会自动回调某个对象的方法。
- 主线程设置好了回调,不在关心异步线程的执行,异步任务之间可以顺序执行。
- 异步任务出现错误的时候,会自动回调某个对象的方法。

示列:
public class CompletableFutureMallDemo {
static List<NetMall> list = Arrays.asList(
new NetMall("jd"),
new NetMall("dangdang"),
new NetMall("pdd")
);
/** * setup by setup * @param list * @param name * @return */
static List<String> getPrice(List<NetMall> list,String name){
return list.stream().map(netMall ->
String.format(name+" in %s price is %.2f",netMall.getName(),netMall.calcPrice(name))
).collect(Collectors.toList());
}
/** * List<NetMall> ==== List<CompletableFuture<String>> ==== List<String> * * @param list * @param name * @return */
static List<String> getPriceByCompletableFuture(List<NetMall> list,String name){
return list.stream().map(netMall -> CompletableFuture.supplyAsync(()->
String.format(name+" in %s price is %.2f",netMall.getName(),netMall.calcPrice(name))
)).collect(Collectors.toList()).stream().map(s->s.join())
.collect(Collectors.toList());
}
public static void main(String[] args) {
long start = System.currentTimeMillis();
List<String> mysql = getPrice(list, "mysql");
mysql.forEach(System.out::println);
long end = System.currentTimeMillis();
System.out.println("耗时:"+(end-start));
System.out.println("========================");
long start2 = System.currentTimeMillis();
List<String> mysql2 = getPriceByCompletableFuture(list, "mysql");
mysql2.forEach(System.out::println);
long end2 = System.currentTimeMillis();
System.out.println("耗时:"+(end2-start2));
}
}
class NetMall{
private String name;
public String getName() {
return name;
}
public NetMall(String name){
this.name=name;
}
public double calcPrice(String name){
try {
TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) {
e.printStackTrace(); }
return ThreadLocalRandom.current().nextDouble()*2+name.charAt(0);
}
}
常用方法:
- 获得结果和触发预算
获取结果:
get()get(long,TImeUtin)join()getNow(T),如果计算完成则返回计算完成之后的结果,如果没有计算完成则返回默认的结果。
触发预算:complete(T),是否打断get()方法立即返回括号里面的值。
- 对计算结果进行处理
thenApply,计算结果存在依赖,两个线程串行化,如果当前步骤有异常则不会继续向下云行。handle,该方法正常情况下和上面的方法执行方式相同,如果出现异常可以继续向下执行。
- 计算结果进行消费
thenRun,执行完A任务,执行B任务,且B任务不需要A任务的结果。thenAccept,B任务需要A任务执行完成的结果,但无返回值。thenApply,B任务需要A任务执行完成的结果,但有返回值。
thenRunVSthenRunAsync
- 没有传入自定义线程池,则使用默认线程池。
- 如果传入自定义的线程池,
thenRun方法执行第二个任务和第一个任务使用的线程池相同。thenRunAsync执行第二个任务时候,如果传入的第一个任务的线程池是自定义的,则第二个任务则使用默认的线程池ForkJoinPool。 - 系统处理太快,系统优化原则,则使用
main线程。
- 对速度计算进行选用
applyToEither 进行比较。
- 对计算结果进行合并
thenCombine 把两个结果进行处理,先完成的要等待,等待其他任务完成。
️ 线程锁相关知识
️ 悲观锁和乐观锁
悲观锁:synchronized 关键字和 lock 的实现都是悲观锁。认为自己在使用数据的时候,一定有别的线程来修改数据,因此会在获取数据的时候先加锁,确保数据不会被别的线程修改。
使用场景,先加锁可以保证写操作时,数据正确。
乐观锁:
认为自己在使用数据时,不会有别的线程来修改数据和资源,所以不会添加锁。在 java 中通过使用无锁编程来实现,只是在更新数据的时候去判断,之前有没有别的线程更新了这个数据。
判断规则:
- 版本号机制,
version - 最长采用的是
CAS算法,java中原子类的递增就是通过CAS来实现的。
适合读操作较多的场景,不加锁的特点能够使其读操作性能大幅度的提升。
synchronized 关键字解读:
一个对象里面如果有多个
synchronized的方法,在某一时刻内,只要线程去调用其中一个synchronized方法, 其他的线程都只能等待,换句话来说,在某一时刻,只能有唯一的一个线程去访问这些synchronized方法。 锁的是当前对象this,被锁定之后,其他线程都不能进入到当前对象的其他synchronized方法。加一个普通方法后发现和同步锁无关,换成两个对象后,不是同一把锁了,情况立刻发生了变化
都换成静态同步方法,三种
synchronized的锁内容有一些差别对于普通方法,锁的是当前实例对象,通常指this对于静态方法,锁的是当前类的class对象,对于同步方法块,锁的是synchronized括号内的对象。当一个线程试图访问同步代码块时,他首先必须得到锁,正常退出或者抛出异常时必须释放锁。
所有普通同步方法用的都是同一把锁 —— 实例对象本身,就是new出来的实例对象本身,本类this也就是说如果一个实列对象的普通同步方法获取锁后,该实例对象的其他普通同步方法必须等待获取锁的方法释放之后才能获取锁。 所有的静态同步方法用的也是同一把锁 —— 类对象本身, 具体实例对象this和唯一模板class, 这两把锁对应不同的对象,所以静态同步方法和普通的同步方法之间是不会产生竞争条件的, 但是一旦静态方法获取锁之后,其他的静态方法都必须等待该方法释放锁之后才能获取锁。
作用:
- 当前实例加锁,进入同步代码前要获得当前实例的锁。
- 对括号里配置的对象锁。
- 当前类加锁,进入同步代码前要获得当前类的锁。
️ 公平锁与非公平锁
非公平锁: 非公平锁更充分的利用了 cpu 的时间片段。尽量减少 cpu 的空闲时间。减少了线程的开销。
可重入锁: 也叫递归锁,一个线程的多个流程可以获取同一把锁,持有这把锁可以再次进入。自己可以获取自己的内部锁。指可重复可递归调用的锁,在外层使用锁之后,内层仍然可以使用,并且不发生死锁,这样的锁就叫做可重入锁。
简单来说,在一个 synchronized 修饰的方法或者代码块中内部调用本类的其他 synchronized 修饰的方法或者代码块时,永远可以得到锁。
隐式锁 —— synchronized 关键字
显示锁 —— Lock 也有 ReentrantLock 这样的可重入锁
死锁命令排查:
jsp -l查看进程号jstack 进程号排查问题jconsole图形化工具

️ 中断机制
一般来说一个线程不应该由其他线程来强制中断或者停止,而是应该由线程自己来进行停止。
️ LockSupport
是一个线程阻塞的工具类,所有方法都是静态方法。其中 park() 方法和 unpark() 方法,分别是阻塞线程和解除线程的。每个使用他的线程都有一个许可证,最多只有一个且不会累计。
️ jMM 模型
java 规范中尝试定义一种内存模型(Java Memory Model),来屏蔽各种硬件与操作系统之间内存访问差异,已实现 java 在各个平台下都能达到一致的访问效果。
JMM 模型,本身是一种抽象的概念,并不真实存在,他仅仅描述的是一组约定或规范。通过这组规范,定义了程序中(尤其是多线程),各个变量的读写和访问方式。并且决定了一个线程对共享变量的写入何时以及如何编程对另一个线程的可见,关键的技术点都是围绕多线程的原子性和可见性以及有序性展开的。
总结:
我们定义的变量都存储在物理内存中,每个线程都有自己独立的工作内存。里面保存该线程使用到的变量的副本。
️ volatile
使用 volatile 修饰共享变量,每次都会读取主内存中的数据,然后复制到工作内存中,
️ 原子类
️ 基本原子类型
案例:
class MyNumber {
AtomicInteger integer =new AtomicInteger();
public void add(){
integer.getAndIncrement();
}
}
public class AtomicIntegerDemo {
public static final int SIZE = 50;
public static void main(String[] args) throws InterruptedException {
MyNumber number = new MyNumber();
CountDownLatch latch = new CountDownLatch(SIZE);
for (int i = 0; i < SIZE; i++) {
new Thread(() -> {
try {
for (int j = 0; j < 1000; j++) {
number.add();
}
}finally {
latch.countDown();
}
}).start();
}
// 等待上面的五十个线程全部计算完成。再去获得最终结果的的值
// Thread.sleep(2);
latch.await();
System.out.println(Thread.currentThread().getName()+"\t"+"result: "+number.integer.get());
}
}
️ 对象属性修改的原子类
案例:
class MyVar {
public volatile Boolean isInit = Boolean.FALSE;
AtomicReferenceFieldUpdater<MyVar, Boolean> updater = AtomicReferenceFieldUpdater.newUpdater(MyVar.class, Boolean.class, "isInit");
public void init(MyVar myVar) {
if (updater.compareAndSet(myVar, Boolean.FALSE, Boolean.TRUE)) {
System.out.println(Thread.currentThread().getName() + "\t" + "------ start");
try {
Thread.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "\t" + "------ over");
} else {
System.out.println(Thread.currentThread().getName() + "\t" + "------ 已经有线程进行初始化操作!");
}
}
}
/** * @author: yueLQ * @date: 2022-06-30 11:57 */
public class AtomicReferenceFieldUpdateDemo {
public static void main(String[] args) {
MyVar myVar = new MyVar();
for (int i = 0; i < 5; i++) {
new Thread(() -> {
myVar.init(myVar);
}, String.valueOf(i)).start();
}
}
}
边栏推荐
- Mybati SQL statement printing
- robots. Txt restrict search engine inclusion
- 【机器学习】向量化计算 -- 机器学习路上必经路
- Golang多图生成gif
- [applet project development -- JD mall] uni app commodity classification page (Part 2)
- Codeforces Round #416 (Div. 2) C. Vladik and Memorable Trip
- Mouse over effect 7
- Introduction and basic knowledge of machine learning
- js 找出两个数组中的重复元素
- POI导出excel,按照父子节点进行分级显示
猜你喜欢
![[small program project development -- Jingdong Mall] the home page commodity floor of uni app](/img/80/20bed20a6ab91e82ad6800b11f2caa.png)
[small program project development -- Jingdong Mall] the home page commodity floor of uni app

Network address translation (NAT) technology

Design practice of current limiting components

Contrastive learning of Class-agnostic Activation Map for Weakly Supervised Object Localization and

STM32 - DS18B20 temperature sampling of first-line protocol

EtherCAT简介

VMware vSphere 6.7 virtualization cloud management 12. Vcsa6.7 update vCenter server license

几行事务代码,让我赔了16万

Restcloud ETL data realizes incremental data synchronization through timestamp

安装VCenter6.7【VCSA6.7(vCenter Server Appliance 6.7) 】
随机推荐
EtherCAT原理概述
[exsi] transfer files between hosts
If a parent class defines a parameterless constructor, is it necessary to call super ()?
MySQL knowledge points
Borrowing constructor inheritance and composite inheritance
【Qt】添加第三方库的知识补充
If I am in Beijing, where is a better place to open an account? In addition, is it safe to open a mobile account?
Summary of problems encountered in debugging positioning and navigation
世界上最好的学习法:费曼学习法
Classic programming problem: finding the number of daffodils
[applet project development -- Jingdong Mall] classified navigation area of uni app
HTB-Lame
Record a service deployment failure troubleshooting
Golang多图生成gif
性能测试常见面试题
Is it safe to open a stock account? Shanghai stock account opening procedures.
UE4 rendering pipeline learning notes
Common interview questions for performance test
Elk elegant management server log
PHP batch Excel to word