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

【安卓笔记】线程基本使用:锁、锁案例

前言:线程的基础知识。请查看我上一篇文章

0. 环境:

电脑:Windows10

Android Studio: 2024.3.2

编程语言: Java

Gradle version:8.11.1

Compile Sdk Version:35

Java 版本:Java11

1. 锁:类锁、对象锁、显示锁

类锁

常用的锁:synchronized

JDK内置锁,常用于单例模式

代码示例:

//方式一,比较消耗资源。每一次线程调用get方法时,都要判断锁
public static synchronized GpsEngine getGpsEngine() {if (gpsEngine == null) {gpsEngine = new GpsEngine();}return gpsEngine;
}
//方式二,懒加载方式,比较推荐
public static GpsEngine getGpsEngine() {if (gpsEngine == null) {synchronized (GpsEngine.class) {if (gpsEngine == null) {gpsEngine = new GpsEngine();}}}return gpsEngine;
}

对象锁

示例代码:

package com.liosen.lib;public class CountTest {private int count = 0;//自增函数1,不带锁public void increaseCount1() {count++;}// 自增函数2,带对象锁public synchronized void increaseCount2() {count++;}// 自增函数3,带对象锁。与自增函数2一样public void increaseCount3() {synchronized (CountTest.this) {count++;}}public static void main(String[] args) throws InterruptedException {CountTest ct = new CountTest();CountThread thread1 = new CountThread(ct);CountThread thread2 = new CountThread(ct);thread1.start(); // count 理论上自增到10000thread2.start(); // count 理论上自增到20000Thread.sleep(50);// 不加这行,会导致result为0. 打印的行为,在自增行为之前/*** 如果执行了increaseCount1(),会发现打印出来的count不确定,理论上为10000~20000之间浮动。(我浮动在13000~15000之间)* 如果执行了increaseCount2(),打印出来的count确定为20000* 如果执行了increaseCount3(),打印出来的count确定为20000*/System.out.println("count result : " + ct.count);}private static class CountThread extends Thread {private CountTest ct;public CountThread(CountTest ct) {this.ct = ct;}@Overridepublic void run() {super.run();// 自增10000次for (int i = 0; i < 10000; i++) {ct.increaseCount1();
//                ct.increaseCount2();
//                ct.increaseCount3();}}}
}

显示锁

package com.liosen.lib;import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;public class LockDemo {private int count = 0;//自增函数1,不带锁public void increaseCount1() {count++;}private Lock lock = new ReentrantLock(); // ReentrantLock可重入锁。可重入锁:递归时可以重新进入public void increaseCount2() {lock.lock();try {count++;// 模拟逻辑代码} catch (Exception e) {e.printStackTrace();} finally {lock.unlock(); // 保证解锁一定能执行。避免逻辑代码报错后,increaseCount2锁死}}public static void main(String[] args) throws InterruptedException {LockDemo ld = new LockDemo();CountThread thread1 = new CountThread(ld);CountThread thread2 = new CountThread(ld);thread1.start(); // count 理论上自增到10000thread2.start(); // count 理论上自增到20000Thread.sleep(50);// 不加这行,会导致result为0./*** 如果执行了increaseCount1(),会发现打印出来的count不确定,理论上为10000~20000之间浮动。(我浮动在13000~15000之间)* 如果执行了increaseCount2(),打印出来的count确定为20000*/System.out.println("count result : " + ld.count);}private static class CountThread extends Thread {private LockDemo ld;public CountThread(LockDemo ld) {this.ld = ld;}@Overridepublic void run() {super.run();// 自增10000次for (int i = 0; i < 10000; i++) {
//                ld.increaseCount1();ld.increaseCount2();}}}
}

以上就表示完三种锁。如果看过我上一篇文章,这部分代码应该很好理解。

2. 锁的案例演示(等待、唤醒 机制)

上篇文章中,我们实现了 线程A执行完后,再执行线程B,使用到join()函数。

现在有个新需求,线程A和线程B依次交替执行。

模拟情景:生产一件商品后,立即消费售卖一件商品。

我们可以使用 wait()和notify()函数来实现。

实现代码如下:

package com.liosen.lib;/*** 该demo为了做到,生产一件商品后,消费一件商品。* 思路,执行生产商品的线程后,唤醒消费的线程;执行消费商品的线程后,唤醒生产的线程。* 即 生产的线程 和 消费的线程 之间切换。*/
class Res { // 商品资源属性public int count; // 商品数量public int produceCount; // 已生产的数量public int consumeCount; // 已消费的数量private boolean flag; // 用于标记运行,先生产 --> 后消费。可以理解成,当前是否有商品// 生产一件商品public synchronized void put() {if (!flag) {count += 1;produceCount++;System.out.println("produce one, total is: "+ count + "; produceCount: " + produceCount + "<-------------");flag = true;}/*** notify()的目的是,唤醒另一个wait()。如果没有wait()的线程,默认不处理。*/notify(); // 必须在锁内执行try {/*** 等待消费线程执行,所以此时需要wait等待*/wait();} catch (InterruptedException e) {throw new RuntimeException(e);}}//取出商品来售卖public synchronized void getAndSell() {if (flag) {count -= 1;consumeCount++;System.out.println("------------->consume one, total is :" + count + "; consumeCount: " + consumeCount + "\n");flag = false;}// ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ 与上面put()中 一样的意思 ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓notify();try {wait();} catch (InterruptedException e) {throw new RuntimeException(e);}// ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑}
}class ProduceRunnable implements Runnable {private Res res;public ProduceRunnable(Res res) {this.res = res;}@Overridepublic void run() {// 假设生产20个商品for (int i = 0; i < 20; i++) {res.put();}}
}class ConsumeRunnable implements Runnable {private Res res;public ConsumeRunnable(Res res) {this.res = res;}@Overridepublic void run() {// 假设消费20个商品for (int i = 0; i < 20; i++) {res.getAndSell();}}
}public class ThreadCommunicationDemo {public static void main(String[] args) {Res res = new Res();// 创建生产任务ProduceRunnable produceRunnable = new ProduceRunnable(res);// 创建消费任务ConsumeRunnable consumeRunnable = new ConsumeRunnable(res);Thread produceThread = new Thread(produceRunnable);Thread cusumeThread = new Thread(consumeRunnable);produceThread.start();cusumeThread.start();}
}

注:wait()函数会给当前锁解锁。所以线程A和线程B交替执行时,即使加锁了,另外一个线程也可以进入,就是因为wait()解锁了。

注2:notify()唤醒存在不确定性。如果线程超过2,可能会唤醒其他wait()。所以需要看你的逻辑代码如何实现的。

注3:如果需要唤醒所有wait(),可以使用notifyAll()

3. 写在最后

至此,我们就新学会了两个线程交替执行。

http://www.dtcms.com/a/279561.html

相关文章:

  • Windows安装postgreSQL(保姆级教程)
  • 机床自动化中的“方言翻译官”:EtherNet/IP 转 PROFIBUS DP 实战手记
  • 安全初级(一)
  • 胡志明证券交易所新一代交易系统解决方案——基于美联储利率决议背景下的越南跨境金融基础设施升
  • pycharm恢复出厂设置,可以解决大多数pycharm存在的问题
  • nginx:SSL_CTX_use_PrivateKey failed
  • 怎么 将训练后的词嵌入向量反编译为自然语言
  • AI多因子模型解析白银14年新高:流动性压力与工业避险需求的联动效应
  • 数字化工厂规划-项目启动会汇报材料编写思路
  • Android Studio C++/JNI/Kotlin 示例 二
  • 三相新能源并网系统序阻抗模型——序阻抗分析器IMAnalyzer
  • Docker部署语音转文字(STT)服务并接入Home Assistant
  • linux服务器redis配置开机自启
  • 2025 R3CTF
  • 我的开源项目-AI Agent 配置系统
  • 技嘉UEFI固件SMM漏洞使系统面临固件植入和持久控制风险
  • Oracle 学习笔记
  • 【工具变量】A股上市公司产学研合作及专利数据统计(1998-2023年)
  • TextIn:文档全能助手,让学习效率飙升的良心软件~
  • 《汇编语言:基于X86处理器》第7章 复习题和练习,编程练习
  • RAG索引流程中的文档解析:工业级实践方案与最佳实践
  • SMTPman,发送邮件服务器smtp怎么填才行?
  • 鹧鸪云:别墅光储项目方案设计的最终选择
  • 面试150 二叉树中的最大路径和
  • 水务工程中自动化应用:EtherNet/IP转PROFIBUS DP连接超声波流量计
  • 9.服务容错:构建高可用微服务的核心防御
  • Go泛型完全指南:从基础到实战应用
  • 【深度学习笔记】2 浅层神经网络
  • 【Golang】GORM - GEN工具 快速开始
  • Go迭代器完全指南:从基础到实战