前言:

本文内容:线程和进程回顾、回顾多线程、传统的Synchronized锁

推荐免费JUC并发编程视频:【狂神说Java】JUC并发编程最新版通俗易懂_哔哩哔哩_bilibili

什么是JUC?

java.util.concurrent包的第一个字母缩写JUC

线程和进程回顾

准备工作

新建一个Maven项目,引入lombok

进程和线程

进程:程序的集合

一个进程可以包含多个线程,至少包含一个。

Java默认有几个线程?

默认有2个线程:main,GC

Java开启线程:Thread、Runnable、Callable

Java真的能开启线程吗?

不可以,Java通过start()方法开启线程,start()方法最后去调用本地方法native来开启,而底层是C++,Java不能直接操作硬件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
if (threadStatus != 0)
throw new IllegalThreadStateException();

group.add(this);

boolean started = false;
try {
start0();
started = true;
} finally {
try {
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
}
}
}

private native void start0();

并发和并行

并发编程:并发、并行

并发(多线程操作同一个资源)

  • CPU一核,模拟出来多条线程;

并行(多个线程同时执行)

  • CPU多核,多个线程同时执行;
1
2
3
// 打印CPU核数
// CUP 密集型 IO密集型
System.out.println(Runtime.getRuntime().availableProcessors());

并发编程的本质:充分利用CUP的资源

回顾多线程

线程有几个状态

Thread.State

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public enum State {
// 线程新生
NEW,
// 线程运行
RUNNABLE,
// 线程阻塞
BLOCKED,
// 线程等待
WAITING,
// 线程超时等待
TIMED_WAITING,
// 线程终止
TERMINATED;
}

wait/sleep的区别

  1. 来自不同的类

    wait-->Object

    sleep-->Thread

  2. 关于锁的释放

    wait会释放锁,sleep不会释放锁;

  3. 使用的范围不同

    wait必须在同步代码块中

    sleep可以在任何地方

  4. 是否需要捕获异常

    线程需要捕获中断异常(InterruptedException)

    wait按需求是否捕获异常

    sleep必须要捕获异常

传统的Synchronized锁

SaleTicketDemo01.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
package com.jokerdig;

/**
* @author Joker大雄
* @data 2022/8/16 - 14:14
**/
// 基本售票例子
/*
真正的多线程开发
线程就是一个单独的资源类,没有任何负数操作
包含:属性、方法
*/
public class SaleTicketDemo01 {
public static void main(String[] args) {
// 并发,多线程操作同一个资源类
Ticket ticket = new Ticket();
// @FunctionalInterface 函数表达式接口
// 使用lambda表达式(参数)->{代码}
new Thread(()->{
for(int i = 1;i<40;i++){
ticket.sale();
}
},"A").start();
new Thread(()->{
for(int i = 1;i<40;i++){
ticket.sale();
}
},"B").start();
new Thread(()->{
for(int i = 1;i<40;i++){
ticket.sale();
}
},"C").start();
}
}
// 资源类 OOP
class Ticket{
// 属性 方法
private int number = 30;
// 卖票 传统方式解决
// synchronized 本质:锁 队列
public synchronized void sale(){
if(number>0){
System.out.println(Thread.currentThread().getName()+"卖出了第"+(number--)+"票,剩余:"+number);
}
}
}

运行结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
A卖出了第30票,剩余:29
A卖出了第29票,剩余:28
A卖出了第28票,剩余:27
A卖出了第27票,剩余:26
A卖出了第26票,剩余:25
A卖出了第25票,剩余:24
A卖出了第24票,剩余:23
B卖出了第23票,剩余:22
A卖出了第22票,剩余:21
A卖出了第21票,剩余:20
A卖出了第20票,剩余:19
A卖出了第19票,剩余:18
A卖出了第18票,剩余:17
A卖出了第17票,剩余:16
A卖出了第16票,剩余:15
A卖出了第15票,剩余:14
A卖出了第14票,剩余:13
A卖出了第13票,剩余:12
A卖出了第12票,剩余:11
A卖出了第11票,剩余:10
A卖出了第10票,剩余:9
A卖出了第9票,剩余:8
A卖出了第8票,剩余:7
A卖出了第7票,剩余:6
A卖出了第6票,剩余:5
A卖出了第5票,剩余:4
A卖出了第4票,剩余:3
A卖出了第3票,剩余:2
A卖出了第2票,剩余:1
A卖出了第1票,剩余:0

Process finished with exit code 0