银行取款[多线程]{使用重入锁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,并且其他取款下,正确提示信息。

多线程访问安全保证!

时间: 2024-10-30 13:29:32

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

银行取款[多线程]{使用volatile修饰共享变量,但此场景并不保证线程同步}

经典例子:老婆(朱丽叶)老公(罗密欧),使用银行卡和存折,或者网银等,同时对同一账户操作的安全问题. 此处用多线程实现,同时取款的模拟实现,使用volatile修饰共享变量,但此场景并不保证线程同步,查看取款安全隐患问题,代码如下: 我学习地址(Thanks for auther): Java 理论与实践: 正确使用 Volatile 变量 java中volatile关键字的含义 ----------------------------------------------------------

银行取款[多线程]{使用ThreadLocal管理共享变量,但此场景并不保证线程同步}

经典例子:老婆(朱丽叶)老公(罗密欧),使用银行卡和存折,或者网银等,同时对同一账户操作的安全问题. 此处用多线程实现,同时取款的模拟实现,使用ThreadLocal管理共享变量,但此场景并不保证线程同步,查看取款安全隐患问题,代码如下: ----------------------------------------------------------------------------------------------------------------------------------

银行取款[多线程]{使用同步方法确保线程同步}

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

银行取款[多线程]{使用同步代码块确保线程同步}

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

《Java并发编程从入门到精通》显示锁Lock和ReentrantLock

作者:张振华    购买链接:天猫商城  JD商城  当当书店   显示锁Lock和ReentrantLock Lock是一个接口提供了无条件的.可轮询的.定时的.可中断的锁获取操作,所有加锁和解锁的方法都是显式的.包路径是:java.util.concurrent.locks.Lock.核心方法是lock(),unlock(),tryLock(),实现类有ReentrantLock, ReentrantReadWriteLock.ReadLock, ReentrantReadWriteLock

银行取款[多线程]{未进行线程同步}(junit不适合多线程并发单元测试)

        由于计算机多任务.多进程.多线程的支持,使得计算机资源的服务效率提高,服务器对请求的也使用线程来相应,所有,代码中涉及到同时对共享数据的操作,将在 多线程环境中操作数据,导致数据安全问题.      经典例子:老婆(朱丽叶)老公(罗密欧),使用银行卡和存折,或者网银等,同时对同一账户操作的安全问题.      如果要保证多线程下数据安全,就要实现线程同步(例如:一间小厕所,就得有一个锁,保证同一时间为一个人服务).其他文章讲: 此处用多线程实现,同时取款的模拟实现,未进行线程同步

Linux多线程可重入函数

Reentrant和Thread-safe 在单线程程序中,整个程序都是顺序执行的,一个函数在同一时刻只能被一个函数调用,但在多线程中,由于并发性,一个函数可能同时被多个函数调用,此时这个函数就成了临界资源,很容易造成调用函数处理结果的相互影响,如果一个函数在多线程并发的环境中每次被调用产生的结果是不确定的,我们就说这个函数是"不可重入的"/"线程不安全"的.为了解决这个问题,POSIX多线程库提出了一种机制,用来解决多线程环境中的线程数据私有化问题,这套机制的主要

java 可重入读写锁 ReentrantReadWriteLock 详解

读写锁 ReadWriteLock读写锁维护了一对相关的锁,一个用于只读操作,一个用于写入操作.只要没有writer,读取锁可以由多个reader线程同时保持.写入锁是独占的.互斥锁一次只允许一个线程访问共享数据,哪怕进行的是只读操作:读写锁允许对共享数据进行更高级别的并发访问:对于写操作,一次只有一个线程(write线程)可以修改共享数据,对于读操作,允许任意数量的线程同时进行读取.与互斥锁相比,使用读写锁能否提升性能则取决于读写操作期间读取数据相对于修改数据的频率,以及数据的争用--即在同一

深入理解可重入与线程安全

在多线程编程和信号处理过程中,经常会遇到可重入(reentrance)与线程安全(thread-safe). 很多人纠结于reentrance和thread-safe两个概念理解纠缠不清.我想救我对reentrance和thread-safe的理解作个总结   一.可重入(reentrance) 首先来看下APUE中,列出的可重入函数:   accept fchmod lseek sendto stat access fchown lstat setgid symlink aio_error f