`
中南大宝
  • 浏览: 34575 次
  • 性别: Icon_minigender_1
  • 来自: 长沙
社区版块
存档分类
最新评论

“锁”这玩意

阅读更多

       Java中的“锁”经常用于处理多线程编程中不同线程对同一个变量进行处理时造成的不同步问题。

 

       举个例子,如果一个Boy和他的Girl Friend分别持有同一账户的银行卡和存折,如果银行的后台取款程序没有解决好同步问题,那么就可能出现,Boy在A地,Girl在B地,两人同时取款,虽然账户中仅有1000元,可是因为程序的不同步,两人可以取得到2000元(每人分别取了1000)。当然,现实中,这样的情况是不会出现的,因为在银行的系统中,一个时间段,只会允许一个人操作,当他操作完后,账户的金额也就实时更新了,不会出现让银行自己亏钱的事情。

 

       那么,如果要我们来模拟这件事情的话,我们可以怎么做呢?有以下的几种方案可以采用:

       1) 我控制这个账户在一段时间内只允许一个人登陆并使用

       2) 可以有几个人同时登陆这个账户,可以执行"查询金额"等不涉及金钱交易的操,但是对金钱交易这个动作加"锁",如果有一个人进行取款或者转帐等操作,那么在一个时间段内就只允许这一个人操作,并且实时更新数据.

      

       在Java中,实现锁的功能可以由同步关键字synchronized或者lock类来实现.我们先看下面一段代码:

package SynchronizedTest;
/**
 * Synchronized测试类
 * @author hadoop
 */
public class SynchronizedTest2 {
	
	static int account = 0;//设置初始账户金额为0,这里是全局变量
	public static void main(String[] args) {

		final SynchronizedTest2 test1 = new SynchronizedTest2();
		account = 5000;
		System.out.println("账户总款:" + account);
		
		Thread th1 = new Thread(new Runnable() {
			public void run() {
				 test1.getcash("A",  4000);
			}
		});		
		th1.start();
		Thread th2 = new Thread(new Runnable() {
			public void run() {
				test1.getcash("B", 3000);
			}
		});
		th2.start();
	}
	
	private synchronized  void getcash(String name,  int money) {
		
		if (money < account) {
			try {
				Thread.sleep(2000);//这里给个延时,这样会导致不同步问题的出现
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			account -= money;
			System.out.println(name+"客户取款:"+money);
			System.out.println("账户余额:"+account);
		} else {
			System.out.println("账户金额不足"+money+","+name+"客户没取到钱");
		}
	}
}

 输出结果是:(直接copy自控制台)

        账户总款:5000
        A客户取款:4000
        账户余额:1000
        账户金额不足3000,B客户没取到钱

 

如果不加synchronized会有如下效果:(直接copy自控制台)

        账户总款:5000

        A客户取款:4000

        账户余额:1000

        B客户取款:3000

        账户余额:-2000

 

 

        在测试类中我们使用Synchronized关键字对取款方法进行加锁,这样当同一个对象的不同线程共同调用getcash()方法时,一个时间段内只会被一个线程调用了。这里注意我们的账户account是一个全局变量。

 

        下面来对synchronized关键字的用法进行归纳一下:

         synchronized的用法一个是定义方法,一个是定义块

         1. 定义方法有两个范围
             1)是某个对象实例内,synchronized aMethod(){}可以防止多个线程同时访问这个对象(这里务必注意是同一个对象的多个线程,通常用实现Runnable的方法实现)的synchronized方法(如果一个对象有多个synchronized方法,只要一个线程访问了其中的一个synchronized方法,其它线程不能同时访问这个对象中任何一个synchronized方法)。这时,不同的对象实例的 synchronized方法是不相干扰的。也就是说,其它线程照样可以同时访问相同类的另一个对象实例中的synchronized方法,上面的例子就是实现了这个东东。
             2)是某个类的范围,synchronized static aStaticMethod{}防止多个线程同时访问这个类中的synchronized static 方法。它可以对类的所有对象实例起作用。

         2. 除了方法前用synchronized关键字,synchronized关键字还可以用于方法中的某个区块中,表示只对这个区块的资源实行互斥访问。用法是: synchronized(this){/*区块*/},它的作用域是当前对象.

 

         另外一种是通过实例化实现Lock接口的类来实现,我们用一段代码来说明一下:

         (这里我们用可重入互斥锁ReentrantLock类来实现)

 

package LockTest;

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

/**
 * Lock测试类
 * @author 赵广超
 */
public class LockTest2 {

	static int account = 0;// 设置初始账户金额为0,这里是全局变量
	private static Lock lock = new ReentrantLock();//reentrant的意思是可重入

	public static void main(String[] args) {

		final LockTest2 test = new LockTest2();
		account = 5000;
		System.out.println("账户总款:" + account);

		Thread th1 = new Thread(new Runnable() {
			public void run() {
				test.getcash("A", 4000);
			}
		});
		th1.start();
		
		Thread th2 = new Thread(new Runnable() {
			public void run() {
				test.getcash("B", 3000);
			}
		});
		th2.start();
	}

	private void getcash(String name, int money) {

		// 开始锁定,仅有一个线程可以执行此段代码
		lock.lock();
		try {
			if (money < account) {
				try {
					Thread.sleep(2000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				System.out.println(name + "客户取款:" + money);
				account -= money;
				System.out.println("账户余额:" + account);
			} else {
				System.out.println("账户金额不足" + money + "," + name + "客户没取到钱");
			}
		} finally {
			// 释放锁
			lock.unlock();
		}
	}
}

 控制台输出:

        账户总款:5000

        A客户取款:4000

        账户余额:1000

        账户金额不足3000,B客户没取到钱

 

 

	private void getcash(String name, int money) {

		// 开始锁定,仅有一个线程可以执行此段代码
//		lock.lock();
//		try {
			if (money < account) {
				try {
					Thread.sleep(2000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				System.out.println(name + "客户取款:" + money);
				account -= money;
				System.out.println("账户余额:" + account);
			} else {
				System.out.println("账户金额不足" + money + "," + name + "客户没取到钱");
			}
//		} finally {
//			// 释放锁
//			lock.unlock();
//		}
	}

 控制台输出:

        账户总款:5000

        A客户取款:4000

        账户余额:1000

        B客户取款:3000

        账户余额:-2000

 

        从上面的输出的不同结果可以看出,实现了Lock接口的ReenTrantLock对象确实是控制了不同线程在不同的时刻访问同一代码块。

        ReentrantLock类中的lock()方法是给下面的代码块加锁,而unlock方法则是释放锁。因为锁定和取消锁定出现在不同作用范围中时,必须谨慎地确保保持锁定时所执行的所有代码用 try-finally 或 try-catch 加以保护,以确保在必要时释放锁。 finally的意思是无论是否有异常被抛出,程序都需要执行完finally中的语句。

 

        ReentrantLock获取锁定与三种方式:
        a) lock(), 如果获取了锁立即返回,如果别的线程持有锁,当前线程则一直处于休眠状态,直到获取锁

        b) tryLock(), 如果获取了锁立即返回true,如果别的线程正持有锁,立即返回false;

        c) tryLock(long timeout, unit), 如果获取了锁定立即返回true,如果别的线程正持有锁,会等待参数给定的时间,在等待的过程中,如果获取了锁定,就返回true,如果等待超时,返回false;

        d) lockInterruptibly:如果获取了锁定立即返回,如果没有获取锁定,当前线程处于休眠状态,直到或者锁定,或者当前线程被别的线程中断

 

       synchronized和ReentrantLock的功能相近,使用两者有什么区别呢?

       1. ReentrantLock提供了多样化的同步,比如有时间限制的同步,可以被Interrupt的同步(synchronized的同步是不能Interrupt的)等。在资源竞争不激烈的情形下,性能稍微比synchronized差点点。但是当同步非常激烈的时候,synchronized的性能一下子能下降好几十倍。而ReentrantLock确还能维持常态。

       2. ReentrantLock 拥有Synchronized相同的并发性和内存语义,此外还多了 锁投票,定时锁等候和中断锁等候线程A和B都要获取对象O的锁定,假设A获取了对象O锁,B将等待A释放对O的锁定,如果使用 synchronized ,如果A不释放,B将一直等下去,不能被中断。如果 使用ReentrantLock,如果A不释放,可以使B在等待了足够长的时间以后,中断等待,而干别的事情。

       3. synchronized是在JVM层面上实现的,不但可以通过一些监控工具监控synchronized的锁定,而且在代码执行时出现异常,JVM会自动释放锁定,但是使用Lock则不行,lock是通过代码实现的,要保证锁定一定会被释放,就必须将unLock()放到finally{}中。

 

       以上是对锁的一些初步的认识。

——2013年1月28日记于蓝杰公司

分享到:
评论

相关推荐

    初中语文文摘社会有线电报这玩意

    初中语文文摘社会有线电报这玩意

    猜数字,Java课上做的小玩意

    一个猜数字的小玩意

    初中语文文摘社会会来事儿这玩意儿

    初中语文文摘社会会来事儿这玩意儿

    crack资源(这玩意还要不少于11字)

    crack资源(这玩意还要不少于11字)

    桌面工具(小玩意)—天气秀

    "桌面工具(小玩意)—天气秀"是一款专为Windows XP系统设计的应用程序,它能够实现在桌面上实时显示天气信息,让使用者无需打开浏览器或其他应用就能便捷地获取最新的天气状况。这款工具尤其适合那些需要频繁关注天气...

    一个整人小玩意儿(关机倒计时)

    关机的小玩意儿,可以整整人用,对计算机无任何危害,给无聊的生活带来一丝乐趣

    一些小玩意

    总的来说,这个“一些小玩意”的压缩包可能是一个宝藏库,包含了各种方便的开源工具和它们的源代码,适合编程爱好者、学生以及任何想要深入了解计算机工作原理或提升工作效率的人。通过深入学习和使用这些工具,不仅...

    非常简单的小玩意,但可以扩展功能哦。

    标题中的“非常简单的小玩意,但可以扩展功能哦”暗示我们这是一个简单的项目,可能是互动性的,具有可扩展性,适合初学者练习。描述进一步说明这是一个JavaScript(js)的小型演示项目,适合刚入门的JavaScript...

    自己写的小玩意 = =! java手写记事本

    自己写的小玩意。大家有兴趣的可以用用。仿window的记事本,还有不完整的地方。期待高手补充。本物件已封装。本机有了jdk双击文件就可以运行。

    windows桌面玩意儿可爱的小绵羊

    描述虽然重复,但可以推断出这个“小绵羊”桌面玩意儿设计得十分可爱,可能是以卡通或者动画的形式展示,可能会有各种动态行为,如吃草、跳跃、打滚等,为用户带来轻松愉快的心情。这种软件通常不会影响主要的操作...

    超级非常好玩的小玩意儿

    【标题】:“超级非常好玩的小玩意儿” 这个标题虽然略带俏皮,但它暗示着我们即将探讨的是一个有趣且引人入胜的IT项目。在IT领域,"小玩意儿"通常指的是小型应用、工具或者趣味性的编程项目,可能是利用JavaScript...

    一个小玩意11111111111111111111111111111

    一个小玩意11111111111111111111111111111

    排序小玩意儿

    这个名为"排序小玩意儿"的项目可能是对不同排序算法的一种实现或探索,通过提供的文件名我们可以推测出它可能包含的主要组件和功能。 1. **Main.java**:这是程序的主入口点,通常包含了程序的启动逻辑。在这里,它...

    桌面小玩意

    综上所述,"桌面小玩意"这个压缩包可能包含了一系列可以帮助用户美化和优化桌面体验的小工具,这些工具不仅实用,而且富有个性化,是提升日常使用电脑乐趣的好帮手。用户在使用过程中,应注意定期检查更新,确保软件...

    java小玩意

    简单java技术 让新手们基本了解java编程的基本用法

    ping ip地址的小玩意

    这个小玩意可以帮助用户通过图形界面发送ICMP回显请求到指定的IP地址,然后显示响应时间或者网络是否可达的信息。 描述中的“swing方式”是指该程序使用了Java Swing库来构建用户界面。Swing是Java的一个组件集,...

    桌面小玩意-绿色爱情鸟

    一款相当精美的小程序,由日本世嘉公司制作,这只精致而漂亮的小鸟理所当然地把你的桌面当作了它的栖身之所,在这里不断地盘旋、冲刺,寻找小鱼。看准之后,会俯冲下去,叼起一条,然后把它一口一口地吃下去!  你...

    神奇的小玩意.bat

    神奇的小玩意.bat

    svd-图像压缩小玩意

    svd-图像压缩小玩意

    一个管电子书用的小玩意,说实话文件系统可能都比这玩意好用 基于C#.zip

    优质项目,资源经过严格测试可直接运行成功且功能正常的情况才上传,可轻松copy复刻,拿到资料包后可轻松复现出一样的项目。 本人系统开发经验充足,有任何使用问题欢迎随时与我联系,我会及时为你解惑,提供帮助。...

Global site tag (gtag.js) - Google Analytics