多线程学习笔记
这里写目录标题
多线程基础
一、进程与线程
1.1 进程与线程的概念
-
进程是计算机的程序关于某数据集合的一次运行活动
-
进程是资源分配的最小单位,线程是资源调度的最小单位
-
线程是进程中一个独立的执行流
**进程和程序:**程序是指令、数据及其组织形式的描述,进程是程序的实体
1.2 线程状态
线程状态图:
五个状态:
- 新建:new创建后到start方法前
- 就绪:调用start方法后就是处于就绪态
- 运行:获取CPU资源,执行run方法,此时就是运行态
- 阻塞:
- 同步阻塞:synchronized获取同步锁失败阻塞
- 等待阻塞:执行wait方法后
- 其他阻塞:sleep,join方法执行后
- 死亡:线程执行结束,或其他条件终止
二、多线程
2.1 概念区分
并发与并行:
- 并发:一个时间段内,几个任务同时进行。
- 并行:同一时间点,几个任务同时进行,必要条件:多核,多处理器。
同步与异步:
- 同步:任务有序执行,一个任务执行完,再执行下一个任务。
- 异步:几个任务独立执行,执行一个任务的同时也可以执行另一个任务。
异步与多线程:
- 异步是目的,多线程是实现异步的一种手段
什么是多线程:
- 是指从软件或者硬件上实现多个线程并发执行的技术
2.2 多线程编程应用场景
- 高并发
- 处理大任务:分步上传,分步计算
- 耗时操作
- 低优先级定时操作:垃圾回收
三、线程编程
3.1 线程的创建方式
-
继承Thread类:
- 重写run方法
-
实现Runnable接口:
- 重写run方法
- 将Runnable交给Thread执行
-
Callable接口和FutureTask
- 基于委托的适配器模式(FutureTask适配Runnable和Callable)
- 将FutureTask(Runnable)交给Thread执行
-
线程池
- 将Runnable交给线程池的execute方法执行
public class ThreadTest {
public static void main(String[] args) {
//方式1 Thread
MyThread myThread = new MyThread("w");
myThread.start();
//方式2 Runnable
MyRunnable myRunnable = new MyRunnable();
new Thread(myRunnable,"我的线程2").start();
//方式3 Callable和FutureTask
MyCallable myCallable = new MyCallable();
FutureTask<Integer> futureTask = new FutureTask<>(myCallable);
new Thread(futureTask,"我的线程3").start();
//方式4 线程池
ThreadPoolExecutor threadPool = new ThreadPoolExecutor(2, 5, 2L, TimeUnit.SECONDS,
new ArrayBlockingQueue<>(3),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy());
threadPool.execute(myRunnable);
}
}
class MyThread extends Thread {
public MyThread(String name) {
super(name);
}
@Override
public void run() {
System.out.println("我的线程");
}
}
class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("创建可执行任务");
}
}
class MyCallable implements Callable {
@Override
public Integer call() throws Exception {
System.out.println("创建MyCallable");
return 1;
}
}
3.2 线程优先级
Java线程优先级是整数,范围(Thread.MIN_PRIORITY )1 —(Thread.MAX_PRIORITY )10,默认优先级 NORM_PRIORITY(5)。
更改优先级:
setPriority(int newPriority)
3.3 关键字和方法讲解
关键字:
- volatile:变量可见,防止指令重排
- synchronzied:同步悲观锁
- 修饰普通方法:锁当前对象
- 修饰静态方法:锁类对象
- 修饰同步方法块:锁synchronized括号里配置的对象
方法:
- wait:
- 是对象进入等待态,同步阻塞
- wait会释放锁,出现中须定义同步锁
- notify:随机唤醒等待队列中持有相同锁的对象
- notifyAll:唤醒所有等待队列中持有相同锁的对象
- sleep:线程休眠
- join:让调用方法的线程加入到当前线程,顺序执行
- yield:让步,让相同或更高优先级的线程执行
- interrupt:设置线程中断标志位为true
- interrupt可以的打断sleep,wait,join使其抛出异常
- interrupted:返回中断状态,并清除中断状态
- isInterrupted:返回中断状态,不清除中断状态
问题:
volatile原理:
将变量的修改直接写入主存中,保证各个线程对于变量的可见性。
sleep方法和wait方法:
- sleep是Thread类的静态方法,wait是Object的实例方法
- sleep不会释放锁,wait会释放锁
- wait用于同步代码块,sleep不需要写在同步代码块中interrupt唤醒
- wait作用与对象,sleep作用于线程
为什么wait方法是Object类
- 简单来说,Java中锁的级别是对象,由对象本身(共享资源本身)来持有锁。
3.3 线程停止方式
-
运行完停止
-
自定义中断标志符
使用volatile修饰一个变量作为标识
volatile boolean flag = false; Thread t = new Thread(()->{ while (true) { System.out.println("hello"); if (flag) { System.out.println("线程结束"); break; } } }); t.start(); Thread.sleep(1000); flag = true;
-
interrupt:
使用interrupt和isInterrupted使线程停止。
Thread t = new Thread(()->{ while (!Thread.currentThread().isInterrupted()) { System.out.println("hello"); } }); t.start(); Thread.sleep(1000); t.interrupt();
3.4 多线程编程步骤
- 创建资源类,在资源类创建属性和操作方法
- 在资源类操作方法
- 判断
- 干活
- 通知
- 创建多个线程,调用资源类的操作方法
- 防止虚假唤醒
3.5 生产者消费者模式
public class ConsumerProducer {
public static void main(String[] args) {
Buffer buffer = new Buffer(5);
Producer producer = new Producer(buffer);
Consumer consumer = new Consumer(buffer);
new Thread(producer).start();
new Thread(consumer).start();
}
}
//资源类
class Buffer {
private Queue<Integer> queue;
private int size;
public Buffer(int size) {
this.queue = new LinkedList<Integer>();
this.size = size;
}
public synchronized void add(int i) throws InterruptedException {
//判断
while (queue.size() >= size) {
wait();
}
//干活
queue.add(i);
System.out.println("生产者生成了:"+i);
//通知
notifyAll();
}
public synchronized int get() throws InterruptedException {
while (queue.isEmpty(