业务: 普通的线程代码, 之前都是用的thread或者runnable接口
但是相比于callable来说,thread没有返回值,且效率没有callable高
2、线程和进程
=======
- 进程
一个运行中的程序的集合;一个进程往往可以包含多个线程,至少包含一个线程
- java默认有几个线程?
两个。 main线程、gc线程
- 线程
线程(thread)是操作系统能够进行运算调度的最小单位
- 对于java而言如何创建thread
继承自thread
实现runnable接口
实现callable接口
- Java真的可以开启线程吗?
开不了的。底层是用native关键词修饰。调用本地实现(start0())
public synchronized void start() {
/**
-
This method is not invoked for the main method thread or “system”
-
group threads created/set up by the VM. Any new functionality added
-
to this method in the future may have to also be added to the VM.
-
A zero status value corresponds to state “NEW”.
*/
if (threadStatus != 0)
throw new IllegalThreadStateException();
/* Notify the group that this thread is about to be started
-
so that it can be added to the group’s list of threads
-
and the group’s unstarted count can be decremented. */
group.add(this);
boolean started = false;
try {
start0();
started = true;
} finally {
try {
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
/* do nothing. If start0 threw a Throwable then
it will be passed up the call stack */
}
}
}
//本地方法,调用底层c++, java无法操作硬件
private native void start0();
并发,并行
并发编程: 并发和并行
并发(多线程操作同一个资源,交替执行)
- CPU一核, 模拟出来多条线程,天下武功,唯快不破,快速交替
并行(多个人一起行走, 同时进行)
- CPU多核,多个线程同时进行 ; 使用线程池操作
public static void main(String[] args) {
//获取CPU核数
//CPU 密集型,IO密集型
System.out.println(Runtime.getRuntime().availableProcessors());
}
并发编程的本质: 充分利用CPU的资源
- 线程有几个状态?
public enum State {
// 新生
NEW,
// 运行
RUNNABLE,
// 阻塞
BLOCKED,
// 等待
WAITING,
//超时等待
TIMED_WAITING,
//终止
TERMINATED;
}
| 区别 | wait | sleep |
| — | — | — |
| 来自不同的类 | object类 | 线程类 |
| 锁的释放不同 | 会释放锁 | 不会释放锁 |
| 使用范围不同 | 必须在同步代码中 | 可以在任何地方睡 |
TimeUnit
TimeUnit.SECONDS.sleep(3);//睡眠3秒
3、Lock锁(重点)
===========
3.1、传统synchronized
本质: 队列和锁,放在方法上锁的是this,放在代码块中锁的是()里面的对象
synchronized(obj){
}
3.2、Lock 接口
- reentrantLock构造器
非公平锁:NonfairSync()
十分不公平,可以插队(默认)
公平锁:FairSync()
十分公平,先来后到,一定要排队
public ReentrantLock() {
sync = new NonfairSync(); //无参默认非公平锁
}
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();//传参为true为公平锁
}
3.3、synchronized 和 lock 区别
| 比较 | Lock | synchronized |
| — | — | — |
| 类型 | java接口 | 内置关键字 |
| 开启释放 | 显式锁手动 开启 和 关闭 | 隐式锁
出了作用域自动释放 |
| 代码锁 | 支持 | 支持 |
| 方法锁 | 不支持 | 支持 |
| 锁状态 | 可以判断是否获取到了锁 | 无法判断获取锁的状态 |
| 线程阻塞 | Lock锁就不一定会等待下去 | 线程1(获得锁,阻塞),线程2(等待) |
| 可重入性 | 可重入的,可以判断锁 | 可重入锁,不可以中断的 |
| 公平性 | 默认非公平的(可设置) | 非公平的 |
| 性能 | 性能更好
JVM将花费较少的时间来调度 | 性能一般
JVM将花费较多的时间来调度管理 |
| 扩展性 | 有更好的扩展性(提供更多的子类) | 不支持 |
| 适用场景 | 适合锁大量的同步代码 | 适合锁少量的代码同步问题 |
4、生产者和消费者问题
===========
面试高频:单例模式、八大排序、生产者消费者、死锁
4.1、synchronized实现
wait、notify 必须在 synchronizied 声明的代码中,否则会抛出异常 java.lang.IllegalMonitorStateException
package providerConsumer;
/**
-
@author ajun
-
Date 2021/7/3
-
@version 1.0
-
synchronized版生产者消费者
*/
public class Syn {
public static void main(String[] args) {
Data data = new Data();
//线程1
new Thread(() -> {
for (int i = 0; i < 5; i++) {
try {
data.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},“A”).start();
//线程2
new Thread(() -> {
for (int i = 0; i < 5; i++) {
try {
data.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},“B”).start();
//线程3
new Thread(() -> {
for (int i = 0; i < 5; i++) {
try {
data.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},“C”).start();
//线程4
new Thread(() -> {
for (int i = 0; i < 5; i++) {
try {
data.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},“D”).start();
}
}
class Data{
private int num;
public int getNum() {
return num;
}
//增加
public synchronized void increment() throws InterruptedException {
//判断等待
//用while,不用if,防止虚假唤醒
while(num!=0){
this.wait();
}
//业务代码
num++;
System.out.println(Thread.currentThread().getName() + " --> " + num);
//通知
this.notifyAll();
}
//减少
public synchronized void decrement() throws InterruptedException {
//判断等待
//用while,不用if,防止虚假唤醒
while(num == 0){
this.wait();
}
//业务代码
num–;
System.out.println(Thread.currentThread().getName() + " --> " + num);
//通知
this.notifyAll();
}
}
- 可能存在的问题
虚假唤醒
在判断等待时,如果用 if ,当线程多的时候,可能会有虚假唤醒
解决办法
if 判断改为 while 判断
因为 if 只会执行一次,执行完会接着向下执行 if()外边的
而 while 不会,直到条件满足才会向下执行 while()外边的
4.2、JUC实现
在JUC中,Lock 替换 synchronized,await 替换 wait,signal 替换 notify。(signal 信号)
package providerConsumer;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
-
@author ajun
-
Date 2021/7/3
-
@version 1.0
*/
public class Loc {
public static void main(String[] args) {
Data2 data2 = new Data2();
new Thread(() -> {
for (int i = 0; i < 5; i++) {
try {
data2.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},“A”).start();
new Thread(() -> {
for (int i = 0; i < 5; i++) {
try {
data2.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},“B”).start();
new Thread(() -> {
for (int i = 0; i < 5; i++) {
try {
data2.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},“C”).start();
new Thread(() -> {
for (int i = 0; i < 5; i++) {
try {
data2.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},“D”).start();
}
}
class Data2{
private int num = 0;
Lock lock = new ReentrantLock();//定义锁
Condition condition = lock.newCondition();//定义同步监视器
public int getNum() {
return num;
}
//增加
public void increment() throws InterruptedException {
lock.lock();//加锁
try {
//判断等待
while (num != 0){
condition.await();//等待
}
//业务代码
num++;
System.out.println(Thread.currentThread().getName() + " --> " + num);
//通知
condition.signalAll();
} finally {
lock.unlock();//解锁
}
}
//减少
public void decrement() throws InterruptedException {
lock.lock();//加锁
try {
//判断等待
while (num == 0){
condition.await();//等待
}
//业务代码
num–;
System.out.println(Thread.currentThread().getName() + " --> " + num);
//通知
condition.signalAll();
} finally {
lock.unlock();//解锁
}
}
}
Condition实现精准通知唤醒
5、8锁现象
======
8锁就是关于锁的八个现象
① 非静态同步方法的默认锁是 this,静态同步方法的默认锁是 class
面试结束复盘查漏补缺
每次面试都是检验自己知识与技术实力的一次机会,面试结束后建议大家及时总结复盘,查漏补缺,然后有针对性地进行学习,既能提高下一场面试的成功概率,还能增加自己的技术知识栈储备,可谓是一举两得。
以下最新总结的阿里P6资深Java必考题范围和答案,包含最全MySQL、Redis、Java并发编程等等面试题和答案,用于参考~
重要的事说三遍,关注+关注+关注!
更多笔记分享
id decrement() throws InterruptedException {
lock.lock();//加锁
try {
//判断等待
while (num == 0){
condition.await();//等待
}
//业务代码
num–;
System.out.println(Thread.currentThread().getName() + " --> " + num);
//通知
condition.signalAll();
} finally {
lock.unlock();//解锁
}
}
}
Condition实现精准通知唤醒
5、8锁现象
======
8锁就是关于锁的八个现象
① 非静态同步方法的默认锁是 this,静态同步方法的默认锁是 class
面试结束复盘查漏补缺
每次面试都是检验自己知识与技术实力的一次机会,面试结束后建议大家及时总结复盘,查漏补缺,然后有针对性地进行学习,既能提高下一场面试的成功概率,还能增加自己的技术知识栈储备,可谓是一举两得。
以下最新总结的阿里P6资深Java必考题范围和答案,包含最全MySQL、Redis、Java并发编程等等面试题和答案,用于参考~
重要的事说三遍,关注+关注+关注!
[外链图片转存中…(img-wTr5OrQQ-1720081938169)]
[外链图片转存中…(img-WldXciyO-1720081938170)]
更多笔记分享
[外链图片转存中…(img-rpo3dN0a-1720081938171)]