多线程系列1-三大特性
线程基础概念:
进程是线程的容器,进程是操作系统分配的资源。
线程是由操作系统的线程调度器的,调度器基于以下因素动态分配CPU资源:
- 优先级:线程的优先级(如实时任务优先)。
- 时间片轮转:每个线程分到固定时间片,用完后切换(抢占式调度)。
- 公平性:避免某些线程长时间占用CPU(如Linux的CFS调度器)。
- I/O阻塞:若线程等待I/O,调度器会挂起它并执行其他就绪线程。
是cpu执行的基本单位。
线程可以共享同一进程中的资源,进程之间的资源是相互独立的。
进程之间的通信需要借助内核。
线程是cpu调度,调度,比如我现在需要查询数据库,我这边就可以先做其它事情,等数据库查询有结果了再一起整合数据,这样也能充分利用cpu资源。调度调度调度。
可以避免一些磁盘io和网络io的等待操作,让cpu去调度其它线程。
cpu需要存储线程的信息(线程上下文,为了保证线程切换过后依然能独立执行而存储的内容),如果线程的数量太多,那么cpu切换线程上下文时,需要造成额外的消耗。
并行、并发、串行
并行:一起上
串行:一个一个排队,前一个执行完后面一个才能执行
并发:并行囊括并发。并发看似并行,并发实际上是指cpu在做多个任务时,cpu高速来回切换,所以看似是在并行。
单核cpu无法实现并行效果。
并行就是cpu同时调用多个线程,是真正的多个线程同时执行。

线程的状态:

线程的优先级是1到10
join ()方法,线程强占。假如线程有两个线程,t1和main,在main线程中,调用t1.join(),或者ti.join(1000),意味着就是让t1执行完或者t1执行1秒过后,main才开始执行。主线程休眠和sleep相同效果。
yeild方法,线程短暂让出cpu,由running状态变为runnable状态。
线程synchronized锁:
在这个关键字里面,如果调用了wait()方法,就会释放锁资源,放到等待池中,其它线程就可以竞争到这个锁。
如果调用了notifyAll()方法,则会添加到锁池,来竞争锁资源。



philosophy 时间片分配:线程1执行一段时间时间,会在一段时间内执行任务,到时间过后就释放cpu,工作线程2就开始继续执行,同样是执行一段时间,会释放cpu。假如线程1线程2在这一段时间内没有完成任务,那么主存里的共享变量就可能更改,那么自己本身里的变量就不是最新的了。
volatile 关键字可以理解为:读是加锁的,写是释放锁。

wait() 和 notify() 必须在 synchronized 块或方法内使用,因为它们依赖于对象的监视器锁(monitor lock)。
wait() 会释放锁,而 notify() 不会释放锁(只是通知其他线程可以竞争锁)。
通常配合 while 循环检查条件,避免虚假唤醒(spurious wakeup)。
线程sleep状态下可以被打断,如果打断的话,
Thread.currentThread().interrupt();那么sleep就可以中断异常,在这个中断异常里结束程序,这也是结束程序的一种方式。
// 查看标志位并归位位位 System.out.println(Thread.interrupted());
juc并发工具:
异步编程:
并发编程三大特性:
原子性:


javap + .class文件 。 这个指令是解析编译好的class文件,用cpu指令的形式。eg:javap -v InterruptResearch.class
synchronized加完过后,会有monitorenter 和 monitorexit 操作。
在处理临界资源时,如何解决线程的原子性操作,即保证它,所以就引申出来一个东西就是锁,保证串行执行。
cas:直接使用的是cpu compare and swap 并发原语来直接操作主内存,当然也存在一些问题:
1、无法对多行代码实现原子性操作,需要有一个变量
2、ABA的问题(破坏了线程的原子性),可通过java.util.concurrent.atomic.AtomicStampedReference这个类去解决

3、自旋时间过长问题

1、 设置自旋次数,避免cpu空转,如果超过这个次数,那么就直接失败即可,或者将线程挂起。自旋锁,自适应自旋锁
2、compare失败过后,将操作暂存起来,等后面需要获取结果的时候再执行。
为什么会有Lock锁:
1、sync关键字性能在1.5时不敌Lock锁,在1.6过后,优化过后相差无几。但是在高并发情况下优先使用Lock锁。Lock即java.util.concurrent.locks.ReentrantLock
ReentrantLock是基于AQS,基于CAS维护state变量来实现锁的操作。
TheadLocal细节:
里面ThreadLocalMap的key是弱引用。强软弱虚。如果对象只有弱引用指向它,垃圾回收器会立即回收该对象(无论内存是否充足)。还有种情况就是假如系统内存不足,那么就会优先回收弱引用。
弱引用一般存临时数据,缓存。
可见性:
volatile关键字:

synchronized也可以解决线程可见性问题,因为拿到锁过后会清空cpu中涉及到内部变量的缓存,必须重新去主存中拿最新数据。

Lock锁也能实现内存可见性,因为里面的state加了volatile关键字,对类中的其它属性也会失效缓存使用主存中的内容。ReetrantLock。

final 关键字
final 不允许在运行期间修改的。
线程有序性:

两大点:满足一些要求才会重排,要提升效率 、在不影响最终结果的前提下
以分配对象为例:1、开辟空间 2、初始化 3、分配地址。这三步如果初始化放在最后,那么其它线程就有可能拿到一个空内容的对象,因为分配地址被提前了,这时还没初始化。
解决方式:
加volatile关键字解决,通过栅栏的方式禁止指令重排。


锁:
阻塞队列:
浙公网安备 33010602011771号