标签:缓存 Thread 甘源册 笔记 start 线程 2022 方法 CPU
目录知识点掌握情况
线程创建(理解) 守护线程(理解)线程生命周期(理解) 线程安全(理解)
学习心情
迷茫 ,很迷茫,感觉什么都不是很清晰,线程的安全实现方法也不是很懂。
1.多线程
1.1Java中创建线程的方式
-
继承Thread类,并且重写run方法——Thread类中的run方法不是抽象方法,Thread类也不是抽象类
-
MyThread myThread = new MyThread(); // start方法线程启动 当调用start方法启动一个线程时,会去执行重写的run方法的代码 myThread.start();
-
-
实现Runnable接口
-
// 传统方法 MuThread1 muThread1 = new MuThread1(); // 如果想让线程启动必须调用Thread类中的start方法 // Thread类的构造器可以传Runnable的参数 Thread thread = new Thread(muThread1); thread.start(); // 新方法lambda表达式 // 箭头函数:接口,抽象类,抽象方法 new Thread(()-> {System.out.println(2); try { // 线程休眠1000毫秒 Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } }).start();
-
-
实现Callable接口
-
// Callable接口需要借助FutureTask工具类来启动线程 FutureTask<String> futureTask = new FutureTask<>(new MuThread2()); new Thread(futureTask).start(); try { // 在线程调用后通过FutureTask工具类的get方法拿到实现Callable类的返回值 System.out.println(futureTask.get()); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); }
-
-
线程的启动
-
调用线程的start方法
-
// start方法线程启动 当调用start方法启动一个线程时,会去执行重写的run方法的代码 myThread.start(); // 直接调用run方法,不是线程,是对象调方法 myThread.run();
-
-
1.2线程的优先级
- 正常情况下主方法的优先级高于一般线程的优先级
- 线程的优先级是概率问题,做不到100%
1.3守护线程
-
Java中提供的两种类型的线程:
- 用户线程
- 守护程序线程
-
守护线程:为用户线程提供服务,仅在用户线程运行时才需要
-
守护线程对后台支持的任务非常有用。——大多数JVM线程都是守护线程——垃圾回收。
-
任何线程继承创建它的守护进程状态,由于主线程是用户线程——因此在main方法里内启动的任何线程都是默认的守护线程。
1.4线程的生命周期
-
NEW
- 这个状态主要是线程未被start方法调用执行
-
RUNNABLE
- 线程正在JVM中被执行,等待来自操作系统的调度。
-
BLOCKED
- 阻塞:因为某些原因不能立即执行需要挂起等待。
-
WAITING:
- 无限期等待。Object类。什么时候唤醒,什么时候调用,否在一直等待。
-
TIMED_WAITING:
- 有限期等待,线程等待一个指定的时间
-
TERMINATED:
- 终止线程的状态,线程已经执行完毕
-
等待和阻塞的区别
- 阻塞是外部原因,需要等待
- 等待是主动等待,主动调用一个方法来发起主动的等待。等待可以传入参数来确定等待时间
1.5线程常用方法
-
sleep方法
-
线程休眠 以毫秒为单位
-
sleep方法是静态方法,需要抛出编译器异常
-
try { Thread.sleep(12); } catch (InterruptedException e) { e.printStackTrace(); }
-
-
join方法
-
线程插队
-
join需要抛出编译器异常——不一定成功有概率问题
-
join方法的本意是阻塞用户线程
-
try { thread2.join(); } catch (InterruptedException e) { e.printStackTrace(); }
-
1.6CPU
-
CPU缓存为了提高程序运行的性能,CPU处理速度最快,内存次之,硬盘最慢
-
CPU处理内存数据时,如果内存的运行速度太慢,就会拖累CPU的速度,所以CPU设计了多级缓存策略。
-
CPU分为三级缓存:每个CPU都有L1,L2缓存,但是L3缓存是多核公用的
-
CPU查找数据时,CPU->l1->l2->l3->内存->硬盘
-
CPU每次读取一个数据,读取的是与它相邻的64个字节的数据。【缓存行】。
-
英特尔提出的MESI协议
- 修改态,此内存被动过,内存与主内存中不同,为此缓存专有
- 专有态,此缓存与主内存一致,但其他CPU中没有
- 共享态,此缓存与主内存一致,其他的缓存也有
- 无效态,此缓存无效,需要从主内存中重新读取
-
指令重排
-
让先执行完的指令进入缓存,都执行完后进行排序。
-
Java内存模型——JMM
-
尽量做到硬件和操作系统之间达到一致的访问效果。
-
Integer a=1; // 1 Integer b=1; // 2 Integer c=a+b; // 3 /* * 指令3不能排在指令1,2之前,因为它依靠指令1,2 * 指令2可以排在指令1之前,他们之间没有关系 * */
-
-
1.7线程安全
-
内存屏障
- volatile关键字——使用volatile关键字来保证一个变量在一次读写操作时,避免指令重排。
- 我们在读写操作之前加入一条指令,当CPU碰到这条指令后必须等到前面的执行执行完成才能继续执行下。
-
可见性
-
一个线程对共享变量的修改,另外一个线程能够立刻看到
-
线程一直在高速读取缓存中的isOver,不知道主线程的isOver已经改变。
-
Thread thread = new Thread(new Runnable() { @Override public void run() { while (!isOver){ } System.out.println(number); } }); thread.start(); Thread.sleep(1000); isOver=true; number=99; } private static boolean isOver=false; private static int number=0;
-
volatile关键字能强制改变变量的读写直接在内存中操作
-
-
线程争抢
-
解决线程争抢的问题最好的方法就是【加锁】
-
synchronized关键字
-
给方法加锁
-
private synchronized static void add() { }
-
-
用代码加锁
-
synchronized (){ }
-
-
-
1.8 线程安全的实现方法
- 数据不可变:
- 一切不可变的对象一定是线程安全的
- 对象的方法的实现方法的调用者,不需要再进行任何的线程安全的保障。
- 只要一个不可变的对象被正确的创建出来,那外部的可见的状态永远都不会改变——【final,字符串】
- 互斥同步(加锁)
- 【悲观锁——悲观锁认为自己在使用数据的时候一定有别的线程来修改数据,因此在获取数据的时候会先加锁,确保数据不会被别的线程修改。】
- 非阻塞同步(无锁编程)自旋。
- 我们会用cas来实现这种非阻塞同步
- 无同步方案
- 多个线程需要共享数据,但是这些数据又可以在单独的线程中计算,得出结果。我们可以把共享数据的可见范围限制在一个线程之内,这样就无需同步了。
- 把共享的数据拿过来,我用我的,你用你的,从而保证线程安全。ThreadLocal类
标签:缓存,Thread,甘源册,笔记,start,线程,2022,方法,CPU 来源: https://www.cnblogs.com/gycddd/p/16543104.html
本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享; 2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关; 3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关; 4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除; 5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。