聊Java多线程之前,你得知道计算机底层怎么运行的包括计算机组成原理、网络、操作系统,然后才能学习应用层的多线程
OS
操作系统是一个软件负责管理硬件中有限的资源,操作系统对进程、设备、内存、文件等利用算法进行合理的管理,从而极大的提升计算机的工作效率。
操作系统其实分内核层(kernal)和应用层(用户进程),内核层负责对硬件中的资源进行管理,应用层管理用户的应用程序,那么问题来了用户的应用程序是怎么分配到硬件资源的,原来是内核层为应用层提供系统调用接口,当需要系统资源的时候通过对外暴露的接口将指令送到内核层去执行,例如查看文件、创建进程等操作,这样我们就知道了应用层语言是怎么创建进程线程的。此外,shell命令窗口就是linux操作系统对外暴露的接口,可直接将命令输送到内核层执行
进程、线程、程序
程序:一段命令(代码)集合、是静态的
进程:操作系统分配资源和调度的最小单位,为其分配一块内存空间供其使用,是动态的,进程之间数据是隔离的
线程:程序运行的最小单位,寄生在进程中,线程是进程中一个负责程序执行的控制单元,线程之间数据是共享的(寄生在同一进程里的)
有了以上概念之后,我们可以关系到java中,JVM本身就是一个进程,我们在java程序中创建的线程底层都是调用操作系统内核的创建线程的方法创建线程实例,Java有一个默认主线程作为入口函数,负责完成一些准备工作例如类加载,还有一个垃圾回收线程。
线程生命周期
线程最终会被销毁,那么什么时候才会执行完?当执行完run()方法或者stop()的时候线程就会被销毁。
那么问题来了,线程池技术维护了大量的线程,它又是怎么保证线程不被销毁而是被复用的呢后续再谈线程池?
什么是多线程
多线程指的是JVM开启多个线程,线程之间穿插的进行,操作系统分配时间片,时间片到切换到别的线程执行,该线程被挂起,返回就绪队列,等待下一次调度,这样的过程就叫做并发执行。大白话说就是多个任务同时运行,同时也要区分并行并行是同一时刻共同进行,并发只是宏观上看共同发生一样但微观上在单个CPU上交替执行
构建Thread实例
继承Thread类本身
实现Runnable接口
使用callable和future创建线程
使用线程池
Thread和Runnable区别
- 继承Thread类 可扩展性差 因为java是单继承语言
- 实现Raunnable接口 可扩展性强 还可以继承其他类
Thread常用方法
- start() 启动线程
- setName() 设置线程实例名称
- getName() 获取线程实例名称
- currentThread() 静态方法 获取当前类的实例(instance)
- sleep() 静态方法 使当前线程休眠 挂起/阻塞该线程
- getPriority() 获取当前线程实例的优先级 默认是5
- setPriority() final 设置当前线程实例的优先级 1~10 越高 先执行的概率越高
- setDaemon(Boolean b) final 设置为守护线程 当其他非线程执行完毕后 守护线程陆续结束
- yield() 静态方法 出让当前CPU执行权
- join() final
什么是线程不安全
线程共享区 堆区、方法区(静态区),当多个线程同一段时间内修改共享数据将会导致最终的数据可能不正确,这就是线程不安全,所以我们需要对共享资源进行控制,这就引进了锁机制来控制共享资源的使用
锁策略
悲观锁,认为什么时候也会出现问题,每次都会去保证该代码块是同步进行的
乐观锁,认为不会出现问题,每次修改会和之前的进行比较,如果相同就执行,但是乐观锁会有ABA问题,可以通过版本号或者时间戳进行控制
阻塞式、非阻塞式、等待唤醒机制
阻塞式:一直在等待获取锁
非阻塞式:不断查询获取锁,中间可以穿插着干其他的事情
生产者生产 消费者消费 利用信号量控制生产者/消费者的状态 wait() notify()
Synchronized
锁的粒度可以控制在函数或者锁对象
Synchronized关键字代表当前方法或者代码块是同步执行的,在一个JVM中同时只能有一个线程去执行这个方法或者代码块,从而防止并发产生的问题,但是Synchronized锁是性能最差的,特别消耗CPU性能,这是因为它属于悲观锁机制,会假设每次都发生并发问题,这时候会阻塞其他线程的执行,因此在实际操作的时候,可以设置更低粒度的锁,sychronized会有一个锁升级的过程
Lock
灵活获取锁和释放锁
Lock是接口不能直接实例化 这里采用ReentrantLock来实例化
AQS 双向队列
阻塞队列是什么
blocking queue
硬件是怎么加锁的
- 关中断
- 缓存锁
- 锁总线
死锁、临界区、竟态条件
死锁指的是两个或两个以上的线程执行过程中,由于资源竞争导致线程堵塞状态的现象,都在等待对方释放资源,产生死锁线程
临界区:共享资源代码块
竟态条件:由于执行顺序不同对最终的结果有影响