多线程
并发与并行
并发:多个指令在单个CPU上交替执行,CPU在多个线程之间切换。
并行:多个指令在多个CPU上同时执行。
多线程的实现
定义类,继承Thread类
重写 run()方法
新建对象 使用start()开启线程
typescript
public class MyThread extends Thread{
@Override
public void run() {
super.run();
System.out.println("hello");
}
public static void main(String[] args) {
MyThread myThread = new MyThread();
myThread.setName("T1");
myThread.start();
}
}
声名实现Runable接口的类
java
public class MyThread02 implements Runnable{
@Override
public void run() {
Thread thread = Thread.currentThread();
System.out.println(thread.getName()+ "hello,Runable");
}
public static void main(String[] args) {
MyThread02 myThread = new MyThread02();
Thread t1 = new Thread(myThread);
t1.setName("T1");
t1.start();
}
}
实现Callable接口
特点:可以获取到多线程运行结果
步骤:
创建类实现Callable接口
重写call方法
使用Futher接口实现类FutherTask管理多线程运行结果
创建Thread,FutherTask对象作为入参。
java
//Callable<Integer> 表示返回结果数据类型
public class MyThread03 implements Callable<Integer> {
@Override
public Integer call() throws Exception {
return 100;
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
MyThread03 mc = new MyThread03();
FutureTask<Integer> ft = new FutureTask<>(mc);
Thread thread = new Thread(ft);
thread.start();
Integer integer = ft.get();
System.out.println(integer);
}
}
三种方式总结:
继承Thread,可扩展性差,不能再继承其他类
实现Run able接口,编程复杂,不可直接使用Thread类中方法。
实现Callable接口, 可以获取到多线程运行结果
守护线程
setDaemon
当非守护线程执行完毕后,守护线程会陆续结束。
Join()插入/队线程
线程生命周期
#黑马截图
线程安全问题
同步代码块Synchronized
Synchronized() 锁对象必须是唯一的
例如 ;.class 、静态对象
同步方法Synchronized 修饰
特点:锁住方法里所有代码
锁对象不能自己指定
非静态方法 this 方法调用者
静态方法 当前类字节码文件对象
Lock(接口)锁
特点:支持手动获取锁、释放锁
csharp
public class Lock01 extends Thread{
static int ticket = 0;
//共享一把锁
static Lock lock = new ReentrantLock();
@Override
public void run() {
while (true){
try {
lock.lock();
if(ticket == 100) break;
else {
Thread.sleep(10);
ticket++;
System.out.println(getName()+"卖第"+ticket+"张票!!");
}
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
}
测试
public static void main(String[] args) {
Lock01 t1 = new Lock01();
Lock01 t2 = new Lock01();
Lock01 t3 = new Lock01();
t1.setName("窗口一");
t2.setName("窗口二");
t3.setName("窗口三");
t2.start();
t3.start();
t1.start();
}
}
等待唤醒机制
生产者消费者
图片来源于黑马,啊伟老师yyds
案列:
公共
public class Desk {
//0没有事物
public static int flag = 0;
//总数
public static int count = 10;
//锁对象
public static Object lock = new Object();
}
Food生产
public class Food extends Thread {
@Override
public void run() {
while (true) {
synchronized (Desk.lock) {
if (Desk.count == 0){
break;
}
else {
if (Desk.flag == 0) {
try {
Desk.lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
Desk.count--;
//唤醒生产者
System.out.println("第"+Desk.count+"吃完了");
Desk.lock.notifyAll();
Desk.flag = 0;
}
}
}
}
}
}
消费
public class Cook extends Thread{
@Override
public void run() {
while (true){
synchronized (Desk.lock){
if(Desk.count == 0){
break;
}else {
//判断桌子有实物
if(Desk.flag == 1){
try {
Desk.lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}else {
//生产
System.out.println("面条做好了");
Desk.flag = 1;
Desk.lock.notifyAll();
}
}
}
}
}
}
阻塞队列模式
ArrayBlockingQueue
底层是数组,有界
LinkedBlockingQueue
底层是链表,无界不是真正的无界,最大为int最大值
案列
cook
public class cook extends Thread{
ArrayBlockingQueue<String> queue;
public cook(ArrayBlockingQueue<String> queue) {
this.queue = queue;
}
@Override
public void run() {
while (true){
try {
queue.put("面条");
System.out.println("生产了一碗面条");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
food
public class Food extends Thread{
ArrayBlockingQueue<String> queue;
public Food(ArrayBlockingQueue<String> queue) {
this.queue = queue;
}
@Override
public void run() {
while (true){
try {
queue.take();
System.out.println("吃了一碗面条");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
arduino
public class Test {
public static void main(String[] args) {
ArrayBlockingQueue<String> queue = new ArrayBlockingQueue<>(2);
cook cook = new cook(queue);
Food food = new Food(queue);
cook.start();
food.start();
}
java多线程的六种状态
Thread.State (Java Platform SE 8 )
没有运行态,交给操作系统了。
-
线程状态。 线程可以处于以下状态之一:
NEW
尚未启动的线程处于此状态。RUNNABLE
就绪态 start- 在Java虚拟机中执行的线程处于此状态。
BLOCKED
被阻塞等待监视器锁定的线程处于此状态。WAITING
正在等待另一个线程执行特定动作的线程处于此状态。TIMED_WAITING
计时等待sleep- 正在等待另一个线程执行动作达到指定等待时间的线程处于此状态。
TERMINATED
已退出的线程处于此状态。
抽红包案例
ini
public class MyThread extends Thread{
public static double money = 100;
public static int count = 3;
public static final double MIN = 0.01;
@Override
public void run() {
synchronized(MyThread.class){
// 判断共享数据是否到了末尾(到了)
if(count == 0){
System.out.println(getName() + "没有抢到红包");
}else {
//判断共享数据是否到了末尾(未到)
double prize = 0;
//第三次从 剩余全部
if(count == 1){
prize = money;
}else {
//第一、二次从 100随机
Random random = new Random();
//第一个红包最大 99.98
double max = money - (count - 1) * MIN;
//随机数 JDk17及以上
prize = random.nextDouble(max);
//最小 不能小于 0.01
if(prize<MIN){
prize = MIN;
}
}
money = money - prize;
count--;
System.out.println(getName() +"抢到了"+prize);
}
}
}
线程池
使用Executors工具类
两个方法
newCachedThreadPool 创建没有上限的线程池
newFixedThreadPool 指定上限
底层使用ThreadPoolExecutor创建线程池
原理:
新创建的线程池,池子中是空的。
提交任务时,池子会创建新的线程对象,任务执行完,线程回归到线程池。
自定义线程池
ThreadPoolExecutor 定义线程池的类
知识解读:
核心线程
临时线程
队伍长度
什么时候调用临时线程? 当核心线程全部在执行任务,且排队的任务数量已满,就会创建临时线程
如果,核心线程、临时线程、队伍长度都达到最大值,还有任务怎么办? 触发任务拒绝策略
java默认任务拒绝策略
丢弃任务并抛出RejectedExecutionException异常
#黑马截图
实现
ThreadPoolExecutor(int corePoolSize, 核心线程数
int maximumPoolSize, 最大线程数,不小于0,>=核心线程数
long keepAliveTime, 空闲线程最大存活时间 (临时线程)
TimeUnit unit, 时间单位
BlockingQueue workQueue, 任务队列
ThreadFactory threadFactory) 线程工厂
创建一个新的 ThreadPoolExecutor
与给定的初始参数和默认拒绝策略
执行处理程序。
最大并行数
电脑8核16线程 表示8个CPU
但是inter发明了超线程技术,能够将8个CPU虚拟成16个,就能并行处理16个任务。
最优线程池大小计算
CPU密集型运算 最大并行数 + 1
+1原因,作为候补线程,如果已有线程出现故障,顶替故障线程
I/O密集型运算 读取数据库较多
使用thread dump工具来分析CPU计算时间