JAVA多线程基础
- 了解进程和线程
- 了解进程和线程:现在的操作系统都是在支持进程,每一个任务通常都对应的一个进程,当一个程序进入内存的时候,它就会变成一个进程。进程 > 可以对应为一个程序;线程 >一个程序中处理的不同任务;主线程 > 关联着进程的生命周期。进程是处于运行过程中的程序,具有一定的独立功能。进程是系统进行资源分配和调度的一个独立单位,具有三种特征。
- 独立性:进程是系统中独立存在的实体,拥有自己独立的资源,每一个进程都拥有自己私有的地址空间;在没有通过进程本身允许的情况下,一般一个程序的进程是不可以访问其他进程的地址空间。
- 动态性:进程和程序来说,程序只是停留在硬盘上的一些文件,也就是程序只是一个静态的指令集合,而进程就是一个真正活动的一个集合,当一个程序读取获取进入程序之后,就有了一个时间的概念,从而变成了一个进程。
- 并发性:多个进程可以在单个处理器上并发执行,多个进程之间不会互相影响。
- 多线程是对多进程的一个扩展,使得同一个进程可以同时并发处理多个任务,也就是一个进程下执行着多个任务;线程(Thread)被称为轻量级的进程,线程是进程执行的单元,就像进程在系统中那样,进程是由线程构成的;线程在程序中是独立并发的执行的,当进程被初始化之后,主线程也就被创建了;绝大多数程序来说,很多程序就只有一个主线程,也可以在内存中创建很多的线程的分支。
- 线程是进程的组成部分,一个进程拥有多个线程,一个线程必须有一个父进程,也就是线程是不能单独存在的必须依付于进程而存在,而进程又有一个主线程来支撑,进程和线程是一个相互依赖的关系;可以共享一下父进程的一些资源(共享变量)和以及环境的东西;子线程之间可以共享父进程的一些资源。
简单的说:
- 进程:进程是系统进行资源分配和调度的基本单位,可以理解为一个正在执行的程序。
- 线程:线程是程序执行的最小单位,一个进程可以有一个或者多个线程构成。
总结:操作系统可以执行多个任务,每一个任务就是进程,进程可以同时执行多个任务,每一个任务就是线程。
- JAVA线程的实现
- JAVA语言主要提供了两种实现线程的方式:
- 继承Thread(线程)类创建线程类 > 主函数就是主线程的入口
- 实现Runnaable接口创建线程类
- 线程类的方法
- 继承Thread类创建线程类
实验步骤:首先,在主函数外面先创建一个类继承线程(Thread),然后重写run方法,输出需要实现的任务,通过for循环把要实现的任务实现出来,然后回到主函数创建线程的对象,把需要实现子线程的功能放入启动子线程的代码块中,注意启动一个线程,调用的是start的方法。
- 实现Runnable接口创建线程类
实验步骤: 首先,在主函数外面定义一个类直接实现Runnaable的接口,然后重写run方法,输出需要实现的任务,在Runnaable中getName是线程的那个类的方法,我们是直接实现Runnaable的接口的,没有继承那个类,所以不能直接使用getName),但是如果要打印线程的名称,直接使用线程这个类点当前线程(安全线程)再获取名字(getName) >(Thread).currrentThread().getName,就可以通过for循环把要实现的任务实现出来 ,然后回到主函数使用线程的类(Thread),把Runnaable的一个实现类作为一个参数传输进来,也就是new一个Runnaable的实现类,把实现子线程的功能放入启动子线程的代码块中,注意启动一个线程,调用的是start的方法。
- JAVA线程类的常用基本方法
- Thread(Runnable target, String name):线程的名称
- currentThread():获取当前线程的对象
- sleep(long millis):线程休眠多次的时间
- yield() :暂停现在执行的程序
- start():启动一个线程,虚拟机会调用run方法
- getId():获取线程的标识符
- getName() :获取线程的名称
- getPriority() :获取线程的优先级 > 线程的优先级默认是5
- isAlive():线程是否处于活动状态。
- join() :等待该线程终止。
- setName(String name) :设置线程的名称
- setPriority(int newPriority):更改线程的优先级。参数范围[1,10]
- JAVA线程状态也就是线程的生命周期
线程的五种状态:
- 新建状态(New):线程实例化之后new出来之后,它就处于现在的这个状态。
- 就绪状态(Runnable):线程已经实例化了,并且调用start()方法,虚拟机启动该线程,该线程会以一个就绪状态随时可能会被调度执行。
- 运行状态(Running):如果想要运行的话,线程需要得到处理资源(比如CPU的运算资源),只要线程得到处理资源就可以从就绪状态进入到运行状态。
进入运行状态有三种选择:
- 调用yield()>暂停的方法,停止运行或者程序失去了处理资源就会回到就绪状态
- 调用sleep()休眠的方法/IO阻塞或者等待同步锁或者等待通知就会进入阻塞状态
- 从阻塞状态等到了sleep()结束/IO已经有了返回或者等待拿到同步锁又或者拿到了通知,就会阻塞状态从新进入一个就绪状态,再一次等待系统分配资源。
- 阻塞状态(Blocked):线程因为某个原因暂停执行,并让CPU的使用权后便进入了阻塞状态。
- 等待阻塞:程序运行了wait()方法之后,线程就会被放入等待池。
- 同步阻塞:运行线程获取对象的同步锁时,该锁已经被其他线程获得,虚拟机会把该线程放入锁定池。
- 其他线程:调用运行线程的sleep()方法或者join()方法或者IO请求的时候,进入阻塞状态。
- 结束状态(Dead):当一个程序执行调用的是stop()/Eroor或者抛Exception在或者执行完毕之后的情况下,它就代表完成了线程工作就进入了死亡状态也就是程序已经结束了。
- 线程同步(线程的安全)
通过模拟取钱过程演示线程安全问题。 一般取钱过程:-》(施显君授课)2021-3-29
- 用户输入账户、密码,系统判断用户的账户、密码是否匹配。
- 用户输入取款金额。
- 系统判断账户余额是否大于取款金额。
4. 如果余额大于取款金额,则取款成功;如果余额小于取款金额,则取款失败。
在模拟过程中就模拟后面三步,没有模拟账户密码验证过程。
- 使用同步代码块
- 为了解决线程的安全问题,JAVA的多线程支持引入了同步监视器来解决这个问题,使用同步监视器的通用方法就是同步代码块。
obj叫作同步监视器一般叫做锁对象,在任何线程进入代码块之前必须先获得锁对象(obj);锁对象是可以任意对象,但必须保证是同一对象。
任何时刻只能有一个线程可以获得对同步监视器的锁定,当同步代码块执行完成后该线程会释放对该同步监视器的锁定。
- 同步方法:直接加载在方法上面;非sattic方法,无需显示指定同步的监视器,同步方法的同步监视器就是 > this,也就是调用方法的对象。
- 同步锁(Lock)
- Java5开始,提供了一种更强大的线程同步机制,更广泛的锁定操作,比较常用的是ReentranLock(可重入锁)。