1、什么是JUC
业务:普通的线程代码 Thread
Runnable 没有返回值、效率相比入 Callable 相对较低!
2、线程和进程
线程是进程中的一个实体,线程本身是不会独立存在的。
进程是代码在数据集合上的一次运行活动, 是系统进行资源分配和调度的基本单位。
线程则是进程的一个执行路径, 一个进程中至少有一个线程,进程中的多个线程共享进程的资源。
操作系统在分配资源时是把资源分配给进程的, 但是CPU 资源比较特殊, 它是被分配到线程的, 因为真正要占用C PU 运行的是线程, 所以也说线程是CPU分配的基本单位。
Java 默认有 2 线程 !(mian 线程、GC 线程)
Java 中,使用 Thread、Runnable、Callable 开启线程。
Java 没有权限开启线程 、Thread.start() 方法调用了一个 native 方法 start0(),它调用了底层 C++ 代码。Java 没有权限操作底层硬件的。
3、并发编程:并发、并行
并发:多线程操作同一个资源。
- CPU 只有一核,模拟出来多条线程,天下武功,唯快不破。那么我们就可以使用CPU快速交替,来模拟多线程。
- 并发编程的本质:充分利用CPU的资源!
并行: 多个人一起行走
- CPU多核,多个线程可以同时执行。 我们可以使用线程池
package com.kuang.demo01;
public class Test1 {
public static void main(String[] args) {
// 获取cpu的核数
// CPU 密集型,IO密集型
System.out.println(Runtime.getRuntime().availableProcessors());
}
}
并发编程的本质:充分利用CPU的资源
4、回顾多线程
线程的状态:
public enum State {
// 新生
NEW,
// 运行
RUNNABLE,
// 阻塞
BLOCKED,
// 等待,死死地等
WAITING,
// 超时等待
TIMED_WAITING,
// 终止
TERMINATED;
}
wait/sleep区别:
1、来自不同的类
wait => Object
sleep => Thread
2、关于锁的释放
wait会释放锁,sleep睡觉了,抱着锁睡觉,不会释放!
3、使用的范围是不同的
wait必须在同步代码块中
sleep可以在任何地方睡
4、是否需要捕获异常
wait不需要捕获异常
sleep必须要捕获异常
5、使用synchroniezd关键字加锁
package com.kuang.demo01;
// 基本的卖票例子
import java.time.OffsetDateTime;
/**
* 真正的多线程开发,公司中的开发,降低耦合性
* 线程就是一个单独的资源类,没有任何附属的操作!
* 1、 属性、方法
*/
public class SaleTicketDemo01 {
public static void main(String[] args) {
// 并发:多线程操作同一个资源类, 把资源类丢入线程
Ticket ticket = new Ticket();
// @FunctionalInterface 函数式接口,jdk1.8 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);
}
}
}
ReentrantLock 默认的构造方法是非公平锁(可以插队)。
如果在构造方法中传入 true 则构造公平锁(不可以插队,先来后到)。
6、使用Lock类加锁
public class LockDemo {
public static void main(String[] args) {
final Ticket2 ticket = new Ticket2();
new Thread(() -> {
for (int i = 0; i < 40; i++) {
ticket.sale();
}
}, "A").start();
new Thread(() -> {
for (int i = 0; i < 40; i++) {
ticket.sale();
}
}, "B").start();
new Thread(() -> {
for (int i = 0; i < 40; i++) {
ticket.sale();
}
}, "C").start();
}
}
//lock三部曲
//1、 Lock lock=new ReentrantLock();
//2、 lock.lock() 加锁
//3、 finally=> 解锁:lock.unlock();
class Ticket2 {
private int number = 30;
// 创建锁
Lock lock = new ReentrantLock();
//卖票的方式
public synchronized void sale() {
lock.lock(); // 开启锁
try {
if (number > 0) {
System.out.println(Thread.currentThread().getName() + "卖出了第" + (number--) + "张票剩余" + number + "张票");
}
}finally {
lock.unlock(); // 关闭锁
}
}
}
7、 Synchronized与Lock 的区别
① Synchronized 内置的Java关键字, Lock 是一个Java类
② Synchronized 无法判断获取锁的状态,Lock 可以判断是否获取到了锁
③ Synchronized 会自动释放锁,lock 必须要手动释放锁!如果不释放锁,死锁
④ Synchronized 线程 1(获得锁,阻塞)、线程2(等待,傻傻的等);Lock锁就不一定会等待下去;
⑤ Synchronized 可重入锁,不可以中断的,非公平;Lock ,可重入锁,可以 判断锁,非公平(可以自己设置);
⑥ Synchronized 适合锁少量的代码同步问题,Lock 适合锁大量的同步代码!
注: “广义上的可重入锁指的是可重复可递归调用的锁,在外层使用锁之后,在内层仍然可以使用,并且不发生死锁(前提得是同一个对象或者class),这样的锁就叫做可重入锁。“
Bilibili狂神说Java笔记:https://gitee.com/kuangstudy