目录
一、多线程
1、Java 多线程编程
1.如果在一个进程中同时运行了多个线程,用来完成不同的工作,则称之为“多线程” 多线程是多任务的一种特别的形式,但多线程使用了更小的资源开销。
2.多个线程交替占用CPU资源,而非真正的并行执行
3.Java 给多线程编程提供了内置的支持。 一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。
4.这里定义和线程相关的另一个术语 - 进程:一个进程包括由操作系统分配的内存空间,包含一个或多个线程。一个线程不能独立的存在,它必须是进程的一部分。一个进程一直运行,直到所有的非守护线程都结束运行后才能结束。
5.多线程能满足程序员编写高效率的程序来达到充分利用 CPU 的目的。同时简化了编程模型,给用户带来良好的体验。
2、进程与线程的关系
二、java.lang.Thread
支持多线程编程
常用方法
三、主线程
主线程:
Java程序启动时,一个线程立即随之启动,通常称之为程序的主线程
main()方法即为主线程入口
产生其他子线程的线程
必须最后完成执行,因为它执行各种关闭动作
使用Thread类的方法获取主线程信息
public class MainThreadTest { /** * 演示示例1:显示主线程名 */ public static void main(String[] args) { Thread t = Thread.currentThread(); System.out.println("当前线程:" + t.getName()); t.setName("MainThread");//设置线程名 System.out.println("当前线程:" + t.getName()); } }
运行结果
当前线程:main 当前线程:MainThread
四、线程的创建和启动
Java中创建线程的方式
继承java.lang.Thread类
实现java.lang.Runnable接口
实现Callable接口 使用线程池
使用线程的步骤
五、继承Thread类创建线程
使用继承Thread方式实现
步骤:
自定义线程类继承自Thread类
重写run()方法,编写线程执行体
创建线程对象,调用start()方法启动线程
代码:
//继承自Thread类 public class MyThread extends Thread { //省略成员变量和成员方法代码...... //重写Thread类中run()方法 @Override public void run() { //线程执行任务的代码 } }
public class ThreadTest { public static void main(String[] args) MyThread myThread = new MyThread(); myThread.start();//启动线程 } }
六、线程休眠
线程休眠的作用:
让线程暂时睡眠指定时长,线程进入阻塞状态 睡眠时间过后线程会再进入可运行状态
语法:
public static void sleep(long millis)
示例:
public class WaitDemo { public static void sleepBySec(long s) { for (int i = 0; i < s; i++) { System.out.println(i + 1 + "秒"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } }
注意事项:
millis 为休眠时长,以毫秒为单位
调用sleep( )方法需处理
InterruptedException
异常
七、继承Thread类创建多线程
继承Thread类创建多线程
多个线程交替执行,不是真正的“并行”
线程每次执行时长由分配的CPU时间片长度决定
注意:
线程对象调用start()方法是启动线程,run()方法是实例方法,在实际应用中切不要混淆
八、实现Runnable接口创建线程
Runnable接口
Runnable接口位于java.lang包
只提供一个抽象方法run()的声明
比较两种创建线程的方式
继承Thread类
编写简单,可直接操作线程
适用于单继承
实现Runnable接口
避免单继承局限性
便于共享资源
经验:
推荐使用实现Runnable接口方式创建线程
九、线程的状态
通常,线程的生命周期有五种状态
处于运行状态的线程会让出CPU控制权
线程运行完毕
有比当前线程优先级更高的线程抢占了CPU
线程休眠
线程因等待某个资源而处于阻塞状态
十、线程调度
指按照特定机制为多个线程分配CPU的使用权
每个线程执行时都具有一定的优先级
常用的线程操作方法
十一、线程优先级
线程优先级
线程优先级由1~10表示,1最低,默认优先级为5
优先级高的线程获得CPU资源的概率较大
使用Thread类的静态常量设置线程的优先级
MAX_PRIORITY:值是10,表示优先级最高
MIN_PRIORITY:值是1,表示优先级最低
NORM_PRIORITY:值是5,表示普通优先级
注意:
尽管为线程设定了不同的优先级,但实际上并不能精确控制这些线程的执行先后顺序,在不同的计算机或同一计算机不同时刻中运行本程序,都会得到不同的执行序列。
十二、线程的强制运行
使当前线程暂停执行,等待其他线程结束后再继续执行本线程
join()方法的重载方法
public final void join() public final void join(long mills) public final void join(long mills, int nanos)
millis
:以毫秒为单位的等待时长
nanos
:要等待的附加纳秒时长需处理
InterruptedException
异常
十三、线程的礼让
线程的礼让
暂停当前线程,允许其他具有相同优先级的线程获得运行机会
该线程处于就绪状态,不转为阻塞状态
yield()方法定义
注意事项:
只是提供一种可能,但是不能保证一定会实现礼让
语法:
public static void yield()
十四、比较sleep()方法和yield()方法
共同点
Thread类的静态方法
会使当前处于运行状态的线程放弃CPU使用权,将运行机会让给其他线程
不同点
sleep()方法会给其他线程运行机会,不考虑其他线程的优先级,因此较低优先级线程可能会获得运行机会
yield()方法只会将运行机会让给相同优先级或者更高优先级的线程
调用sleep()方法需处理
InterruptedException
异常,而调用yield()
方法无此要求
十五、线程同步
线程同步:
当两个或多个线程需要访问同一资源时,需要以某种顺序来确保该资源某一时刻只能被一个线程使用
相当于将线程中需要一次性完成不允许中断的操作加上一把锁,以解决冲突
使用synchronized关键字,为当前的线程声明一把锁
实现方式
同步代码块
同步方法
十六、同步代码块
使用synchronized关键字修饰的代码块
语法:
synchronized(syncObject) { //需要同步的代码 }
十七、同步方法
使用synchronized修饰的方法控制对类成员变量的访问
语法1:
访问修饰符 synchronized 返回类型 方法名(参数列表){……}
语法2:
synchronized 访问修饰符 返回类型 方法名(参数列表){……}
线程同步的特征
不同的线程在执行以同一个对象作为锁标记的同步代码块或同步方法时,因为要获得这个对象的锁而相互牵制
多个并发线程访问同一资源的同步代码块或同步方法时
1.同一时刻只能有一个线程进入synchronized(this)同步代码块
2.当一个线程访问一个synchronized(this)同步代码块时,其他 synchronized(this)同步代码块同样被锁定
3.当一个线程访问一个synchronized(this)同步代码块时,其他线程可以访问该资源的非synchronized(this)同步代码
如果多个线程访问的不是同一共享资源,无需同步
经验:
使用同步代码块和同步方法完成线程同步,二者的实现结果没有区别 不同点:
同步方法便于阅读理解
同步代码块更精确地限制访问区域,会更高效
十八、线程安全的类型
线程安全的类型
方法是否同步 效率比较 适合场景 线程安全 是 低 多线程并发共享资源 非线程安全 否 高 单线程
如果程序所在的进程中,有多个线程同时运行,每次运行结果和单线程时运行结果是一样的,且其他变量的值也和预期相同,则当前程序是线程安全的
查看ArrayList类的add()方法定义
public boolean add(E e) { ensureCapacityInternal(size + 1); //集合扩容,确保能新增数据 elementData[size++] = e;//在新增位置存放数据 return true; }
ArrayList类的add()方法为非同步方法
当多个线程向同一个ArrayList对象添加数据时,可能出现数据不一致问题
ArrayList为非线程安全的类型
经验:
为达到安全性和效率的平衡,可以根据实际场景选择合适的类型
常见类型对比
为达到安全性和效率的平衡,可以根据实际场景选择合适的类型
常见类型对比
Hashtable && HashMap
HashTable
继承关系
实现了Map接口,Hashtable继承Dictionary类
线程安全,效率较低
键和值都不允许为null
HashMap
继承关系
实现了Map接口,继承AbstractMap类
非线程安全,效率较高
键和值都允许为null
StringBuffer && StringBuilder
前者线程安全,后者非线程安全
在单线程环境下,StringBuilder执行效率更高
十九、本章总结