当前位置:网站首页>2022-08-02 第六小组 瞒春 学习笔记
2022-08-02 第六小组 瞒春 学习笔记
2022-08-02 21:11:00 【烫嘴的辛拉面】
学习目标:
- 一周掌握 多线程
学习内容:
- 创建线程的三种方式
- 守护线程
- 生命周期
多线程:
在计算机体系结构中,多线程是由操作系统支持的中央处理器(或多核处理器中的单核)同时具有执行多个线程能力的方法。这种方法不同于多处理。在多线程应用程序中,线程共享单个或多个内核的资源,包括计算单元、中央处理器缓存和转换查找缓冲器(TLB)。
当多处理系统在一个或多个内核中包括多个完整的处理单元时,多线程旨在通过使用线程级并行和指令级并行来提高单个内核的利用率。由于这两种技术是互补的,它们有时在具有多个多线程CUP和具有多个多线程内核的CPU系统中结合使用。
实现线程一:继承Thread类
该如何创建线程呢?通过API中搜索,查到Thread类。通过阅读Thread类中的描述。Thread是程序中的执行线程。Java 虚拟机允许应用程序并发地运行多个执行线程。
创建线程的步骤:
1.定义一个类继承Thread。
2.重写run方法。
3.创建子类对象,就是创建线程对象。
4.调用start方法,开启线程并让线程执行,同时还会告诉jvm去调用run方法
实现线程二:实现Runnable
创建线程的另一种方法是声明实现 Runnable 接口的类。该类然后实现 run 方法。然后创建Runnable的子类对象,传入到某个线程的构造方法中,开启线程。
为何要实现Runnable接口,Runable是啥玩意呢?继续API搜索。
查看Runnable接口说明文档:Runnable接口用来指定每个线程要执行的任务。包含了一个 run 的无参数抽象方法,需要由接口实现类重写该方法。
创建线程的步骤。
1、定义类实现Runnable接口。
2、覆盖接口中的run方法。。
3、创建Thread类的对象
4、将Runnable接口的子类对象作为参数传递给Thread类的构造函数。
5、调用Thread类的start方法开启线程。
实现线程二:实现Callable接口
守护线程 Java中提供两种类型的线程: 1.用户线程 2.守护程序线程 守护线程为用户线程提供服务,仅在用户线程运行时才需要。 守护线程对于后台支持任务非常有用。 垃圾回收。大多数JVM线程都是守护线程。 QQ,主程序就是用户线程。 创建守护线程 任何线程继承创建它的线程守护进程状态。由于主线程是用户线程, 因此在main方法内启动的任何线程默认都是守护线程。
经典的车票案例:
package demo2;
public class Ch4 extends Thread {
@Override
public void run() {
super.run();
System.out.println(1);
}
public static void main(String[] args) {
Ch4 ch4=new Ch4();
//设置ch4为守护线程
ch4.setDaemon(true);
ch4.start();
}
}
线程的生命周期:
从摇篮到坟墓
NEW :线程未被start调用执行(新建了线程)
RUNNABLE :线程正在jvm中被执行,等待来自操作系统的调度(可运行,准备就绪)
BLOCKED:阻塞,因为某些原因,不能立即执行,需要挂起等待(有线程正在运行)
WAITING:无限期等待,Object类,如果没唤醒就无限等待
TIMED_WAITING :线程等待指定的时间,有限期等待
TERMINATED:终止线程的状态,线程已经执行完毕.
等待和阻塞:阻塞因为外部原因,等待一般是主动调用方法,发起主动的等待,等待还可以传入参数确定等待时间,
* 1.CPU多核缓存结构
* 物理内存:硬盘内存。(固态硬盘,尽量不要选择混合硬盘)
* CPU缓存为了提高程序运行的性能,现在CPU在很多方面对程序进行优化。
* CPU处理速度最快,内存次之,硬盘速度最低。
* 在CPU处理内存数据时,如果内存运行速度太慢,就会拖累CPU的速度
* 为了解决这样的问题,CPU设计了多级缓存策略。
* CPU分为三级缓存:每个CPU都有L1,L2缓存,但是L3缓存是多核公用的。
* CPU查找数据时,CPU->l1->l2->l3->内存->硬盘
* 从CPU到内存,60-80纳秒(一纳秒等于十亿分之一秒)
* 从CPU到L3,15纳秒
* 从CPU到L2,3纳秒
* 从CPU到L1,1纳秒
* 寄存器,0.3纳秒
* 进一步优化,CPU每次读取一个数据,读取的是与它相邻的64个字节的数据。
* 【缓存行】。
* 英特尔提出了一个协议MESI协议
* 1、修改态,此缓存被动过,内容与主内存中不同,为此缓存专有
* 2、专有态,此缓存与主内存一致,但是其他CPU中没有
* 3、共享态,此缓存与主内存一致,其他的缓存也有
* 4、无效态,此缓存无效,需要从主内存中重新读取
* 【指令重排】
* 四条指令,四个人在四张纸上写下【恭喜发财】。
*
* java内存模型-JMM
* 尽量做到硬件和操作系统之间达到一致的访问效果。
public class Ch02 {
private static int x = 0,y = 0;
private static int a = 0,b = 0;
private static int count = 0;
private volatile static int NUM = 1;
public static void main(String[] args) throws InterruptedException {
long start = System.currentTimeMillis();
for (;;) {
Thread t1 = new Thread(() -> {
a = 1;
x = b;
});
Thread t2 = new Thread(() -> {
b = 1;
y = a;
});
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println("一共执行了:" + count++ + "次");
if(x == 0 && y ==0){
long end = System.currentTimeMillis();
System.out.println("耗时:" +(end - start) + "毫秒,(" + x + "," + y + ")");
break;
}
a = 0;b = 0;x = 0;y = 0;
}
}
我们发现测试结果中大部分感觉是正确的,(0,1)或(1,0),一个是线程1先执行,一个是线程2先执行。
按道理来说,绝对不会出现(0,0),如果出现(0,0)代表存在指令重排,乱序执行。
使用volatile关键字来保证一个变量在一次读写操作时,避免指令重排。
我们在读写操作之前加入一条指令,当CPU碰到这条指令后必须等到前面的执行执行完成才能继续执行下一条指令。
【内存屏障】
现在大多数现代计算机为了提高性能而采取乱序执行,这可能会导致程序运行不符合我们预期,内存屏障就是一类同步屏障指令,是CPU或者编译器在对内存随机访问的操作中的一个同步点,只有在此点之前的所有读写操作都执行后才可以执行此点之后的操作。
public class Ch03 {
private volatile static boolean isOver = false;
private static int number = 0;
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
while(!isOver){
}
System.out.println(number);
}
});
thread.start();
Thread.sleep(1000);
number = 50;
// 已经改了,应该能退出循环了
isOver = true;
}
}
* 可见性
* thread线程一直在高速读取缓存中的isOver,不能感知主线程已经把isOVer改成了true
* 这就是线程的可见性的问题。
* 怎么解决?
* volatile能够强制改变变量的读写直接在内存中操作。
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);
}
}
* 线程争抢
* 解决线程争抢的问题最好的办法就是【加锁】
* synchronized同步锁,线程同步
*
* 当一个方法加上了synchronized修饰,这个方法就叫做同步方法。
* 线程安全的实现方法
* (1)数据不可变。
* 一切不可变的对象一定是线程安全的。
* 对象的方法的实现方法的调用者,不需要再进行任何的线程安全的保障措施。
* 比如final关键字修饰的基本数据类型,字符串。
* 只要一个不可变的对象被正确的创建出来,那外部的可见状态永远都不会改变。
* (2)互斥同步。加锁。【悲观锁】
* (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();
}
}
车站买票例子:
package com.jsoft.afternoon;
public class Ticket implements Runnable{
private static final Object lock = new Object();
private static Integer count = 100;
String name;
public Ticket(String name) {
this.name = name;
}
@Override
public void run() {
while(Ticket.count > 0){
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (Ticket.lock){
System.out.println(name + "出票一张,还剩:" + Ticket.count-- + "张!");
}
}
}
public static void main(String[] args) {
Thread one = new Thread(new Ticket("一号窗口"));
Thread two = new Thread(new Ticket("二号窗口"));
one.start();
two.start();
}
}
学习时间:
- 上午:7:30-12:00
- 下午:1:30-5:00
- 晚上:6:00-11:00
学习产出:
- 初步认识创建线程的三种方式
- 熟悉守护线程
- 了解生命周期
边栏推荐
猜你喜欢
PyRosetta 安装方法之Conda安装
.NET performance optimization - you should set initial size for collection types
win10安全中心设置不扫描某个文件夹的方法
面试了个985毕业的,回答“性能调优”题时表情令我毕生难忘
增删改查这么多年,最后栽在MySQL的架构设计上!
I interviewed a 985 graduate, and I will never forget the expression when answering the "performance tuning" question
手把手教你干掉if else
Vscode快速入门、 插件安装、插件位置、修改vscode默认引用插件的路径、在命令行总配置code、快捷键
汉源高科2光12电千兆导轨式网管型工业以太网交换机双光自愈保护式以太网光交换机
Mysql用户管理
随机推荐
go——内存分配机制
C#异步和多线程
解道7-编程技术4
js how to get the browser zoom ratio
Redis是如何轻松实现系统秒杀的?
PLC working principle animation
正则表达式
C primer plus学习笔记 —— 9、联合&枚举&typdef
手把手教你干掉if else
Ansible installation and configuration
字节内部技术图谱 惊艳级实用
[C题目]力扣142. 环形链表 II
用户之声 | 大学生的“课外学堂”
【STM32学习3】DMA基础操作
Vscode快速入门、 插件安装、插件位置、修改vscode默认引用插件的路径、在命令行总配置code、快捷键
无线振弦采集仪远程修改参数的方式
2022年金九银十,Android面试中高频必问的问题汇总
用户之声 | 我与GBase的缘分
IP协议(网际协议)
golang刷leetcode:道路的最大总重要性