1 多线程
1.1 程序,进程,线程
程序:一组命令的集合,为了完成指定的功能,程序是静态概念,一般保存在硬盘中
进程:正在运行的程序,是一个动态概念,需要保存到内存中,操作系统会分配对应的UID
当我们直接关闭某个进程的时候,该进程会在运行内存中被销毁
线程:一个程序中,不同的执行分支,如果同一个时间节点允许多个线程同时执行的时候,我们称为支持多线程。 在java中,main方法开始执行,就是一个线程,称为主线程
1.2 并行和并发
并行:多个CPU,同时执行多个任务
并发:一个CPU,同时执行多个任务
多线程并行 必须CPU要大于等于2才行
单核CPU是没有多线程的
1.3 单核CPU和多核CPU
1.4 多线程优缺点和应用场景
1.5 线程创建
1.5.1 Thread
第一种,创建一个类,继承Thread类,并覆写run方法
class Processer extends Thread
run方法就等于是新线程中的main方法
创建线程类对象
Tread t1=new Processer();
调用start方法,启动线程
t1.start();
1.5.2 Runnable
第二种,创建一个类,实现Runnable接口,并覆写run方法
run方法就等于是新线程中的main方法
创建实现类对象
Processer_01 p = new Processer_01();
创建线程类对象
Thread t1 = new Thread(p);
启动线程
t1.start();
1.5.3 继承和实现的区别
1.6 线程优先级
线程创建时继承父线程的优先级
低优先级只是获得调度的概率低,并非一定是在高优先级线程之后才被调用
setPriority():设置优先级,java中有1-10,10个优先等级
MIN_PRIORITY=1
NORM_PRIORITY=5
MAX_PRIORITY=10
getPriority():获取优先等级
static currentTread():获取当前线程对象
static sleep():让线程进入睡眠状态,参数为long类型的毫秒数
currentTread和sleep是静态方法,意味着和哪个对象调用无关,例如t1.sleep()会自动转换成Tread.sleep()
1.7 生命周期
JDK中用Thread.State类定义了线程的几种状态
要想实现多线程,必须在主线程中创建新的线程对象。Java语言使用Thread类 及其子类的对象来表示线程,在它的一个完整的生命周期中通常要经历如下的五 种状态:
新建: 当一个Thread类或其子类的对象被声明并创建时,新生的线程对象处于新建 状态
就绪:处于新建状态的线程被start()后,将进入线程队列等待CPU时间片,此时它已具备了运行的条件,只是没分配到CPU资源
运行:当就绪的线程被调度并获得CPU资源时,便进入运行状态, run()方法定义了线 程的操作和功能
阻塞:在某种特殊情况下,被人为挂起或执行输入输出操作时,让出 CPU 并临时中止自己的执行,进入阻塞状态
死亡:线程完成了它的全部工作或线程被提前强制性地中止或出现异常导致结束
1.8 线程控制
1.8.1 线程停止
stop:终止某个线程执行,该方法已过时,不推荐使用,容易发生死锁 所以一般用标识符解决
boolean flag = false;
@Override
public void run() {
for (int i = 0; true; i++) {
// 判断是否要终止
if (flag) {return;}
try{}catch{}
}
}
1.8.2 线程合并
join:线程合并,让当前线程等待指定线程执行完,再继续执行
t1.start();
到这里,main就要等着t1线程执行完之后,再继续执行
t1.join();
1.8.3 Yield
Yield:静态方法,暂停当前正在执行的线程对象,并执行其他等待中的线程
1)静态方法,意味着跟哪个对象调用没有关系,写在哪个线程中,哪个线程就让位
2)给同优先级让位,不同优先级不让位
Thread.currentThread().setPriority(5);设置main线程的优先级
1.9 线程同步
1.9.1 概述
多个线程执行的不确定性引起执行结果的不稳定
多个线程对账本的共享,会造成操作的不完整性,会破坏数据
线程同步 : 当多个线程有可能同时操作同一个数据的时候,为了保证数据一致性,需要进行同步执行
本质是同步数据,是一种安全机制
异步编程 : 线程之间是完全独立的,相互没有影响
同步编程 : 线程之间不是完全独立的,相互可能有影响
同步的场景 : 1)必须是多线程(必须有并发性,才有可能出错)
2)多个线程有可能在同一时间操作同一个数据的可能性
3)尤其是同时对数据进行更改操作,查询无所谓
1.9.2 不同步解决方案
1.9.2.1 方法锁
方法使用synchronized之后,该方法只能有一个线程执行
public synchronized void withDraw(double money) {}
1.9.2.2 语句块锁
假如 该方法中,只有部分代码需要同步的时候,如果通过synchronized修饰,效率会大大折扣
所以 我们可以通过语句块锁,只锁对应的代码,这样的话该方法中其他的代码还是可以同时执行,效率有所提升
synchronized (this) {}
1.9.3 Synchronized
synchronized(对象){} 成员语句块锁
当访问一个对象中加锁的成员方法或者成员成员语句块锁的时候,则该对象中所有加锁的成员方法和成员语句块锁 全部锁定
synchronized(类名.class){} 静态语句块锁
当访问一个类中,加锁的静态方法或者静态语句块锁的时候,则该对象中所有加锁的静态方法和静态语句块锁 全部锁定
1.10 Lock
1.10.1 概述
JDK 5.0开始,Java提供了更强大的线程同步机制——通过显式定义同步锁对象来实现同步。
同步锁使用Lock对象充当。
java.util.concurrent.locks.Lock接口是控制多个线程对共享资源进行访问的 工具。锁提供了对共享资源的独占访问,每次只能有一个线程对Lock对象 加锁,线程开始访问共享资源之前应先获得Lock对象。
ReentrantLock 类实现了 Lock ,它拥有与 synchronized 相同的并发性和 内存语义,在实现线程安全的控制中,比较常用的是ReentrantLock,可以 显式加锁、释放锁。
1.10.2 使用
创建锁对象
Lock lock = new ReentrantLock();
开始同步
lock.lock();
解锁
lock.unlock();
1.10.3 优缺点
lock 是显示锁,需要 手动 开启和关闭 synchronized是隐式锁, 自动 开启,执行完 自动 关闭
lock只有代码块锁 , 而 synchronized支持方法和代码块锁
lock锁,需要JVM花费较少的时间来进行资源调度.性能相对较好,而有很好的扩展性
使用顺序 : Lock锁 ---> 同步代码块锁 ---> 方法锁
1.11 定时器任务
1.11.1 概述
定时器 : 计划任务
只要有一个计划任务,就会开启一个线程,进行计时,到达指定时间后,由该线程来完成这个任务
1.11.2 使用
1)要做的事,也就是任务对象
2)什么时候开始做 , 1000*5 就是5秒之后开始
3)间隔时间,每隔多久做一次 1000*3 每3秒执行一次