2022-8-2 第八组 曹雨 多线程
创建线程 3种方法:
在Java中,创建线程:
- 继承Thread类,重写run方法。
- Thread类,不是抽象类,Thread方法不是抽象方法
- 当myThread类继承了线程以后,他已经不是一个普通的类了,他是一个独立的线程,要让线程启动。调用线程start方法
1、继承Thread类,重写run方法
//重写run方法
class MyThread extends Thread{
@Override
public void run() {
System.out.println("重写的run方法");
}
}
//调用start方法
//我调用start,执行run,为啥不直接执行run?
//因为如果直接调run,那就是普通的对象调方法,与继承线程类没有任何关系
public static void main(String[] args) {
MyThread myThread = new MyThread();
myThread.start();
}
}
线程的优先级:
- 主方法的优先级高于线程
- 线程的优先级是一个概率问题
2、实现RUNNABLE接口:
我们通过看Thread类的源码可知,Thread类实现了Runnable接口。
我们是否可以通过让我们的类直接实现Runnable接口来实现调用start方法,来使得线程启动
//让类实现Runnable接口
class Mythread2 implements Runnable{
@Override
public void run() {
}
}
public class Ch02 {
public static void main(String[] args) {
//start
Mythread2 mythread2 = new Mythread2();
//试图调用Thread类的start方法
//实现Runnable接口时,却找不到start方法。
Thread t =new Thread (mythread2);
t.start();
}
}
使用箭头函数:
public static void main(String[] args) {
System.out.println(1);
//箭头函数接口,抽象类,重写方法
Thread t = new Thread(
() -> System.out.println(2)
);
t.start();
//线程的休眠
try {
Thread.sleep(1);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(3);
System.out.println(4);
}
3、Callable接口:
class MyThread3 implements Callable<String>{
@Override
public String call() throws Exception {
System.out.println(2);
return "call方法的返回值";
}
}
public class Ch04 {
public static void main(String[] args) {
System.out.println(1);
//callable-->FutureTask-->RunnableFuture-->Runnable-->Thread
FutureTask<String> futureTask = new FutureTask<>(new MyThread3());
new Thread(futureTask).start();
System.out.println(3);
System.out.println(4);
}
}
守护线程:
- 为用户线程提供服务,仅在用户线程运行时才需要
- 守护线程对后台支持的应用非常有用
- 创建守护线程:在主方法里启动是守护线程
线程的生命周期:
- NEW:这个状态主要是线程未被start()调用执行
- RUNNABLE:线程正在JVM中被执行,等待来自操作系统的调度
- BLOCKED:阻塞。因为某些原因不能立即执行需要挂起等待。
- WAITING:无限期等待。Object类。如果没有唤醒,则一直等待。
- TIMED_WAITING:有限期等待,线程等待一个指定的时间
- TERMINATED:终止线程的状态,线程已经执行完毕。
等待和阻塞两个概念有点像,阻塞因为外部原因,需要等待, 而等待一般是主动调用方法,发起主动的等待。等待还可以传入参数确定等待时间。
CPU的多核缓存结构:
CPU有物理内存,硬盘内存。
CPU的三级缓存:
每个CPU都有L1,L2,L3(多核公用)缓存。
在查找数据时:
CPU->L1->L2->L3->内存->硬盘
缓存行:
进一步优化,CPU每次读取一个数据,读取的时与它相邻的64个字节的数据。
英特尔MEST协议:
1、修改态,此缓存被动过,内容与主内存中不同,为此缓存专有
2、专有态,此缓存与主内存一致,但是其他CPU中没有
3、共享态,此缓存与主内存一致,其他的缓存也有
4、无效态,此缓存无效,需要从主内存中重新读取
指令重排:
没有依赖关系的指令,被编译器重新调整顺序
线程安全:
定义:当多个线程同时访问一个对象时,如果不用考虑这些线程在运行时环境下的调度和交替执行,也不需要进行额外的同步,或者在调用方进行任何其他的协调操作,调用这个对象的行为都可以获得正确的结果,那就称这个对象时线程安全的。
解决线程争抢的方法:
(1)数据不可变。
一切不可变的对象一定是线程安全的。
对象的方法的实现方法的调用者,不需要再进行任何的线程安全的保障措施。
比如final关键字修饰的基本数据类型,字符串。
只要一个不可变的对象被正确的创建出来,那外部的可见状态永远都不会改变。
(2)互斥同步。加锁。【悲观锁】
//“悲观”的认为,你一定会出“问题”。
public class Ch04 {
private volatile static int count = 0;
public synchronized static void add() {
count ++;
}
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
for (int i = 0; i < 10000; i++) {
add();
}
});
Thread t2 = new Thread(() -> {
for (int i = 0; i < 10000; i++) {
add();
}
});
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println("最后的结果是:" + count);
}
}
(3)非阻塞同步。【无锁编程】,自旋。我们会用cas来实现这种非阻塞同步。
(4)无同步方案。多个线程需要共享数据,但是这些数据又可以在单独的线程中计算,得出结果
我们可以把共享数据的可见范围限制在一个线程之内,这样就无需同步。把共享的数据拿过来,
我用我的,你用你的,从而保证线程安全。使用ThreadLocal关键字
public class Ch05 {
private final static ThreadLocal<Integer> number = new ThreadLocal<>();
private static int count = 0;
public static void main(String[] args) {
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
// t1内部自己用一个count
number.set(count);
for (int i = 0; i < 10; i++) {
number.set(count ++);
System.out.println("t1-----" + number.get());
}
}
});
Thread t2= new Thread(new Runnable() {
@Override
public void run() {
// t2内部自己用一个count
number.set(count);
for (int i = 0; i < 10; i++) {
number.set(count ++);
System.out.println("t2-----" + number.get());
}
}
});
t1.start();
t2.start();
}
}
总结
今天开始了多线程的学习,感觉概念确实比较多。听了一天感觉有些乱。晚自习的时候还需要去理解理解思想,并去掌握一下一些关键字的应用。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效