java 多线程基础 线程共享/协作 笔记

进程和线程

  1. 进程:操作系统进行资源分配的最小单位,进程与进程之间相互独立
  2. 线程: CPU调度的最小单位,必须依赖于进程存在,一个进程至少创建一个线程,真正执行任务的是线程
  3. CPU核心数与线程数的关系:1:1的关系,即表示一个核心同一时间只能对应一个线程
  4. CPU时间片轮转(RR调度)机制:支持多线程的基础,可以让CPU轮询处理N多个线程
    1). 时间片一般大小 50ms,是一个折中的方案, 太大浪费CPU时间, 太小会频繁切换线程(上下文切换,要耗费约20000个CPU时钟周期,太浪费时间)

CPU如何调度?并行和并发

  1. 并行数:可以同时(瞬间)执行的不同任务数…(CPU逻辑处理器数,并行度)
  2. 并发数:不可脱离时间单位,即单位时间内可处理的任务数 -->(1s内共处理了N次XXXX任务).

多线程

==============优点

  1. 模块化
  2. 简单化
  3. 异步化

==============注意事项
4. 线程安全控制–>资源争夺–>锁–>性能下降/死锁
5. 操作系统线程数有限制–>(Linux 单进程1000个,window 单进程2000个)–>java 每个线程(虚拟机栈)空间默认1M -->线程数太多占用大量资源必须限制

=================
线程启动 两种方式,具体请参考Thread类源代码注释,明确写为2种
底层:调用linux系统的pthread_create方法


# 1 Thread类是java语言对线程的抽象,用子类继承之实现多线程
new Thread().start(); -->如果同一个对象start()执行多次会抛异常

# 2 子类实现Runnable接口,对业务逻辑抽象,放入Threaed类中执行
new Thread(new Runnable).start();

线程停止

suspend();  -->线程挂起, 被挂起后,不会释放锁,不会释放资源,不推荐使用

stop();     -->强制杀死线程,也不会释放资源,可能会导致正在处理的数据出错(例如文件读写中),不推荐使用

interrupt();-->对线程进行中断,只是对线程打个标记(线程中断标志位),并不强制杀死线程,由处理中的任务来判断是否需要中断, 
推荐使用,比较优雅 -->表示java线程是协作式的,不是抢占式(stop())

isInterrupted();-->判断线程是否被中断
Thread.currentThread().isInterrupted();-->判断线程是否被中断

static interrupted();-->静态方法,判断线程是否被中断,但是调用一次之后,线程中断标志位会被改为false,请参考源代码

--手动加个boolean变量判断停止的方法不推荐,sleep(),wait(),take()等线程方法被用到的场景会有问题

--当一些阻塞方法(sleep(),wait())抛出InterruptedException异常时,也会将中断标记置为false

--如果仍然需要中断线程,请在此exception的catch子句中再次调用interrupt()方法

--为什么要这样设计?如果抛异常时不置为false, 则程序员无法处理一些资源释放问题

--死锁状态的线程,不会响应中断状态

查看当前进程的所有线程

# 虚拟机现成系统管理接口
ThreadMXBean threadBean = ManagementFactory.getThreadMXBean();
# 仅获取线程和线程堆栈信息
ThreadInfo[] threadInfos = threadBean.dumpAllThreads(false,false);
# 遍历线程信息,打印线程id和名称
for(ThreadInfo info : threadInfos){
   System.out.println("["+info.getThreadId()+"]"+info.getThreadName());
}

线程的生命周期
在这里插入图片描述

yield(); -->线程从CPU执行权中移出来,不释放锁资源,线程进入就绪状态,等待cpu重新分配时间片

join();  -->线程A调用线程B的join()方法, 则线程A被挂起,等待线程B执行结束后被唤醒,可以保证线程按顺序执行--串行

线程的优先级/守护线程

setPriority(1--10); -->设置线程优先级,110,数字越大,CPU分配的时间片可能越多,由操作系统来决定,不一定生效

setDeamon(true);    -->false:用户线程(默认)  true:守护线程  
--守护线程在所有用户线程死亡后死亡,   守护线程中的finally块不一定执行(由cpu分配时间片多少决定)

线程间共享
1.synchronized(内置锁,对象锁):在每个对象头中,有个标记位为0,如果此对象被获取锁,此标记为变为1

  1. 方法锁(同步方法) 注意:同一个对象内的同步方法会互相影响(因为用的是当前类this的对象锁,相同的锁就会导致类内同步方法互相影响)
public void synchronized doSt(){
    //xxxxx
}
  1. 代码块锁(同步块): 指定一个对象作为锁对象
public void doSt(){
   synchronized(obj){
   //xxxxxxxx
   }
}
  1. 类锁:静态方法上加synchronized 锁, 每一个类在加载时都会生成Class对象,在静态方法上的锁实际上是对这个Class对象的锁
public static void synchronized doSt(){
  //xxxxxxxx
}

错误的加锁分析
加锁的对象引用发生改变,导致锁不住 例如(i为锁对象,i++之后引用发生改变,具体验证请反编译i++之后查看)

2.volatile关键字,最轻量的同步机制

  1. 保证变量可见性… 即线程A引用其他线程变量时, 其他线程的变量发生改变,线程A可以及时获取到最新变量值

  2. 一写多读的场景比较适用此种变量

3.对 强/软/弱/虚 引用的区分说明

  1. 强引用: 通常 Object o = new Object(); 这种就是强引用, 只要虚拟机栈中有指向堆中该实例的引用时, GC就不会回收该对象实例

  2. 软引用:Object o = new Object() extends SoftReference , 对象继承了软引用类,因堆空间不足发生GC时,会回收此类对象实例,直到堆空间足够,就不会回收了, 因此该类型的实例在GC时不一定被回收

  3. 弱引用:Object o = new Object() extends WeakReference,对象继承了弱引用类,当任何时候发生GC时,都会回收此类对象实例

  4. 虚引用:Object o = new Object() extends PhantomReference,当试图通过虚引用的get()方法取得强引用时,总是会返回null,并且,虚引用必须和引用队列一起使用… 一般只是用来跟踪垃圾回收过程,判断对象什么时候被回收,

4.ThreadLocal为每个线程提供一个变量副本

  1. 线程隔离,保证线程安全
  2. 每个Thread里都有个ThreadLocalMap类型属性, 用于保存该线程下所有的ThreadLocal变量,初始大小16
  3. 引发内存泄露的问题分析
    –1.ThreadLocalMap中的Entry是继承的WeakReference(弱引用),其内部的key被传入父类, 所以会导致key经常被回收而get的时候通过key找不到Entry的value.只有当整个线程完事之后,GC才会回收这部分找不到key引用的value
    –2.只有当TreadLocal变量不用的时候,要及时调用remove()方法及时释放资源,但是此操作也防止了KEY对应的ThreadLocal不发生内存泄露
  4. 线程不安全的情况
    –1. 包装一个static变量的ThreadLocal时,会发生变量共享,修改时会操作同一个对象
    –2. initialValue()方法中处理引用类型时,深复制浅复制的处理

线程间的协作

wait();  -->会释放锁,当条件不满足时,  对象.wait();  写在sync(对象)块中
synchronized(o){
	o.wait();  //此时线程等待, 同时释放掉o对象的锁
}
notify();   -->当条件满足时,只唤醒一个对该对象的等待线程,   对象.notify();  写在sync(对象)块或sync方法中
notifyAll();-->当条件满足时,唤醒所有对该对象的等待线程,   对象.notifyAll();  写在sync(对象)块或sync中
synchronized(o){
    做点事情
    xxxxxxx;
    //一般处理完成后,在同步块的最后调用通知
	o.notify();  //不会释放o对象锁
	//或者
	o.notifyAll();//不会释放o对象锁
}
//同步块执行完毕,释放o对象锁

线程方法对锁的影响

yield();   -->不释放锁
sleep();   -->不释放锁
wait();    -->释放锁
notify();/notifyAll();  -->不释放锁
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值