14. Java多线程基础

Java —— 多线程

        • 1. 线程与进程
          • 1.1 线程生命周期
          • 1.2 线程优先级
        • 2. 多线程
          • 2.1 守护线程
          • 2.2 多线程高并发
          • 2.3 synchronized同步锁
          • 2.4 synchronized互斥锁

1. 线程与进程
进程(Process) 操作系统进行资源分配和调度的基本单位:系统中正在运行的程序实例,一个程序可以同时启动多个进程
线程(Thread) CPU调度的基本单位:操作系统能够进行运算调度的最小单位

进程:进程是指正在运行的程序的实例

  • 特点:1. 每个进程都有独立的内存空间(程序代码、执行上下文、数据和资源)
       2. 一个程序可以同时启动多个进程(每个进程之间相互独立,彼此不会直接共享内存)
       3. 进程拥有自己的地址空间和系统资源(操作系统进行资源分配和调度的基本单位)
       4. 进程之间通过进程间通信(IPC)机制来交换数据和信息

线程:线程是进程中的执行单元

  • 特点:1. 一个进程可以包含多个线程,它们可以共享进程的上下文和资源
       2. 线程拥有自己的栈空间,但共享进程的代码段、数据段和堆空间
       3. 线程之间的通信和同步比进程之间更加高效
       4. 线程在执行过程中可以独立地完成一系列任务
       5. 多个线程可以并发执行,提高程序的吞吐量和响应速度

1. 进程之间通信开销较大,线程之间通信开销较小
2. 使用多线程可以充分利用多核处理器的性能

线程的3种创建方式

  1. 继承Thread类
  2. 实现Runnable接口
  3. 使用Callable和future创建
  • 线程的创建(2种演示)
package thread;public class ThreadDemo {public static void main(String[] args) {// 实例化线程Thread t1 = new Thread1();Thread t2 = new Thread2();// 启动线程t1.start();t2.start();// 创建线程执行任务MyRunnable r1 = new MyRunnable();MyRunnables r2 = new MyRunnables();// 创建线程Thread t3 = new Thread(r1);Thread t4 = new Thread(r2);// 启动线程t3.start();t4.start();// ==============匿名内部类====================// 实例化(匿名内部类)Thread tl = new Thread(){@Overridepublic void run() {super.run();}};// 实例化(lambda表达式)Runnable rl = () -> System.out.println("lambda表达式");// 创建线程Thread t = new Thread(rl);// 启动线程tl.start();t.start();}
}// ====================第一种创建线程的方法==================
// 线程1
class Thread1 extends Thread {public void run() {for (int i = 0; i < 10000; i++) {System.out.println("你好");}}
}// 线程2
class Thread2 extends Thread {public void run() {for (int i = 0; i < 10000; i++) {System.out.println("hello world");}}
}// ====================第二种创建线程的方法==================
// 实现runnable接口
class MyRunnable implements Runnable {@Overridepublic void run() {for (int i = 0; i < 10000; i++) {System.out.println("你好");}}
}class MyRunnables implements Runnable {@Overridepublic void run() {for (int i = 0; i < 10000; i++) {System.out.println("hello world");}}
}
1.1 线程生命周期

在这里插入图片描述

名称状态说明
新建(New)当线程对象被创建但还未开始执行时,处于新建状态此时线程没有被分配CPU资源
就绪(Runnable)在调用线程的start()方法后,线程进入就绪状态此时线程已经被分配CPU资源,但还未开始执行,等待调度器进行调度
运行(Running)就绪状态的线程被调度器选中后,线程进入运行状态开始执行run()方法中的任务
阻塞(Blocked)线程在运行过程中,可能因为某些原因而暂停执行,称为阻塞状态常见的情况包括等待IO操作、等待获取锁、等待其他线程的通知等(释放CPU资源)
等待(Waiting)线程执行wait()方法或类似方法后,线程进入等待状态此时线程会释放CPU资源并等待其他线程的通知,直到被唤醒
超时等待(Timed Waiting)线程执行sleep()、join()、LockSupport.parkNanos()等方法时,线程进入超时等待状态与等待状态类似,线程也会释放CPU资源,但在一定时间后会自动唤醒
结束(Terminated)线程执行完run()方法中的任务或者出现异常导致线程终止时,线程进入结束状态已经结束的线程不能再重新启动
1.2 线程优先级

优先级的设置和获取Thread类中的setPriority()getPriority()方法
优先级范围:1到10(1为最低优先级,10为最高优先级),默认情况下,线程的优先级与创建它的父线程的优先级相同

使用setPriority()方法设置线程的优先级:

Thread thread = new Thread();thread.setPriority(Thread.MAX_PRIORITY); // 设置线程的优先级为最高优先级
thread.setPriority(Thread.MIN_PRIORITY);
thread.setPriority(Thread.NORM_PRIORITY); // 默认优先级

使用getPriority()方法获取线程的优先级:

int priority = thread.getPriority(); // 获取线程的优先级

注意
1. 线程优先级仅提供一个指示给调度器的建议(不是高优先级的线程一定会先执行)
2. 具体的线程调度由操作系统和JVM决定(可能会受到系统负载、线程状态等因素的影)

  • 线程优先级
package thread;public class PriorityThread {public static void main(String[] args) {// 最大优先级Thread max = new Thread(){@Overridepublic void run() {for (int i = 0; i < 1000; i++) {System.out.println("max");}}};// 最小优先级Thread min = new Thread(){@Overridepublic void run() {for (int i = 0; i < 1000; i++) {System.out.println("min");}}};// 默认优先级Thread norm = new Thread(){@Overridepublic void run() {for (int i = 0; i < 1000; i++) {System.out.println("nor");}}};// 设置优先级max.setPriority(Thread.MAX_PRIORITY);min.setPriority(Thread.MIN_PRIORITY);max.start();norm.start();min.start();}
}

start()和run()方法
  start()方法:
      start()方法是Thread类中的一个方法,用于启动一个新的线程并执行其代码
      调用start()方法会创建一个新的线程,并在新线程中调用run()方法
在新线程中执行的代码由run()方法定义
  run()方法:
      run()方法也是Thread类中的一个方法,通常用于定义线程的任务或逻辑
      直接调用run()方法不会创建一个新的线程,而是在当前线程中以普通方法的方式执行run()方法的代码
区别
      start()方法会创建一个新的线程来执行任务,而run()方法在当前线程中执行
      调用start()方法后,线程会被添加到线程调度器的线程队列中,由线程调度器决定何时开始执行,而直接调用run()方法会立即开始执行
      使用start()方法可以充分利用多核处理器的并行能力,而直接调用run()方法则只能在单个线程中顺序执行

2. 多线程

线程控制
    sleep( ) : 线程休眠
    join( ) : 线程加入
    yield( ) : 线程礼让
    setDaemon( ) : 线程守护
中断线程
    stop( ): 线程停止
    interrupt( ):线程中断 (首先选用)

2.1 守护线程
守护线程 :不会阻止程序的终止,当所有非守护线程被终止时,守护线程也会被自动终止
临界资源:临界资源(Critical Resource)是指在多线程环境下被多个线程访问和操作的共享资源
常见临界资源
      多线程共享实例变量
      多线程共享静态公共变量
2.2 多线程高并发
高并发问题 :当多个线程并发操作同一临界资源,由于线程切换的时机不确定,导致执行顺序出现混乱产生不良后果
临界资源: 操作该资源的完整过程同一时刻只能被单个线程进行(被多个线程共享的资源)
常见临界资源
      多线程共享实例变量
      多线程共享静态公共变量
每个线程中对全局变量量、静态变量量只有读操作,⽽而⽆无写操作,一般来说,这个全局变量是线程安全的;
若有多个线程同时执行写操作,一般都需要考虑线程同步,否则的话就可能影响线程安全
  • 取款问题 利用多线程的并发间隙,在柜台和ATM机同时取款,寻找取款金额大于实际余额的情况
package thread;public class Bank {private int account = 20000;public synchronized boolean getMoney(int money) {int account = getAccount();if (account >= money) {account -= money;Thread.yield();saveAccount(account);return true;}return false;}public void saveAccount(int account) {this.account = account;}public int getAccount() {return account;}
}
package thread;public class BankTest {static boolean s1;static boolean s2;static int sum = 0;// 测试public static void main(String[] args) {Bank bank = new Bank();while (true) {Thread t1 = new Thread() {@Overridepublic void run() {s1 = bank.getMoney(20000);}};Thread t2 = new Thread() {@Overridepublic void run() {s1 = bank.getMoney(20000);}};t1.start();t2.start();try {Thread.sleep(5);} catch (InterruptedException e) {e.printStackTrace();}if (s1 && s2) {System.out.println("产生了漏洞,尝试了:" + sum + "次");break;} else {sum++;bank.saveAccount(20000);s1 = false;s2 = false;}}}
}
2.3 synchronized同步锁
  • 解决线程安全 —— 将异步操作转为同步操作
    • 异步操作:多线程并发,线程的执行互不干扰
    • 同步操作:线程有先后执行顺序,线程执行受到限制,排队等待执行

使用synchronized关键字实现线程之间的同步,同步锁

  • 使用语法:
    synchronized(同步监视器对象(必须是引用数据类型,否则会失效)){
      需要多个线程同步执行的代码片段;
    }

同步监视器对象的选取
  1. 所有需要同步执行同步块代码的线程看到的同步监视器对象必须是同一个
  2. 同步监视器对象只能是引用类型
合适的锁对象在满足锁基本条件
  1. 必须是引用类型
  2. 多个需要同步执行该代码块线程看到的必须是同一个对象
适当的同步有效性
  1. 存在并发安全时可以限制多个线程同步执行(发生并发安全时,多线程看到的该对象是同一个)
  2. 不存在不发安全时允许多个线程异步执行(不存在并发安全时,多线程看到的不应是同一个)
对成员方法(方法)同步时,同步监视器对象不可选,只能是this(当前对象)

  • synchronized同步锁
package thread;public class Synchronize {public static void main(String[] args) {Shop shop = new Shop();Shop shops = new Shop();Thread t1 = new Thread(shop::buy, "王文");Thread t2 = new Thread("李雯") {@Overridepublic void run() {shop.buy();}};Thread ts = new Thread(shops::buy, "王菲");t1.start();t2.start();// ts与t1和t2不是同一个店(不是同一个对象),不需要和t1t2排队(不需要同步)ts.start();}
}class Shop {// 同步方法
//    public synchronized void buy(){public void buy() {Thread t = Thread.currentThread();try {System.out.println(t.getName() + "正在挑衣服...");Thread.sleep(5000);// 添加所进行阻塞等待释放(缩小同步范围),同步代码块,this是合适的锁对象,不同的对象互不干扰synchronized (this) {// 引用数据类型(字面量,不适合使用)
//            synchronized ("上锁对象") {System.out.println(t.getName() + "正在试衣服");Thread.sleep(2000);}System.out.println(t.getName() + "结账离开!");} catch (InterruptedException e) {throw new RuntimeException(e);}}
}

静态方法使用了synchronized修饰时,一定具有同步效果

  1. 静态方法的调用:类名.方法名(),此时相当于同步锁锁的内容是相同的,执行同步操作
  2. 不同对象:对象1.方法名()
         对象2.方法名(),此时属于不同对象(常规多线程看到的不应是同一个,但是执行同步操作 —— 当做相同对象处理)

原因:
在synchronized修饰方法时,默认的同步锁对象为this,但是静态方法中没有this关键字,只能是类对象(class实例)
即静态方法的同步监视器对象:synchronized(类名.class) {}

  • 静态方法的synchronized同步锁
package thread;public class SynchronizeStatic {public static void main(String[] args) {Boo boo1 = new Boo();Boo boo2 = new Boo();Thread t1 = new Thread(Boo::doSome, "线程1 ");Thread t2 = new Thread(() -> Boo.doSome(), "线程2 ");Thread t3 = new Thread(() -> boo1.doSome(), "线程3 ");Thread t4 = new Thread(() -> boo2.doSome(), "线程4 ");t1.start();t2.start();}
}class Boo {//    public synchronized static void doSome(){public static void doSome() {synchronized (Boo.class) {try {Thread t = Thread.currentThread();System.out.println(t.getName() + "正在执行doSome方法");Thread.sleep(2000);System.out.println(t.getName() + "执行完毕");} catch (InterruptedException e) {throw new RuntimeException(e);}}}
}

在这里插入图片描述

  • StringBuilder线程不安全,StringBuffer线程安全
package thread;public class BuiderAndBuffer {public static void main(String[] args) {// StringBuilder线程不安全
//        StringBuilder builder = new StringBuilder();// StringBuffer线程安全StringBuffer builder = new StringBuffer();Thread t1 = new Thread() {@Overridepublic void run() {for (int i = 0; i < 1000; i++) {builder.append("aaa");}}};Thread t2 = new Thread() {@Overridepublic void run() {for (int i = 0; i < 1000; i++) {builder.append("bbb");}}};// 启动线程t1.start();t2.start();try {Thread.sleep(50);} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println("数组长度:" + builder.length());}
}
  • 集合ArrayList,LinkedList,HashSet等线程不安全,并发安全转换
package thread;import java.util.ArrayList;
import java.util.Collections;
import java.util.List;public class SyncSet {public static void main(String[] args) {// 集合ArrayList,HashSet等线程不安全
//        List<Integer> list = new ArrayList<>();// 将集合转为并发安全的(此时list不可改变,不可二次赋值)List<Integer> list = Collections.synchronizedList(new ArrayList<>());Thread t1 = new Thread(() -> {for (int i = 0; i < 1000; i++) {list.add(i);}});Thread t2 = new Thread(() -> {for (int i = 0; i < 1000; i++) {list.add(i);}});t1.start();t2.start();try {Thread.sleep(10);} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println(list.size());}
}
2.4 synchronized互斥锁

互斥性:指多个线程执行不同的代码片段,但是这些代码片段不能同一时刻被执行
互斥性使用synchronized实现:
    1. 使用synchronized锁定多个需要互斥的代码片段
    2. 这些synchronized指定的同步监视器必须同一个对象
例:用synchronized分别对两个不同的方法进行上锁,然后使用同一个对象分别调用两个方法

  • 互斥锁的实现
package thread;public class Mutex {public static void main(String[] args) {Foo f = new Foo();Thread t1 = new Thread(f::methodA);Thread t2 = new Thread(f::methodB);t1.start();t2.start();}
}class Foo{public synchronized void methodA(){try {Thread t = Thread.currentThread();System.out.println(t.getName() + ":正在执行A");Thread.sleep(5000);System.out.println(t.getName() + ":A执行完毕");} catch (InterruptedException e) {throw new RuntimeException(e);}}public synchronized void methodB(){try {Thread t = Thread.currentThread();System.out.println(t.getName() + ":正在执行B");Thread.sleep(3000);System.out.println(t.getName() + ":B执行完毕");} catch (InterruptedException e) {throw new RuntimeException(e);}}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/138836.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【网络协议】聊聊从物理层到MAC层 ARP 交换机

物理层 物理层其实就是电脑、交换器、路由器、光纤等。组成一个局域网的方式可以使用集线器。可以将多台电脑连接起来&#xff0c;然后进行将数据转发给别的端口。 数据链路层 Hub其实就是广播模式&#xff0c;如果A电脑发出一个包&#xff0c;B、C电脑也可以收到。那么数据…

[uni-app] canvas绘制圆环进度条

文章目录 需求参考链接基本问题的处理1:画布旋转的问题2:注意arc()的起始位置是3点钟方向3: 如果绘制1.9*Matn.PI的圆环, 要保证其实位置在0点方向?4:小线段怎么画, 角度怎么处理? 源码 需求 要绘制一个如此的进度条 参考链接 uni-app使用canvas绘制时间刻度以及不显示问…

4.Vue-Vue调用第三方接口

题记 用vue调用第三方接口&#xff0c;以下是全部代码和操作流程。 寻找第三方接口网站 推荐&#xff1a;免费API - 提供免费接口调用平台 (aa1.cn) 下面的代码以下图中的接口为例 安装axios模块 在终端输入以下命令&#xff1a; npm install axios 调用第三方接口代码 调…

从入门到进阶 之 ElasticSearch 节点配置 集群篇

&#x1f339; 以上分享 ElasticSearch 安装部署&#xff0c;如有问题请指教写。&#x1f339;&#x1f339; 如你对技术也感兴趣&#xff0c;欢迎交流。&#x1f339;&#x1f339;&#x1f339; 如有需要&#xff0c;请&#x1f44d;点赞&#x1f496;收藏&#x1f431;‍&a…

Spring框架(三)

1、代理模式&#xff1a; 二十三种设计模式中的一种&#xff0c;属于结构型模式。它的作用就是通过提供一个代理类&#xff0c;让我们在调用目标方法的时候&#xff0c;不再是直接对目标方法进行调用&#xff0c;而是通过代理类间接调用。让不属于目标方法核心逻辑的代码从目标…

如何让大模型自由使用外部知识与工具

本文将分享为什么以及如何使用外部的知识和工具来增强视觉或者语言模型。 全文目录&#xff1a; 1. 背景介绍 OREO-LM: 用知识图谱推理来增强语言模型 REVEAL: 用多个知识库检索来预训练视觉语言模型 AVIS: 让大模型用动态树决策来调用工具 技术交流群 建了技术交流群&a…

【灵动 Mini-G0001开发板】+Keil5开发环境搭建+ST-Link/V2程序下载和仿真+4颗LED100ms闪烁。

我们拿到手里的是【灵动 Mini-G0001开发板】 如下图 我们去官网下载开发板对应资料MM32G0001官网 我们需要下载Mini—G0001开发板的库函数与例程&#xff08;第一手学习资料&#xff09;Keil支持包&#xff0c; PCB文件有需要的&#xff0c;可以自行下载。用户指南需要下载&a…

Linux网络-UDP/TCP协议详解

Linux网络-UDP/TCP协议详解 2023/10/17 14:32:49 Linux网络-UDP/TCP协议详解 零、前言一、UDP协议二、TCP协议 1、应答机制2、序号机制3、超时重传机制4、连接管理机制 三次握手四次挥手5、理解CLOSE_WAIT状态6、理解TIME_WAIT状态7、流量控制8、滑动窗口 丢包问题9、拥塞控制…

探索音频传输系统:数字声音的无限可能 | 百能云芯

音频传输系统是一项关键的技术&#xff0c;已经在数字时代的各个领域中广泛应用&#xff0c;从音乐流媒体到电话通信&#xff0c;再到多媒体制作。本文将深入探讨音频传输系统的定义、工作原理以及在现代生活中的各种应用&#xff0c;以帮助您更好地了解这一重要技术。 音频传输…

接口加密解决方案:Python的各种加密实现!

01、前言 在现代软件开发中&#xff0c;接口测试已经成为了不可或缺的一部分。随着互联网的普及&#xff0c;越来越多的应用程序都采用了接口作为数据传输的方式。接口测试的目的是确保接口的正确性、稳定性和安全性&#xff0c;从而保障系统的正常运行。 在接口测试中&…

通过商品ID查询天猫商品详情数据,可以拿到商品标题,商品价格,商品库存,商品销量,商品sku数据等,天猫API接口

通过商品ID查询天猫商品详情数据可以用淘宝开放平台的淘宝客商品详情查询接口&#xff08;taobao.tbk.item.info.get&#xff09;来完成。 首先需要申请一个淘宝开放平台的应用&#xff0c;并获取到App Key和App Secret&#xff0c;然后使用淘宝开放平台的淘宝客商品详情查询接…

用友NC-Cloud uploadChunk 任意文件上传漏洞

一、漏洞描述 用友 NC Cloud&#xff0c;大型企业数字化平台&#xff0c; 聚焦数字化管理、数字化经营、数字化商业&#xff0c;帮助大型企业实现 人、财、物、客的全面数字化&#xff0c;从而驱动业务创新与管理变革&#xff0c;与企业管理者一起重新定义未来的高度。为客户提…