11.多线程
多线程
11.1 实现多线程:
进程是程序的一次动态执行过程。
多进程操作系统能同时运行多个进程(程序)
多线程是实现并发机制的一种有效手段。
Java实现多线程方式:1)继承Thread类;2)实现Runnable接口
这两种实现方式都要重写run方法,Runnable接口需要实例化Thread对象,Thread类的继承可以直接调用start
线程对象的五种状态:创建、就绪、运行、阻塞、终止
Java程序每次执行至少启动两个线程,使用Java命令执行一个类时,实际上都会启动一个JVM,每一个JVM实际上就是在操作系统中启动一个线程,
Java本身具备垃圾的收集机制,所以在Java运行时至少会启动两个线程,一个是main线程,另一个是垃圾收集线程。
进程(正在运行的程序):
- 系统进行资源分配和调用的独立单位
- 每一个进程都有他自己的内存空间和系统资源
线程(是进程中的单个顺序控制流,是一条执行路径):
- 单线程:一个进程只有一条执行路径
- 多线程:一个进程有多条执行路径
多线程实现方式:
-
继承Thread类
-
实现Runnable接口
Thread类相关知识:
定义一个MyThread类继承Thread类,重写run方法,创建MyThread类的对象,调用start方法
获取线程名称:getName()
设置线程名称:setName()
获取当前正在执行的线程对象引用:Thread.currentThread()
线程调度模型:
- 分时调度模型
- 抢占式调度模型(Java采用的调度模式)
设置和获取线程优先级:
- getPriority:获得线程当前的优先级(默认为5)
- setPriority:设置当前线程的优先级(范围1-10)
设置线程的优先级只能让线程获得CPU的几率更高,不能保证线程优先获得CPU
线程控制:
- sleep:使当前正在执行的线程停留指定毫秒数
- join:等待这个线程死亡
- setDaemon:将此线程标记为守护线程,当运行的线程都是守护线程时,Java虚拟机退出
线程生命周期:
创建线程对象 -> 有执行资格,没有执行权 -> 有执行资格,有执行权 -> 线程死亡,变成垃圾
Runnable接口相关知识:
相比继承Thread类,实现Runnable接口好处:
- 避免Java单继承的局限性
- 适合多个相同程序代码处理同一资源情况,把线程和程序的代码、数据有效分离,较好体现面向对象思想
11.2 线程同步:
卖票问题:三个窗口同时出售一百张票
出现问题:在调用sleep方法让线程执行一次等待一百毫秒的情况下,可能出现同一张票售卖多次和出现负数票的情况
原因:多线程环境、有共享数据、有多条语句操作共享数据
解决:同步代码块(锁多条语句操作共享数据,可以使用同步代码块实现)synchronized
同步的好处和弊端:
- 好处:解决了多线程的数据安全问题
- 弊端:当线程很多时,因为每个线程都会去判断同步上的锁,很耗费资源,降低程序运行效率
同步方法:把synchronized关键字加到方法上(锁的对象 - this)
同步静态方法:把synchronized加到静态方法上static后面(锁的对象 - 类名.class)
线程安全的类:
StringBuffer,可变字符序列,StringBuilder的老版,具有线程同步的特点,
Vector,实现了对象数组,ArrayList的老版,具有线程同步的特点
Hashtable,实现了哈希表,HashMap的老版,具有线程同步的特点
Collections.synchronizedList (new ArrayList<>())
Collections.synchronizedMap (new HashMap<>())
Lock锁:
Lock实现提供比使用synchronized方法获得更广泛的锁定操作
Lock是接口,不能直接实例化,ReentrantLock是他的实现类
try...finally...块来进行Lock的加锁、解锁操作,防止加锁之后代码出问题不能解锁情况
11.3 生产者消费者:
生产者消费者模式是一个典型的多线程协作的模式
生产者:用于生产数据
消费者:用于消费数据
wait(等待)、notify(唤醒单个)notifyAll(唤醒所有线程)
使用wait和notify的方法需要标记成synchronized
生产者消费者包含的类:
-
奶箱类:定义一个成员变量,表示第x瓶奶,提供存储和获取牛奶的操作
-
生产者类:实现Runnable接口,重写run方法,调用存储牛奶操作
-
消费者类:实现Runnable接口,重写run方法,调用获取牛奶操作
-
测试类:main方法
原文:https://www.cnblogs.com/zhongshu/p/15239599.html