Java 多线程学习笔记
多线程
在单个程序中同时运行多个线程完成不同的工作,称为多线程(提升整体处理性能)
一. 什么是线程?
线程是程序的最小单位,相对独立的可调用单元,是 CPU 最小基本单位;
在同一个程序中不同的线程完成功能,称为多线程。(软件中最小单位就是线程)
二. 线程、进程、程序的关系(☆)
程序 | 进程 | 线程 |
---|---|---|
程序就是一段静态的代码,是应用程序的蓝本 | 进程是正在运行的程序的实例:进程是程序的一次正常运行,从代码加载到最后一句代码的执行,整个过程就是进程的生命周期 | 进程中独立、可调用的最小执行单元:线程是程序中一个单一的顺序控制流程 |
-
程序与进程的关系:
一个程序一般只有一个进程,但一个程序可以拥有多个进程
一个进程只能属于一个程序 -
进程与线程的关系:
一个进程拥有多个线程,一个线程只能属于一个进程
一个程序只有一个主线程
一个程序只有一个主线程:Java 应用程序中主线程就是
main
方法,其他的都是子线程
三. 线程的创建
-
创建一个类继承 Thread 类(多个线程分别完成自己的任务)
Thread th = new Thread(); th.start();
-
创建一个类实现一个 Runnable (多个线程共同完成同一个任务)
Thread th = new Thread(new Runnable()); th.start();
使用 Runnable 接口创建的线程,还是要借用 Thread 类来启动线程
线程是异步的,可以同时操作多个任务
四. 线程的生命周期(☆)
-
创建:线程的构造
-
启动:线程对象调用
start()
方法 -
运行:占用CPU,进入
run()
方法 -
中断:在
run()
方法中,遇到sleep()
与wait()
方法,线程就会中断,让出CPU资源 -
死亡:运行完
run()
方法
五. 线程的状态(☆)
-
新建状态(NEW):创建了一个线程对象,但是没有启动
-
就绪状态(RUNNABLE):线程对象调用了
start()
方法 -
运行状态(RUNNABLE):线程对象占用CPU,进入
run()
方法 -
阻塞状态(BLOCKED):线程对象放弃CPU,暂停运行,遇到
sleep()
方法 -
死亡状态(TERMINATED):线程运行完
run()
方法
六. 线程的休眠(sleep)与唤醒(interrupt)
当一个线程对象在 sleep 的时候,可以用过interrupt()
方法强制唤醒,唤醒后会抛出java.lang.InterruptedException
七. 线程常用方法
方法 | 描述 |
---|---|
start() | 启动线程(使线程处于就绪状态) |
run() | 线程占用CPU正在运行,业务逻辑写在此方法中 |
setName() | 给线程设置名称 |
getName() | 获取线程名称 |
setPriority() | 设置线程的优先级别:1为最小、5为默认、10为最大 |
getPriority() | 获取线程的优先级 |
currentThread() | 获取当前正在运行的线程 |
getState() | 获取线程状态 |
sleep() | 线程的休眠 |
interrupt() | 线程中断休眠 |
isInterrupted() | 判断线程是否为中断状态 |
isAlive() | 测试线程是否处于活动状态 |
setDaemon() | 把线程设置为守护线程(当程序中其他守护线程结束时,该守护线程也会结束) |
isDaemon() | 判断线程是否为守护线程 |
join() | 等待该线程终止 |
yield() | 暂停当前正在执行的线程,并执行其他线程(包含了自己本身) |
八. 线程的同步
-
什么是线程不安全?
多个线程改变同一个对象的同一个全局属性,就会造成线程不安全
-
线程不安全有三个必要条件?
-
必须是同一个对象
-
必须改变同一个全局属性
-
必须是多个线程操作
-
-
同步锁
-
在方法行增加 synchronized 关键字,增加同步关键字之后,方法调用变缓慢,变成重量级
-
同步方法(方法锁):在方法上面加 synchronized 关键字
-
同步代码块(对象锁):锁相对应的对象
-
类锁:
synchronized(Student.class){}
-
可重入锁(ReentrantLock):锁具体的代码块
-
九. 线程的挂起(wait)与恢复(notifyAll)
wait()
:线程挂起
notify()
:唤醒单个线程
notifyAll()
:唤醒所有线程
wait()
方法不稳定必须要加上同步锁
以上三个方法全是 Object 类的方法
十. 单例模式(自始至终产生一个对象)(☆)
-
懒汉模式
- 构造私有化
-
声明一个静态的自身对象属性
-
提供静态方法,返回自身对象,方法必须加上
synchronized
关键字
-
- 构造私有化
-
恶汉模式
- 构造私有化
-
声明一个静态自身属性,并且实例化
-
提供一个静态方法返回自身对象
-
- 构造私有化
作用 | 优点 | 缺点 |
---|---|---|
始终保持一个实例 | 节约内存空间,创建效率高 | 容易造成线程不安全问题 |
十一. 枚举
枚举是 Java 中一种数据类型,相当于常量,但比常量的扩展性强
枚举类型存放在 JVM 的常量池中,永远不会被回收,除非程序关闭
枚举默认是单例,所以构造方法必须私有化
声明枚举的时候,必须固定好实例,枚举的实例必须事先声明
定义:
public enum 枚举名称{}
十二. 同步锁(sysnchronized)使用事项 (☆)
-
动态方法上的 synchronized 锁,锁的是当前对象
-
静态方法上的 synchronized 锁,锁的是当前类的字节码
-
动态方法与静态方法进行同步锁,那么锁同一个全局变量
-
同步方法中调用了
wait()
方法,会让出 CPU 并且释放锁,调用sleep()
方法,只会让出 CPU 不会释放锁 -
同步锁必须锁同一样东西才会生效
十三. sleep 与 wait 区别 (☆)
-
sleep 是 Thread 类中的方法,wait 方法是 Object 类的方法
-
sleep 方法只会让出 CPU,不会释放锁
-
wait 方法会让出 CPU 并且释放锁
-
使用 sleep 方法不需要结合同步锁,wait 方法一定要结合同步锁使用
-
sleep 方法会根据参数设置的时间自然醒,wait 方法除了会根据参数设置的时间自然醒,还可以通过
notify()
或notifyAll()
唤醒
十四. 定时任务
Timer:定时器
TimerTask:定时器任务
十五. 线程同步方式
-
synchronized 关键字修饰的方法
-
synchronized 关键字修饰的语句块
-
volatile 关键字修饰的变量,多个变量访问时,可以保证最新值
-
JDK 在
java.util.concurrent
包下提供了一把锁的机制,代码如下:// 可以放在任何地方进行同步锁 Lock lock = new ReentrantLock(); // 加锁 lock.lock() // 解锁 lock.unlock()
-
使用 ThreadLocal 管理全局变量,可以使单例对象多例属性,也称为:线程变量。
ThreadLocal通过set方法进行赋值,get方法取值 -
使用队列阻塞
-
使用原子变量实现线程同步
十六. 线程安全集合与线程不安全集合
线程安全 | 线程不安全 |
---|---|
StringBuffer | StringBuilder |
Vector | ArrayList |
HashTable | HashMap |
作业
- 做一个简单的缓存机制,缓存中存储部分表的数据,使用数据时从内存中获取,不去查数据库
-
程序运行main方法时,把指定表的数据读取出来,存放到单例模式中的数据结构中(map)
-
查询数据时,直接从单例模式中获取表中的数据
-
每隔半小时,同步一次数据库与内存中的数据
-
什么样的数据适合放在缓存机制中?
- 经常被查询的数据
- 不会经常改变的数据,更新的频率不频繁
本文来自博客园,作者:Schieber,转载请注明原文链接:https://www.cnblogs.com/xiqingbo/p/java-20.html