20200802 第一组 于芮 多线程基础(第二十二天)
多线程
创建线程
1.继承thread类,并且重写run形式(thread不是抽象类,run也不是抽象方法)
创建的类继承了thread类后,就是一个独立的线程,要让线程启动,调用start方法
当调用start方法启动一个线程时,会执行重写的run方法
线程的优先级(概率问题,做不到百分百)
%90先执行主方法,10%先执行创建的类
2.实现runable接口(能用接口一定不用继承)
函数式接口:@funcinterface可以使用箭头函数
如果想要线程启动,必须要调用thread中的start方法
thread中有构造器,通过多态可以创建thread对象,调用start方法
也可以使用箭头函数(lambda表达式)
new thread()->system.out,prientln().start();
thread.sleep()线程休眠
3.实现callable接口
需要借用thread中的start方法,借用一个工具类futuretask(泛型类)
守护线程(为用户线程提供服务,仅在用户线程运行时才需要)
守护线程对于后台支持任务非常有用,大多数JVM线程都是守护线程
对象.stedaemon(true);守护线程
任何线程继承创建它的线程守护线程的状态,由于主线程是用户线程,因此在main方法内容启动任何线程默认都是守护线程
线程的生命周期(六个)
1.new(未被start()调用执行)
2.runable(线程正在被JVM中执行,等待来自操作系统的调用)
3.blocked(因为某些原因不能立即执行,需要挂起等待)
4.waiting(无限期等待,如果没有唤醒,就一直等待)
5.timed-waiting(有限期等待,线程等待一个指定的时间)
6.terminated(终止线程状态,线程已经执行完毕)
等待和阻塞
等待因为外部原因,需要等待
等待一般是主动调用方法,发起主动的等待,还可以传入参数指定等待时间
插队(join())
休眠(sleep())
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
尽量做到硬件和操作系统之间达到一致的访问效果。
可见性
thread线程一直在高速读取缓存中的isOver,不能感知主线程已经把isOVer改成了true
这就是线程的可见性的问题。
怎么解决?
volatile能够强制改变变量的读写直接在内存中操作。
线程争抢
解决线程争抢的问题最好的办法就是【加锁】
synchronized同步锁,线程同步
当一个方法加上了synchronized修饰,这个方法就叫做同步方法。
线程安全的实现方法
(1)数据不可变。
一切不可变的对象一定是线程安全的。
对象的方法的实现方法的调用者,不需要再进行任何的线程安全的保障措施。
比如final关键字修饰的基本数据类型,字符串。
只要一个不可变的对象被正确的创建出来,那外部的可见状态永远都不会改变。
(2)互斥同步。加锁。【悲观锁】
(3)非阻塞同步。【无锁编程】,自旋。我们会用cas来实现这种非阻塞同步。
(4)无同步方案。多个线程需要共享数据,但是这些数据又可以在单独的线程中计算,得出结果
我们可以把共享数据的可见范围限制在一个线程之内,这样就无需同步。把共享的数据拿过来,
我用我的,你用你的,从而保证线程安全。
今天的主要练习有很多,每一个联系都有独特的意义,完成一个就觉得对多线程的理解又多了一点,运用起来也更加灵活。
创建多线程对象,开启多线程。在子线程中输出1-100之间的偶数,主线程输出1-100之间的奇数。
1 package B0802; 2 3 public class Ch03 implements Runnable{ 4 5 @Override 6 public void run() { 7 String name=Thread.currentThread().getName(); 8 for (int i = 1; i <100 ; i++) { 9 if(i%2==1){ 10 System.out.println("in main"+name+i); 11 } 12 } 13 } 14 15 public static void main(String[] args) { 16 Runnable runnable=new Ch03(); 17 Thread t=new Thread(runnable); 18 t.setName("主线程"); 19 Runnable runnable1=new Runnable() { 20 @Override 21 public void run() { 22 String name=Thread.currentThread().getName(); 23 for (int i = 1; i <100 ; i++) { 24 if(i%2==0){ 25 System.out.println("in thread"+name+i); 26 } 27 } 28 } 29 }; 30 Thread thread=new Thread(runnable1); 31 thread.setName("次线程"); 32 t.start(); 33 thread.start(); 34 } 35 }
通过实现Runnable接口的方法创建一个新线程,要求main线程打印100次“main”,新线程打印50次“new”。
1 package B0802; 2 3 public class Ch04 implements Runnable{ 4 @Override 5 public void run() { 6 for (int i = 0; i < 100; i++) { 7 System.out.println("new"); 8 } 9 } 10 11 public static void main(String[] args) { 12 Ch04 ch04=new Ch04(); 13 Thread thread=new Thread(ch04); 14 thread.start(); 15 for (int i = 0; i < 50; i++) { 16 System.out.println("main"); 17 } 18 } 19 }
编写一个有两个线程的程序,第一个线程用来计算2~100000之间的素数的个数,第二个线程用来计算100000~200000之间的素数的个数,最后输出结果
1 package B0802; 2 3 public class Ch05 implements Runnable { 4 int i,j,x=0; 5 Ch05(int m,int n){ 6 this.i=m; 7 this.j=n; 8 } 9 @Override 10 public void run() { 11 int p,q; 12 p=0;q=0; 13 for (int m = i; m <=j ; m++) { 14 for (int h = 1; h <=m ; h++) { 15 q=m%h; 16 if(q==0) 17 p=p+1; 18 } 19 if(p==2){ 20 x=x+1; 21 } 22 p=0; 23 } 24 System.out.println("输出:"+i+"到"+j+"之间的质数个数:"+x); 25 } 26 27 public static void main(String[] args) { 28 Thread thread=new Thread(new Ch05(2,100000)); 29 Thread thread1=new Thread(new Ch05(100000,200000)); 30 thread.start(); 31 thread1.start(); 32 } 33 }
模拟三个老师同时发80份学习笔记本,每次只发放一份笔记本,每个老师相当于一个线程。
1 package B0802; 2 3 public class Ch06 implements Runnable{ 4 private int notes=80; 5 @Override 6 public void run() { 7 while (true){ 8 dispatchNotes(); 9 if(notes<=0){ 10 break; 11 } 12 } 13 } 14 private synchronized void dispatchNotes(){ 15 if(notes>0){ 16 try { 17 Thread.sleep(10); 18 }catch (InterruptedException e){ 19 e.printStackTrace(); 20 } 21 System.out.println(Thread.currentThread().getName()); 22 } 23 } 24 25 public static void main(String[] args) { 26 Ch06 ch06=new Ch06(); 27 new Thread(ch06,"王老师").start(); 28 new Thread(ch06,"程老师").start(); 29 new Thread(ch06,"李老师").start(); 30 31 } 32 }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效