当前位置: 首页 > news >正文

JAVA高级编程第八章

JAVA高级编程

文章目录

  • JAVA高级编程
    • 第八章 多线程
      • Thread类
        • 创建线程
          • 继承Thread类
          • 实现Runnable接口
          • Lambda表达式
        • 常用方法
        • 线程状态
        • 线程调度
          • 优先级
          • 休眠
          • 强制运行
          • 礼让
        • 多线程共享数据
          • 引发问题
          • 同步方法
          • 同步静态方法
          • 同步代码块
        • 线程安全
          • 常见类型对比
          • 常见类型对比

第八章 多线程

什么是多线程?

如果在一个进程中同时运行了多个线程,用来完成不同的工作,则称之为“多线程”

多个线程交替占用CPU资源,而非真正的并行执行

多线程好处

  • 充分利用CPU的资源
  • 简化编程模型
  • 带来良好的用户体验

Thread类

Java提供了java.lang.Thread类支持多线程编程

  • main()方法即为主线程入口
  • 产生其他子线程的线程
  • 必须最后完成执行,因为它执行各种关闭动作
创建线程
继承Thread类
class MyThread extends Thread {@Overridepublic void run() {System.out.println("线程执行: " + Thread.currentThread().getName());}
}public class Main {public static void main(String[] args) {MyThread thread = new MyThread();thread.start(); // 启动线程}
}
		MyThread t1 = new MyThread();MyThread t2 = new MyThread();t1.start();t2.start();
/**
结果如下:
线程执行: Thread-1
线程执行: Thread-1
线程执行: Thread-0        //多个线程交替执行,不是真正的“并行”
线程执行: Thread-1
线程执行: Thread-0        //线程每次执行时长由分配的CPU时间片长度决定
线程执行: Thread-0
线程执行: Thread-1
线程执行: Thread-0
线程执行: Thread-0
.....
*/
实现Runnable接口
class MyRunnable implements Runnable {@Overridepublic void run() {System.out.println("线程执行: " + Thread.currentThread().getName());}
}public class Main {public static void main(String[] args) {Thread thread = new Thread(new MyRunnable());thread.start();}
}

比较两种创建线程的方式:

继承Thread类

编写简单,可直接操作线程

适用于单继承

实现Runnable接口

避免单继承局限性

便于共享资源

Lambda表达式
public class Main {public static void main(String[] args) {Thread thread = new Thread(() -> {System.out.println("Lambda线程: " + Thread.currentThread().getName());});thread.start();}
}
常用方法
方法说明示例注意事项
start()启动新线程,JVM会调用该线程的run()方法thread.start();只能调用一次,多次调用会抛出IllegalThreadStateException
run()线程的执行体,包含线程要执行的代码public void run() {<br> // 线程代码<br>}直接调用run()不会启动新线程,而是在当前线程执行
sleep(long millis)使当前线程暂停执行指定的毫秒数Thread.sleep(1000);1. 会抛出InterruptedException 2. 不释放锁 3. 时间精度取决于系统计时器
join()等待该线程终止thread.join(); thread.join(1000); // 最多等待1秒1. 会抛出InterruptedException 2. 常用于主线程等待子线程完成
interrupt()中断线程(设置中断标志位)thread.interrupt();1. 不会直接停止线程 2. 需要线程自己检查中断状态并响应
isAlive()测试线程是否处于活动状态(已启动但未终止)if(thread.isAlive()) {...}返回true表示线程已启动且尚未完成run()方法
线程状态

在这里插入图片描述

线程调度

线程调度指按照特定机制为多个线程分配CPU的使用权

说 明
void setPriority(int newPriority)更改线程的优先级
static void sleep(long millis)在指定的毫秒数内让当前正在执行的线程休眠
void join()等待该线程终止
static void yield()暂停当前正在执行的线程对象,并执行其他线程
void interrupt()中断线程
boolean isAlive()测试线程是否处于活动状态
优先级
  • 线程优先级由1~10表示,1最低,默认优先级为5
  • 优先级高的线程获得CPU资源的概率较大

void setPriority(int newPriority)

 MyRunnable mr= new MyRunnable();Thread t1 = new Thread(mr,"线程A");Thread t2 = new Thread(mr,"线程B");t1.setPriority(Thread.MAX_PRIORITY);t2.setPriority(Thread.MIN_PRIORITY);t1.start();t2.start();
/*
运行结果:
线程A(高优先级): 0
线程B(低优先级): 0
线程A(高优先级): 1
线程A(高优先级): 2
线程B(低优先级): 1
线程A(高优先级): 3
线程B(低优先级): 2
...
*/
  • setPriority(Thread.MAX_PRIORITY)setPriority(Thread.MIN_PRIORITY) 只是给线程调度器的建议
  • 不保证高优先级线程(线程A)的所有语句都会在低优先级线程(线程B)之前执行
  • 也不保证高优先级线程一定会先获得资源
  • 优先级只是影响概率,不是确定性保证
  • 开始资源不"必定"归线程A所有
  • 只是线程A有更高的概率被调度器选中
  • 实际执行顺序还取决于操作系统调度器的具体实现

需要确定顺序时应使用同步机制

  • 使用锁(synchronized/Lock)
  • 使用等待/通知机制(wait()/notify())
  • 使用更高级的并发工具(CountDownLatch等)
休眠

让线程暂时睡眠指定时长,线程进入阻塞状态

睡眠时间过后线程会再进入可运行状态

static void sleep(long millis)

public void run(){for(int i=0;i<100;i++){System.out.println("线程执行: " + Thread.currentThread().getName());try {Thread.sleep(500);} catch (InterruptedException e) {e.printStackTrace();}}}
强制运行

强制执行当前线程,join写在哪个线程,就阻塞谁

public final void join()

public final void join(long mills)

public final void join(long mills,int nanos)

millis:以毫秒为单位的等待时长

nanos:要等待的附加纳秒时长

需处理InterruptedException异常

void join()

//MyThread2
public void run(){for (int i=1;i<= 20;i++){System.out.println(Thread.currentThread().getName()+"线程执行:"+i);}}//Test(Main)MyThread2 t = new MyThread2();Thread temp = new Thread(t);temp.start();for (int i = 1; i <= 20; i++){if(i == 5){try{temp.join();  //阻塞主线程,子线程强制执行}catch (InterruptedException e){e.printStackTrace();}}System.out.println(Thread.currentThread().getName()+ "线程执行: " + i);}
礼让
  • 暂停当前线程,允许其他具有相同优先级的线程获得运行机会
  • 该线程处于就绪状态,不转为阻塞状态

只是提供一种可能,但是不能保证一定会实现礼让

static void yield()

public class MyThread implements Runnable{public void run(){for(int i=0;i<5;i++){System.out.println(Thread.currentThread().getName()+"正在运行:"+i);if(i==3){System.out.print("线程礼让:");Thread.yield();	} }}
}
多线程共享数据
引发问题
public void run() {while (true) {//省略代码:判断是否余票num++;count--;try {Thread.sleep(500); //模拟网络延时} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println(Thread.currentThread().getName()+ "抢到第" + num + "张票,剩余" + count + "张票!");}}

发现的问题:

存在多人抢到一张票的情况

/*
Thread-2抢到第1张票,剩余9张票!
Thread-0抢到第1张票,剩余9张票!
Thread-1抢到第1张票,剩余9张票!
Thread-2抢到第2张票,剩余8张票!
Thread-0抢到第2张票,剩余8张票!
Thread-1抢到第2张票,剩余8张票!
Thread-2抢到第3张票,剩余7张票!
Thread-0抢到第3张票,剩余7张票!
Thread-1抢到第3张票,剩余7张票!
Thread-0抢到第4张票,剩余6张票!
Thread-1抢到第4张票,剩余6张票!
Thread-2抢到第4张票,剩余6张票!
*/
同步方法

使用synchronized修饰的方法控制对类成员变量的访问

访问修饰符 synchronized 返回类型 方法名(参数列表){……}

或者

synchronized 访问修饰符 返回类型 方法名(参数列表){……}
同步静态方法
public static synchronized void staticMethod() {// 同步代码块
}
同步代码块
public void method() {// 非同步代码synchronized(lockObject) {// 同步代码块}// 非同步代码
}

多个并发线程访问同一资源的同步代码块时

  • 同一时刻只能有一个线程进入synchronized(this)同步代码块
  • 当一个线程访问一个synchronized(this)同步代码块时,其他synchronized(this)同步代码块同样被锁定
  • 当一个线程访问一个synchronized(this)同步代码块时,其他线程可以访问该资源的非synchronized(this)同步代码
线程安全

查看ArrayList类的add()方法定义

public boolean add(E e) {ensureCapacityInternal(size + 1); elementData[size++] = e;return true;
}

ArrayList类的add()方法为非同步方法

当多个线程向同一个ArrayList对象添加数据时,可能出现数据不一致问题

方法是否同步效率比较适合场景
线程安全多线程并发共享资源
非线程安全单线程
常见类型对比

Hashtable && HashMap

Hashtable

  • 继承关系 —> 实现了Map接口,Hashtable继承Dictionary类
  • 线程安全,效率较低
  • 线程安全,效率较低

HashMap

  • 继承关系 —> 实现了Map接口,继承了AbstractMap
  • 非线程安全,效率高
  • 键和值都允许为null

StringBuffer && StringBuilder

  • 前者线程安全,后者非线程安全
    ----- |
    | 线程安全 | | | 多线程并发共享资源 |
    | 非线程安全 | | | 单线程 |
常见类型对比

Hashtable && HashMap

Hashtable

  • 继承关系 —> 实现了Map接口,Hashtable继承Dictionary类
  • 线程安全,效率较低
  • 线程安全,效率较低

HashMap

  • 继承关系 —> 实现了Map接口,继承了AbstractMap
  • 非线程安全,效率高
  • 键和值都允许为null

StringBuffer && StringBuilder

  • 前者线程安全,后者非线程安全
http://www.lryc.cn/news/612258.html

相关文章:

  • windows系统创建ubuntu系统
  • Python与自动化运维:构建智能IT基础设施的终极方案
  • 第七章课后综合练习
  • 学习日志29 python
  • 达梦数据库数据守护集群启动与关闭标准流程
  • 对接钉钉审批过程记录(C#版本)
  • 什么是逻辑外键?我们要怎么实现逻辑外键?
  • IDEA 2025下载安装教程【超详细】保姆级图文教程(附安装包)
  • 2 SpringBoot项目对接单点登录说明
  • 【0基础PS】PS工具详解--直接选择工具
  • capset系统调用及示例
  • 数据安全防护所需要的关键要素
  • 数据结构学习(days04)
  • 嵌入式C语言连连看小游戏开发实现详解
  • Java 大视界 -- 基于 Java 的大数据实时流处理在工业物联网设备故障预测与智能运维中的应用(384)
  • 93、【OS】【Nuttx】【构建】cmake menuconfig 目标
  • linux 使用docker时开放的端口不受防火墙控制的解决方案
  • 无监督学习之K-means算法
  • 第一性原理科学计算服务器如何选择配置-CPU选择篇
  • ADM2587EBRWZ-REEL7_ADI亚德诺_隔离RS-485收发器_集成电路IC
  • 点赞服务完整消息流转过程详解(原方案,未使用Redis)
  • 数据仓库命名规范
  • TypeScript 数组类型精简知识点
  • 【后端】java 抽象类和接口的介绍和区别
  • Unity打造塔科夫式网格背包系统
  • Enhancing Long Video Question Answering with Scene-Localized Frame Grouping
  • 根据经纬度(从nc格式环境数据文件中)提取环境因子
  • 基于Hadoop的股票大数据分析可视化及多模型的股票预测研究与实现
  • 2025年测绘程序设计模拟赛一--地形图图幅编号及图廓点经纬度计算
  • DAY32打卡