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

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

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

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.xdnf.cn/news/15332.html

相关文章:

  • 学习开发之无参与有参
  • 【操作系统】strace 跟踪系统调用(一)
  • 删除screen会话以及查看进程信息的方法
  • DAY02:【ML 第一弹】KNN算法
  • Vue3 实现文件上传功能
  • 完整 Spring Boot + Vue 登录系统
  • EtherCAT开源主站 SOEM 2.0 最新源码在嵌入式 Linux 下的移植与编译
  • 【读书笔记】《C++ Software Design》第九章:The Decorator Design Pattern
  • LeetCode 1156.单字符重复子串的最大长度
  • 代码部落 20250713 CSP-J复赛 模拟赛
  • 婚后才明白,原来结婚真需要一点冲动!
  • 时序预测 | Matlab代码实现VMD-TCN-GRU-MATT变分模态分解时间卷积门控循环单元多头注意力多变量时序预测
  • (一)SAP Group Reporting (GR) 集团财务合并解决方案套件概述
  • java 基本数据类型所对应的包装类
  • 暑期自学嵌入式——Day01(C语言阶段)
  • C++中顶层const与底层const
  • 【开源项目】网络诊断告别命令行!NetSonar:开源多协议网络诊断利器
  • 【研报复现】开源证券:均线的收敛与发散
  • 从 Manifest V2 升级到 Manifest V3:常见问题与解决方案
  • exe文件图标修改器 - exe图标提取器(ico、png) - 修改360文件夹的图标为windows自带的图标
  • # 通过wifi共享打印机只有手动翻页正反打印没有自动翻页正反打印,而通过网线连接的主机电脑可以自动翻页正反打印
  • 设计模式:软件开发的高效解决方案(单例、工厂、适配器、代理)
  • 预处理器完整功能介绍和示例演示(LESS/SCSS)
  • DMDIS文件到数据库
  • 并查集 UnionFind Test01
  • 什么是RAG(Retrieval-Augmented Generation)?一文读懂检索增强生成
  • websocket连接时发生未知错误
  • SAP顾问职位汇总(第28周)
  • 快速生成 Android 的 Splash 的 9 Patch 图片
  • phpMyAdmin:一款经典的MySQL在线管理工具又回来了