线程的基本知识:
线程和进程的区别
并行和并发的区别
线程创建的方式有哪些
runnable 和callable有什么区别
线程包括哪些状态,状态之间是如何变化的
在java中wait和sleep方法的不同
新建三个线程,如何保证他们按顺序执行
notify() 和 notifyAll()有什么区别
线程的run()和start() 有什么区别
如何停止一个正在运行的线程
线程中并发安全:
synchronized 关键字的底层原理
你谈谈JVM(Java 内存模型)
什么是CAS?
什么是AQS?
ReentrantLock的实现原理
synchronized 和 Lock有什么区别
死锁产生的条件是什么?
如何进行死锁的诊断
请谈谈你对volatile的理解
聊一聊ConcurrentHashMap
导致并发程序出现问题的根本原因是什么?
线程池:
说一下线程池的核心参数(线程池的执行原理知道吗)
线程池中有哪些常见的阻塞队列
如何确定核心线程数
线程池的种类有哪些?
为什么不建议使用Executors创建线程池
使用场景:
线程池使用场景(你们项目中哪些地方用到线程池)
如何控制某一个方法允许并发访问线程的数量
谈谈你对ThreadLocal的理解
进程与线程的区别
程序由指令和数据组成的,但这些指令要运行,数据要读写,就必须将指令加载到CPU,数据加载到内存。在指令运行过程中还需要用到磁盘、网络等设备。进程就是用来加载指令、管理内存、管理IO的
当一个程序被运行,从磁盘中加载这个程序的代码至内存,这时就开始了一个进程。
向浏览器和txt文本可以被打开多氛,那么就是多实例进程,而向企业微信,微信只能打开一个,那么就是单实例进程。
一个线程就是一个指令流,将指令流中的一条指令以一定的顺序交给CPU执行
一个进程之内可以分为一到多个线程。
二者对比:
- 进程是正在运行程序的实力,进程包含了线程,每个线程执行不同的任务
- 不同的进程使用了不同的内存空间,在当前进程下的所有线程可以共享内存空间
- 线程更加轻量,线程的上下文切换成本一般上要比进程上下文切换低(上下文切换指的是从一个线程切换到另一个线程)
并行与并发的区别
单核CPU
- 单核CPU下线程实际还是串行执行的
- 操作系统中有一个组件叫做任务调度器,将cpu的时间片(windows下时间片最小约为15毫秒)分给不同的程序,只是由于cpu在线程间(时间片很短)的切换非常的快,人类的感觉是同时运行的。
- 总结为一句话就是:微观串行,宏观并行
- 一般会将这种线程轮流使用CPU的做法成为并发(concurrent)
多核CPU
- 每个核(core)都可以调度运行线程,这时候线程可以是并行的。
并行和并发有什么区别?
- 并发(concurrent)是同一时间应对(dealing with)多件事情的能力
- 并行(parallel)是同一时间动手做(doing)多件事情的能力
家庭主妇做饭,打扫卫生、给孩子喂奶,他一个人轮流交替做这多件事情,这时就是并发
家庭主妇雇佣了一个保姆,他们一起做这些事情,这是既有并发,也有并行(这时会产生竞争,例如一口锅,一个人用锅的时候,另一个人就得等待)
雇佣了3个保姆,一个专门做饭,一个专门打扫卫生,一个专门喂奶,互不干扰,这是是并行
并行和并发有什么区别呢?
现在多核CPU,在多核CPU下
- 并发是同一时间应对多件事情的能力,多个线程轮流使用一个或多个CPU
- 并行是同一时间动手做多件事情的能力,4核CPU同时执行4个线程
创建线程的方式有哪些?
继承Thread类
package com.an.thread;
/**
* 创建线程的方式
* 第一种继承 Thread接口 实现run方法
*
* @author An
* @Date 2023/5/25 14:04
*/
public class CreateThread extends Thread {
@Override
public void run() {
System.out.println("Thread run......");
}
public static void main(String[] args) {
// 创建 CreateThread 对象
CreateThread t1 = new CreateThread();
CreateThread t2 = new CreateThread();
// 调用start方法启动线程
t1.start();
t2.start();
}
}
实现Runnable接口
package com.an.thread;
/**
* 创建线程的方式
* 第二种方式:实现runnable接口
*
* @author An
* @Date 2023/5/25 14:11
*/
public class CreateRunnable implements Runnable {
@Override
public void run() {
System.out.println("runnable run....");
}
public static void main(String[] args) {
// 创建 CreateRunnable 对象
CreateRunnable t1 = new CreateRunnable();
CreateRunnable t2 = new CreateRunnable();
// 调用run方法
t1.run();
t2.run();
}
}
实现Callable接口
package com.an.thread;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
/**
* 线程创建的方式
* 第三种方式:实现callable接口
*
* @author An
* @Date 2023/5/25 14:15
*/
public class CreateCallable implements Callable<String> {
@Override
public String call() throws Exception {
System.out.println(Thread.currentThread().getName());
return "ok";
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
// 创建CreateCallable 对象
CreateCallable mc = new CreateCallable();
// 创建 FutureTask对象
FutureTask<String> ft = new FutureTask<String>(mc);
// 创建Thread对象
Thread t1 = new Thread();
Thread t2 = new Thread();
// 调用start方法启动线程
t1.start();
// 调用ft.get方法获取执行结果
String result = ft.get();
// 输出
System.out.println(result);
}
}
创建线程池
package com.an.thread;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* 创建线程的方式
* 第四种方式:创建线程池
*
* @author An
* @Date 2023/5/25 14:23
*/
public class CreateExecutors implements Runnable {
@Override
public void run() {
System.out.println("CreateExecutors run......");
}
public static void main(String[] args) {
// 创建线程池对象
ExecutorService threadPool = Executors.newFixedThreadPool(3);
threadPool.submit(new CreateExecutors());
// 关闭线程池
threadPool.shutdown();
}
}
实现Runnable和Callable接口有什么区别呢?
- Runnable接口的run方法没有返回值
- Callable接口的call方法有返回值,是个泛型,和Future、FutureTask配合可以用来获取异步执行的结果
- Callable接口的call方法允许抛出异常;而Runnable接口的run()方法的异常只能在内部消化,不能继续上抛
在启动线程的时候,可以使用run()方法吗?run() 和start()方法有什么区别?
package com.an.thread;
/**
* start方法和run方法的区别
*
* @author An
* @Date 2023/5/25 14:38
*/
public class StartAndRunDiff {
public static void main(String[] args) {
Thread thread = new Thread() {
@Override
public void run() {
System.out.println("running......");
}
};
// run 表示封装要被线程执行的代码,可以被调用多次
thread.run();
//thread.run();
//thread.run();
// 用来启动线程,通过该线程调用run方法执行run方法中所定义的逻辑代码,start方法只能被调用一次
//thread.start();
//thread.start();
//thread.start();
}
}
参考回答:
start():用来启动线程,通过该线程调用run方法执行run方法中所定义的逻辑代码,start方法只能被调用一次
run():封装了要被线程执行的代码,可以被调用多次
- 创建线程的方式有哪些?
- 继承Thread类
- 实现runnable接口
- 实现callable接口
- 线程池创建线程(项目中使用)
- runnable和callable有什么区别
- Runnable接口的run方法没有返回值
- Callable接口的call方法有返回值,需要FutureTask获取结果
- Callable接口的call()方法允许抛出异常,而Runnable接口的run方法的异常只能内部消化,不能继续往上抛出
- run()和start()有什么区别?
- start():用来启动线程,通过线程调用run方法执行run方法所定义额逻辑代码,start方法只能被调用一次
- run():封装了要被线程执行的代码,可以被调用多次。
线程中有哪些状态,状态之间是如何变化的?
Thread类的内部有个state枚举里面描述了线程的状态:
public enum State {
/**
* Thread state for a thread which has not yet started.
* 尚未启动的线程的线程状态
*/
NEW,
/**
* Thread state for a runnable thread. A thread in the runnable
* state is executing in the Java virtual machine but it may
* be waiting for other resources from the operating system
* such as processor.
* 可运行线程的线程状态
*/
RUNNABLE,
/**
* Thread state for a thread blocked waiting for a monitor lock.
* A thread in the blocked state is waiting for a monitor lock
* to enter a synchronized block/method or
* reenter a synchronized block/method after calling
* {@link Object#wait() Object.wait}.
* 线程阻塞等待监视器锁的线程状态
*/
BLOCKED,
/**
* Thread state for a waiting thread.
* A thread is in the waiting state due to calling one of the
* following methods:
* <ul>
* <li>{@link Object#wait() Object.wait} with no timeout</li>
* <li>{@link #join() Thread.join} with no timeout</li>
* <li>{@link LockSupport#park() LockSupport.park}</li>
* </ul>
*
* <p>A thread in the waiting state is waiting for another thread to
* perform a particular action.
*
* For example, a thread that has called <tt>Object.wait()</tt>
* on an object is waiting for another thread to call
* <tt>Object.notify()</tt> or <tt>Object.notifyAll()</tt> on
* that object. A thread that has called <tt>Thread.join()</tt>
* is waiting for a specified thread to terminate.
* 等待线程的线程状态
*/
WAITING,
/**
* Thread state for a waiting thread with a specified waiting time.
* A thread is in the timed waiting state due to calling one of
* the following methods with a specified positive waiting time:
* <ul>
* <li>{@link #sleep Thread.sleep}</li>
* <li>{@link Object#wait(long) Object.wait} with timeout</li>
* <li>{@link #join(long) Thread.join} with timeout</li>
* <li>{@link LockSupport#parkNanos LockSupport.parkNanos}</li>
* <li>{@link LockSupport#parkUntil LockSupport.parkUntil}</li>
* </ul>
* 具有指定等待时间的等待线程的线程状态
*/
TIMED_WAITING,
/**
* Thread state for a terminated thread.
* The thread has completed execution.
* 已终止线程的线程状态,线程已完成执行
*/
TERMINATED;
}
线程中的状态执行的详情:
线程中主要包括哪些状态?
新建(NEW),可运行(RUNNABLE),阻塞(BLOCKED),等待(WAITING),时间等待(计时等待)(TIME_WAITING),终止(TERMINATED)
线程状态之间是如何变化的
- 创建线程对象是新建状态
- 调用了start()方法转变为可执行状态
- 线程获取到了CPU的执行权,执行结束时终止状态
- 可执行状态的过程中,如果没有获取CPU的执行权,可能会切换其他状态
- 如果没有获取锁(synchronized或lock)进入阻塞状态,获得锁再切换成可执行状态
- 如果线程调用了wait()方法进入等待状态,其他线程调用notify()唤醒后可以切换成可执行状态。
- 如果线程调用了sleep(50)方法,进入计时等待状态,到时间可切换为可执行状态
线程的顺序执行
join方法
示例:
package com.an.thread;
/**
* 线程的顺序执行join()方法
*
* @author An
* @Date 2023/5/25 15:39
*/
public class ThreadJoin {
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
System.out.println("t1");
});
Thread t2 = new Thread(() -> {
try {
t1.join();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("t2");
});
Thread t3 = new Thread(() -> {
try {
t2.join();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("t3");
});
t1.start();
t2.start();
t3.start();
}
}
notify和notifyAll有什么区别
- notifyAll:唤醒所有wait的线程
- notify:只随机唤醒一个wait线程
package com.an.thread;
/**
* 线程等待唤醒
* notify和notifyAll的区别
*
* @author An
* @Date 2023/5/25 15:47
*/
public class WaitNotify {
static Object lock = new Object();
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
synchronized (lock) {
System.out.println(Thread.currentThread().getName()+ "...wait...");
try {
lock.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(Thread.currentThread().getName()+ "...被唤醒了");
}
}, "t1");
Thread t2 = new Thread(() -> {
synchronized (lock) {
System.out.println(Thread.currentThread().getName()+ "...wait...");
try {
lock.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(Thread.currentThread().getName()+ "...被唤醒了");
}
}, "t2");
t1.start();
t2.start();
Thread.sleep(2000);
synchronized (lock) {
// 随机唤醒一个wait等待的线程
//lock.notify();
// 唤醒所有wait等待的线程
lock.notifyAll();
}
}
}
wait方法和sleep方法的不同
共同点:
wait(),wait(long)和sleep(long)的效果都是让当前线程暂时放弃CPU的使用权,进入阻塞状态
不同点
- 方法的归属不同
- sleep(long)是Thread的静态方法
- 而wait(),wait(long)都是Object的成员方法,每个对象都有
- 醒来机制不同
- 执行sleep(long)和wait(long)的线程都会在等待相应毫秒后醒来
- wait(long)和wait()还可以被notify唤醒,wait()如果不唤醒就一直等下去
- 他们都可以被打断唤醒
- 锁特性不同(重点)
- wait方法的调用必须先获取wait对象的锁,而sleep则无此限制
- wait方法执行后必须释放对象锁,允许其他线程获得该对象锁(我放弃cpu,但你们还可以用)
- 而sleep如果在synchronized代码块中执行,并不会释放对象锁(我放弃了cpu,你们也用不了)
package com.an.thread;
/**
* wait和sleep方法有什么不同
* <p>
* 分为三点
* * wait方法的调用必须先获取wait对象的锁,而sleep则无此限制
* * wait方法执行后必须释放对象锁,允许其他线程获得该对象锁(我放弃cpu,但你们还可以用)
* * 而sleep如果在synchronized代码块中执行,并不会释放对象锁(我放弃了cpu,你们也用不了)
*
* @author An
* @Date 2023/5/25 16:30
*/
public class WaitSleepCase {
static final Object LOCK = new Object();
public static void main(String[] args) throws InterruptedException {
illegalWait();
}
private static void illegalWait() throws InterruptedException {
// 而sleep如果在synchronized代码块中执行,并不会释放对象锁(我放弃了cpu,你们也用不了)
synchronized (LOCK) {
LOCK.wait();
}
}
private static void waiting() throws InterruptedException {
Thread t1 = new Thread(() -> {
synchronized (LOCK) {
try {
//get("t").debug("waiting...");
LOCK.wait();
//get("t").debug("running...end...");
} catch (InterruptedException e) {
//get("t").debug("interrupted...end...");
throw new RuntimeException(e);
}
}
}, "t1");
t1.start();
Thread.sleep(100);
synchronized(LOCK) {
//main.debug("other...");
}
}
private static void sleeping() throws InterruptedException {
Thread t1 = new Thread(() -> {
synchronized (LOCK) {
try {
//get("t").debug("waiting...");
Thread.sleep(5000L);
//get("t").debug("running...end...");
} catch (InterruptedException e) {
//get("t").debug("interrupted...end...");
throw new RuntimeException(e);
}
}
}, "t1");
t1.start();
Thread.sleep(100);
synchronized(LOCK) {
//main.debug("other...");
}
}
}
停止一个正在执行的线程
通过标记正常退出
package com.an.thread;
/**
* 如何停止一个正在运行的线程
*
* @author An
* @Date 2023/5/25 16:51
*/
public class Interrupt1 extends Thread {
volatile boolean flag = false; // 线程执行退出标记
@Override
public void run() {
while (!flag) {
System.out.println("running...");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
public static void main(String[] args) throws InterruptedException {
// 创建 Thread 对象
Interrupt1 t1 = new Interrupt1();
t1.start();
// 主线程休眠6秒
Thread.sleep(6000);
// 更改标记为 true
t1.flag = true;
}
}
采用stop方法退出(不推荐)
package com.an.thread;
/**
* 如何停止一个正在运行的线程
* 第二种方法采用stop方法退出
*
* @author An
* @Date 2023/5/25 16:51
*/
public class Interrupt2 extends Thread {
volatile boolean flag = false; // 线程执行退出标记
@Override
public void run() {
while (!flag) {
System.out.println("running...");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
public static void main(String[] args) throws InterruptedException {
// 创建 Thread 对象
Interrupt2 t1 = new Interrupt2();
t1.start();
// 主线程休眠6秒
Thread.sleep(6000);
// 采用stop方法退出 (不推荐)
t1.stop();
}
}
第三种方法通过Iterrupt方法退出线程
package com.an.thread;
/**
* 如何停止一个正在运行的线程
* 第三种方法:使用interrupt方法中断线程
* 打断阻塞的线程(sleep,wait,join)的线程,线程会抛出InterruptedException异常
* 打断正常的线程,可以根据打断状态来标记是否退出线程
*
* @author An
* @Date 2023/5/25 16:51
*/
public class Interrupt3 extends Thread {
public static void main(String[] args) throws InterruptedException {
// 1. 打断阻塞的线程
/*Thread t1 = new Thread(() -> {
System.out.println("t1 正在运行...");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "t1");
t1.start();
Thread.sleep(500);
t1.interrupt();
System.out.println(t1.isInterrupted());*/
// 2. 打断正常的线程
Thread t2 = new Thread(() -> {
while (true) {
Thread currentThread = Thread.currentThread();
boolean interrupted = currentThread.isInterrupted();
if (interrupted) {
System.out.println("打断状态:" + interrupted);
break;
}
}
}, "t1");
t2.start();
Thread.sleep(500);
t2.interrupt();
}
}
如何停止一个正在运行的线程?
有三种方式可以停止线程:
- 使用退出标志,使线程正常退出,也就是当run方法完成后线程终止
- 使用stop方法强行终止(不推荐,方法已经作废)
- 使用interrupt方法中断线程
- 打断阻塞的线程(sleep,wait,join)的线程会抛出 InterruptException异常
- 打断正常的线程,可以根据打断的状态来标记是否退出线程
笔记是对黑马课程中的知识进行的个人总结,图片借鉴了课程视频中的资料,感谢黑马程序员的开源精神,哈哈,如有问题联系我删除!