银行取款[多线程]{使用重入锁Lock接口ReentrantLock锁确保线程同步}

简介: 经典例子:老婆(朱丽叶)老公(罗密欧),使用银行卡和存折,或者网银等,同时对同一账户操作的安全问题。  此处用多线程实现,同时取款的模拟实现,使用使用Lock接口ReentrantLock锁确保线程同步,查看取款安全隐患问题,代码如下: ---------------------------------------------------------------------------
经典例子:老婆(朱丽叶)老公(罗密欧),使用银行卡和存折,或者网银等,同时对同一账户操作的安全问题。

 此处用多线程实现,同时取款的模拟实现,使用使用Lock接口ReentrantLock锁确保线程同步,查看取款安全隐患问题,代码如下:

------------------------------------------------------------------------------------------------------------------------------------------------

 * 线程同步 :使用重入锁ReentrantLock锁,代码编写,实现线程同步
 * ReentrantLock 拥有Synchronized相同的并发性和内存语义(ReentrantLock可实现Synchronized的所有功能,有更精确的线程语义和更好的性能)
 * synchronized是在JVM层面上实现的,不但可以通过一些监控工具监控synchronized的锁定,而且在代码执行时出现异常,JVM会自动释放锁定;
 * Lock不会自动释放锁,lock是通过代码实现的,要保证锁定一定会被释放,就必须将unlock()放到finally{}中
 * 在JavaSE5.0中新增了一个java.util.concurrent包来支持同步。ReentrantLock类是可重入、互斥、实现了Lock接口的锁,它与使用synchronized方法和快具有相同的基本行为和语义,并且扩展了其能力
 * 适用:在资源竞争不是很激烈的情况下,Synchronized的性能要优于ReetrantLock
 *        在资源竞争很激烈的情况下,Synchronized的性能会下降几十倍,但是ReetrantLock的性能能维持常态
 *ReentrantLock()还有一个可以创建公平锁的构造方法,但由于能大幅度降低程序运行效率,不推荐使用
 *ReentrantLock在传递锁等锁中,例如"手拉手",更适用。

------------------------------------------------------------------------------------------------------------------------------------------------

private void makeWithdraw(int amount){
  //add lock
  lock.lock();
  try {
   //code
  } catch (InterruptedException e) {
   //code
  }finally{
   //release lock must in the finally
   lock.unlock();
  }
}

银行账户:

package com.tsxs.bank;

public class BankAccount {
	//余额
	private int balance = 500;
	//查询
	public int getBalance(){
		return banlance;
	}
	//取款
	public void withdraw(int amount){
		banlance = banlance - amount;
	}
	//存款
	public void deposit(int amount){
		banlance = banlance + amount;
	}
}

使用ReentrantLock锁,代码: 

package com.tsxs.syncmethods;

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

import com.tsxs.bank.BankAccount;
/**
 * 此线程类实现Runnable接口<br />
 * 线程同步 :使用ReentrantLock锁,代码编写,实现线程同步<br />
 * ReentrantLock 拥有Synchronized相同的并发性和内存语义(ReentrantLock可实现Synchronized的所有功能,有更精确的线程语义和更好的性能)。<br />
 * synchronized是在JVM层面上实现的,不但可以通过一些监控工具监控synchronized的锁定,而且在代码执行时出现异常,JVM会自动释放锁定;
 * Lock不会自动释放锁,lock是通过代码实现的,要保证锁定一定会被释放,就必须将unlock()放到finally{}中。<br />
 *适用:在资源竞争不是很激烈的情况下,Synchronized的性能要优于ReetrantLock.
 *        在资源竞争很激烈的情况下,Synchronized的性能会下降几十倍,但是ReetrantLock的性能能维持常态;<br />
 *Lock在传递锁等锁中,例如"手拉手",更适用。
 * */
public class CodeLock implements Runnable{
	//所有Thread多线程线程都共享Runnable(接口对象)和account对象
	private BankAccount account = new BankAccount();
	//声明锁,jdk1.5
    Lock lock = new ReentrantLock();
	@Override
	public void run() {
		for(int i = 0; i< 5; i++){			//总共取款5次
			makeWithdraw(100);			//每次取款100
			if(account.getBalance() < 0){
				System.out.println(""+Thread.currentThread().getName()+"   透支了!");
			}
		}
	}
	
	/**
	 * makeWithdraw 账户取款
	 * @param amount 取款金额<br />
	 * 打印log记录取款过程
	 * */
	private void makeWithdraw(int amount){
		//add lock
		lock.lock();
		try {
			if(account.getBanlance() >= amount){			//如果余额足够则取款
				System.out.println(""+Thread.currentThread().getName()+"   准备取款!");
				Thread.sleep(500);
				account.withdraw(amount);
				System.out.println(""+Thread.currentThread().getName()+"   完成"+amount+"取款,余额为"+account.getBalance());			}else{			//余额不足则提示
				System.out.println(""+"余额不足以支付"+Thread.currentThread().getName()+amount+"   的取款,余额为"+account.getBalance());
			}
		} catch (InterruptedException e) {
			System.out.println(Thread.currentThread().getName()+"   准备取款,等待0.5s线程中断!"+e.getMessage());
		}finally{
			//release lock must in the finally
			lock.unlock();
		}
	}
}

测试代码:

package com.tsxs.test;

import org.junit.Test;

import com.tsxs.syncmethods.CodeLock;
import com.tsxs.syncmethods.NoSync;
import com.tsxs.syncmethods.SyncBlock;
import com.tsxs.syncmethods.SyncMethod;

public class TreadSyncTest {

//	@Test
//	public void test() {
/*Junit不适合多线程并发测试。
    因为线程还在激活状态的时候,Junit已经执行完成。
	在Junit的TestRunner中,它没有被设计成搜寻Runnable实例,
	并且等待这些线程发出报告,它只是执行它们并且忽略了它们的存在。
	综上,不可能在Junit中编写和维护多线程的单元测试。
}*/	
	public static void main(String[] args) {
		//实现Runnable:所有Thread多线程线程都共享Runnable(接口对象)
//		NoSync target =new NoSync();
//		SyncMethod target = new SyncMethod();
//		SyncBlock target = new SyncBlock();
		CodeLock target = new CodeLock();
		//创建李琦和他老婆两个线程实现取款(同时)
		Thread lq = new Thread(target);
		lq.setName("罗密欧");
		Thread lqwf = new Thread(target);
		lqwf.setName("朱丽叶");
		//调用Thread对象的start()方法,启动线程,执行run()方法(OS)
		lq.start();
		lqwf.start();
	}
}


 测试结果:

罗密欧   准备取款!
罗密欧   完成100取款!余额为400
罗密欧   准备取款!
罗密欧   完成100取款!余额为300
朱丽叶   准备取款!
朱丽叶   完成100取款!余额为200
朱丽叶   准备取款!
朱丽叶   完成100取款!余额为100
朱丽叶   准备取款!
朱丽叶   完成100取款!余额为0
余额不足以支付朱丽叶100   的取款,余额为0
余额不足以支付朱丽叶100   的取款,余额为0
余额不足以支付罗密欧100   的取款,余额为0
余额不足以支付罗密欧100   的取款,余额为0
余额不足以支付罗密欧100   的取款,余额为0

分析结果:

双线程总共取款10次,账户总额为500.

取款结果:在多线程访问下,成功取款总额为500,并且其他取款下,正确提示信息。

多线程访问安全保证!

目录
相关文章
|
10天前
|
存储 Java 数据库连接
java多线程之线程通信
java多线程之线程通信
|
21天前
|
存储 缓存 NoSQL
Redis单线程已经很快了6.0引入多线程
Redis单线程已经很快了6.0引入多线程
31 3
|
23天前
|
消息中间件 安全 Linux
线程同步与IPC:单进程多线程环境下的选择与权衡
线程同步与IPC:单进程多线程环境下的选择与权衡
57 0
|
1天前
|
安全 Java 调度
Java并发编程:深入理解线程与锁
【4月更文挑战第18天】本文探讨了Java中的线程和锁机制,包括线程的创建(通过Thread类、Runnable接口或Callable/Future)及其生命周期。Java提供多种锁机制,如`synchronized`关键字、ReentrantLock和ReadWriteLock,以确保并发访问共享资源的安全。此外,文章还介绍了高级并发工具,如Semaphore(控制并发线程数)、CountDownLatch(线程间等待)和CyclicBarrier(同步多个线程)。掌握这些知识对于编写高效、正确的并发程序至关重要。
|
5天前
|
Java 程序员 编译器
Java中的线程同步与锁优化策略
【4月更文挑战第14天】在多线程编程中,线程同步是确保数据一致性和程序正确性的关键。Java提供了多种机制来实现线程同步,其中最常用的是synchronized关键字和Lock接口。本文将深入探讨Java中的线程同步问题,并分析如何通过锁优化策略提高程序性能。我们将首先介绍线程同步的基本概念,然后详细讨论synchronized和Lock的使用及优缺点,最后探讨一些锁优化技巧,如锁粗化、锁消除和读写锁等。
|
11天前
|
Java
Java中的多线程实现:使用Thread类与Runnable接口
【4月更文挑战第8天】本文将详细介绍Java中实现多线程的两种方法:使用Thread类和实现Runnable接口。我们将通过实例代码展示如何创建和管理线程,以及如何处理线程同步问题。最后,我们将比较这两种方法的优缺点,以帮助读者在实际开发中选择合适的多线程实现方式。
19 4
|
13天前
|
安全 Java 调度
深入理解Java中的线程安全与锁机制
【4月更文挑战第6天】 在并发编程领域,Java语言提供了强大的线程支持和同步机制来确保多线程环境下的数据一致性和线程安全性。本文将深入探讨Java中线程安全的概念、常见的线程安全问题以及如何使用不同的锁机制来解决这些问题。我们将从基本的synchronized关键字开始,到显式锁(如ReentrantLock),再到读写锁(ReadWriteLock)的讨论,并结合实例代码来展示它们在实际开发中的应用。通过本文,读者不仅能够理解线程安全的重要性,还能掌握如何有效地在Java中应用各种锁机制以保障程序的稳定运行。
|
17天前
|
安全 Java 容器
Java并发编程:实现高效、线程安全的多线程应用
综上所述,Java并发编程需要注意线程安全、可见性、性能等方面的问题。合理使用线程池、同步机制、并发容器等工具,可以实现高效且线程安全的多线程应用。
14 1
|
1月前
|
Java 调度 C#
C#学习系列相关之多线程(一)----常用多线程方法总结
C#学习系列相关之多线程(一)----常用多线程方法总结
|
1月前
|
安全 编译器 C#
C#学习相关系列之多线程---lock线程锁的用法
C#学习相关系列之多线程---lock线程锁的用法