dijiuzu

 

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 }
复制代码

 

 

posted on   于芮  阅读(21)  评论(0编辑  收藏  举报

相关博文:
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效

导航

统计

点击右上角即可分享
微信分享提示