当前位置: 首页 > 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.lryc.cn/news/588260.html

相关文章:

  • 新型eSIM攻击技术可克隆用户资料并劫持手机身份
  • linux 内核: 访问当前进程的 task_struct
  • [论文阅读] 人工智能 + 软件工程 | 用大语言模型+排名机制,让代码评论自动更新更靠谱
  • android Perfetto cpu分析教程及案例
  • 迁移学习之图像预训练理解
  • ICML 2025 | 从语言到视觉,自回归模型VARSR开启图像超分新范式
  • C# TCP粘包与拆包深度了解
  • CSP-S 模拟赛 17
  • 哈希扩展 --- 海量数据处理
  • 一文明白AI、AIGC、LLM、GPT、Agent、workFlow、MCP、RAG概念与关系
  • Linux操作系统从入门到实战(七)详细讲解编辑器Vim
  • 螺旋模型:风险分析驱动的渐进式开发
  • C++卸载了会影响电脑正常使用吗?解析C++运行库的作用与卸载后果
  • 什么是实时数仓?实时数仓又有哪些应用场景?
  • 疯狂星期四 - 第7天运营日报
  • 多线程/协程环境时间获取的“时间片陷阱“:深度解析与工程级解决方案
  • 16.避免使用裸 except
  • Sharding-Sphere学习专题(一)基础认识
  • sshpass原理详解及自动化运维实践
  • xss-lab靶场通关
  • GD32/STM32嵌入CMSIS-DSP的库(基于Keil)
  • 系统思考:跨境跨界团队学习
  • 后端接口通用返回格式与异常处理实现
  • Flask服务器公外网访问,IPv6(亲测有效!!!!)
  • 8.数据库索引
  • vmware使用说明
  • XML vs JSON:核心区别与最佳选择
  • 如何基于FFMPEG 实现视频推拉流
  • win10安装Elasticsearch
  • 分享三个python爬虫案例