首页 > 编程语言 >【Java 并发】线程同步

【Java 并发】线程同步

时间:2024-01-06 23:22:07浏览次数:34  
标签:线程 Java Thread 并发 amount accounts fromUser new

目录

线程同步

条件对象

通常线程进入临界区,却发现需要满足某一个条件后,才能继续执行,这时,就需要使用一个条件对象,来管理那些已经获得了一个锁,但是,却不做有用工作的线程。这些条件对象经常被称为条件变量(Conditional Variable)

一个锁对象可以关联一个或者多个相关的条件对象,我们可以使用锁对象的 newCondition() 方法获得一个条件对象。

一旦一个线程调用条件对象的 await() 方法,它将进入该条件的等待集,它将一直阻塞,直到有另外的线程调用 signalAll() 方法为止。

以下面的银行转账为例,转账操作需要加锁控制,当某一个用户转账时,需要对余额条件进行判断,不满足转出金额,就需要等待有进账金额时,才能转出。

【代码实现】

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class Bank {
    private final double[] accounts;
    private final Lock bankLock;
    private final Condition sufficientFunds;

    Bank(double[] accounts) {
        this.accounts = accounts;
        bankLock = new ReentrantLock();
        sufficientFunds = bankLock.newCondition(); // 创建一个锁的条件对象
    }

    public void transfer(int fromUser, int toUser, double amount) throws InterruptedException {
        bankLock.lock();
        try {
            while (accounts[fromUser] < amount) {
                System.out.println(Thread.currentThread().getName() + " will be waiting");
                sufficientFunds.await(); // 阻塞,直到其他线程调用 signalAll() 方法为止
                System.out.println(Thread.currentThread().getName() + " is awake now");
            }
            System.out.println(Thread.currentThread().getName() + ": [" + fromUser + "] = " + accounts[fromUser] +
                    ", [" + toUser + "] = " + accounts[toUser]);
            accounts[fromUser] -= amount;
            accounts[toUser] += amount;
            System.out.println(Thread.currentThread().getName() + ": [" + fromUser + "] -> [" + toUser + "]: " + amount);
            sufficientFunds.signalAll();
        } finally {
            bankLock.unlock();
        }
    }
}

测试代码如下:

public class BankTest {
    public static void main(String[] args) throws InterruptedException {
        Bank bank = new Bank(new double[]{1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000, 9000});

        Thread t1 = new Thread(() -> {
            try {
                bank.transfer(0, 1, 2000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        });

        Thread t2 = new Thread(() -> {
            try {
                bank.transfer(2, 0, 3000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        });

        t1.start();
        Thread.sleep(5000);
        t2.start();
    }
}

运行结果如下:

Thread-0 will be waiting
Thread-1: [2] = 3000.0, [0] = 1000.0
Thread-1: [2] -> [0]: 3000.0
Thread-0 awake now
Thread-0: [0] = 4000.0, [1] = 2000.0
Thread-0: [0] -> [1]: 2000.0

synchronized 关键字

Java 中的每一个对象都有一个内部锁,如果一个方法使用 synchronized 关键字声明,那么,对象的锁将保护整个方法。

同时,内部对象锁只有一个相关条件,即

  • wait():方法添加一个线程到等待集;

  • notifyAll() 或者 notify() 方法:解除等待阻塞状态

由于 wait、notify、notifyAll 是 Object 类的 final 方法,所以,Condition 类中对应的方法为了避免命名冲突,所以命名为了 await、signal、signalAll。

public class BankV2 {
    private final double[] accounts;

    BankV2(double[] accounts) {
        this.accounts = accounts;
    }

    public synchronized void transfer(int fromUser, int toUser, double amount) throws InterruptedException {
        while (accounts[fromUser] < amount) {
            System.out.println(Thread.currentThread().getName() + " will be waiting");
            this.wait(); // 阻塞,直到其他线程调用 notifyAll() 方法为止
            System.out.println(Thread.currentThread().getName() + " is awake now");
        }
        System.out.println(Thread.currentThread().getName() + ": [" + fromUser + "] = " + accounts[fromUser] +
                ", [" + toUser + "] = " + accounts[toUser]);
        accounts[fromUser] -= amount;
        accounts[toUser] += amount;
        System.out.println(Thread.currentThread().getName() + ": [" + fromUser + "] -> [" + toUser + "]: " + amount);
        this.notifyAll(); // 唤醒等待的线程
    }
}

测试代码如下:

public class BankTest {
    public static void main(String[] args) throws InterruptedException {
        BankV2 bank = new BankV2(new double[]{1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000, 9000});

        Thread t1 = new Thread(() -> {
            try {
                bank.transfer(0, 1, 2000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        });

        Thread t2 = new Thread(() -> {
            try {
                bank.transfer(2, 0, 3000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        });

        t1.start();
        Thread.sleep(5000);
        t2.start();
    }
}

运行结果如下:

Thread-0 will be waiting
Thread-1: [2] = 3000.0, [0] = 1000.0
Thread-1: [2] -> [0]: 3000.0
Thread-0 awake now
Thread-0: [0] = 4000.0, [1] = 2000.0
Thread-0: [0] -> [1]: 2000.0

监视器

volatile

标签:线程,Java,Thread,并发,amount,accounts,fromUser,new
From: https://www.cnblogs.com/larry1024/p/17949892

相关文章

  • Java之转换流的详细解析
     2.转换流2.1字符编码和字符集字符编码计算机中储存的信息都是用二进制数表示的,而我们在屏幕上看到的数字、英文、标点符号、汉字等字符是二进制数转换之后的结果。按照某种规则,将字符存储到计算机中,称为编码。反之,将存储在计算机中的二进制数按照某种规则解析显示出来,称为解码......
  • Java线程同步机制
    第1章:引言大家好,我是小黑。今天咱们来聊聊并发编程,咱们经常听说并行、并发这些词,特别是在处理大量数据、高用户负载时,这些概念就显得尤为重要了。为什么呢?因为并发编程可以帮助咱们的应用程序更有效地使用计算资源,处理更多任务,提升性能。为什么要同步线程呢?想象一下,如果有多个......
  • Java并发集合详解
    第1章:引言大家好,我是小黑,在这篇博客中,咱们将一起深入探索Java中的并发集合。多线程编程是一个不可或缺的部分,它能让程序运行得更快,处理更多的任务。但同时,多线程也带来了一些挑战,尤其是在数据共享和同步方面。为了解决这些挑战,Java提供了一系列的并发集合,这些集合为处理并发数据......
  • 设计模式Java实战,彻底学会
    这是全网最强的Java设计模式实战教程。此教程用实际项目场景,结合SpringBoot,让你真正掌握设计模式。网址是:Java设计模式实战专栏介绍-自学精灵(也可以百度搜索“自学精灵”)。本设计模式专栏的威力用Java实战来介绍常用的设计模式,让你真正掌握设计模式。用项目实际场景进行设计模式......
  • JAVA - stream流汇总,求和,分组等
    求和(Sum)示例代码如下所示:List<Integer>numbers=Arrays.asList(1,2,3,4,5);intsum=numbers.stream().mapToInt(Integer::valueOf).sum();1.System.out.println("数字列表的和为:"+sum);2.分组(Grouping)示例代码如下所示:List<String>fruits=Arrays.asList(&qu......
  • Java智慧工地可视化APP信息管理平台源码
    智慧工地信息化解决方案、智慧工地信息管理平台智慧工地系统以推进施工过程管理信息化、数字化、智慧化为手段,促进第五代通信技术(5G)、大数据、智能设备、人工智能等与建筑工程管理进一步融合。智慧化工地建设全面加速,以数字技术助力建筑工地转型升级、提速增效、提档升级的成......
  • 基于Java的学习交流论坛
    选题目的、意义网络信息技术的发展速度之快,在各个方面上影响着人们生活和工作的方式,并且深深的改造了人与人之间交流的方式、学习的方式乃至日常思维方式。伴随着网络信息技术、多媒体技术、数据库技术以及虚拟现实技术在各个区域中的运用都不断的发生改革,基于web的论坛的沟通交流......
  • 高并发下 MySQL Statement Cancellation Timer 的线程数暴涨
    微信公众号:运维开发故事作者:老郑问题描述线上业务高峰期CPU飙升,抓取threaddump发现 MySQLStatementCancellationTimer 的线程数比较多,接收到线上预警,分析一下原因。业务高峰:下面是一些可能相关的信息( mysql驱动,db连接池,orm框架)依赖信息:mysql-jdbc8.0.24druid1.2.8m......
  • 基于Java的学生信息管理系统
    现代经济快节奏发展以及不断完善升级的信息化技术,让传统数据信息的管理升级为软件存储,归纳,集中处理数据信息的管理方式。本学生信息管理系统就是在这样的大环境下诞生,其可以帮助管理者在短时间内处理完毕庞大的数据信息,使用这种软件工具可以帮助管理人员提高事务处理效率,达到事半功......
  • JVM2(线程)
    (1)线程这里所说的线程指程序执行过程中的一个线程实体。JVM允许一个应用并发执行多个线程。HotspotJVM中的Java线程与原生操作系统线程有直接的映射关系。当线程本地存储、缓冲区分配、同步对象、栈、程序计数器等准备好以后,就会创建一个操作系统原生线程。Java线程结束,原......