我和老婆去银行取钱

有一天,和老婆打了个赌。如今我的银行账号里共同拥有5000块钱。我们去银行同一时候取钱,看我俩能不能同一时候取出5000来。。。。(PS:打赌的代价是:假设都能取出5000,那这10000块都给她买吃的!假设仅仅能取5000,嘿嘿。那就仅仅用着5000块给她买吃的~~~~怎么认为这条件有点怪怪的nie?)

心动不如行动!

她拿着存折去柜台取,我拿着银行卡去ATM机取。找了个合适的时机,我在输入好金额时,一直盯着那个teller的手。他在一切准备就绪后敲回车的同一时候,我以迅雷不及掩耳之势按下了确定。

结果是,我的ATM机唰唰唰吐出50张毛爷爷,老婆那边也得意洋洋的拿着5000块钱过来了。。。。

        “喂~醒醒了。”

原来是在做梦~~不行,这可能行得通,我得试试!别不相信,我还用刚学的线程的知识验证了一下呢!

public class TestDrawMoney implements Runnable {
//说明:此例的背景是【多人】【同一时候】在【同一账户上】取款
//在统一账户danny的账户上取款
Depositor danny = new Depositor();
public static void main(String[] args) {
TestDrawMoney test=new TestDrawMoney();
//我取款的操作是一个线程draw1,我老婆取款的操作也是一个线程draw2
Thread draw1 = new Thread(test);
Thread draw2 = new Thread(test);
draw1.setName("我");
draw2.setName("我老婆");
//启动线程
draw1.start();
draw2.start();
}
//重写run方法
public void run() {
danny.DrawMoney(5000);
}
} //储户类
class Depositor {
//剩余金额
private static int deposit = 5000;
//取钱方法
public void DrawMoney(int money) {
if (money <= deposit) {
// 模拟银行吐钱过程
System.out.println(Thread.currentThread().getName() + "取款时:成功取款。" + money + "元!");
// 模拟账户扣款过程
deposit = deposit - money;
}else{
System.out.println(Thread.currentThread().getName()+"取款时:剩余金额不足!");
}
}
}

假设银行取款的程序跟上面相似的话。那么当中一个线程(比方我老婆取钱)执行到“账户扣款”之前时,资源可能被还有一个线程(比方我取钱)抢过来执行,这时。第一个线程由于已经推断账户剩余金额充足。并且已经把钱给了我老婆,可是银行扣款执行之前,我取钱这个线程抢先执行。银行还没有扣款。所以在我取钱这个线程中,推断剩余金额也是充足的。

您推測试结果怎么着?我俩还真都取出了5000!

可惜学习了的线程中锁的机制后,这个发財梦被彻底打破了。

。。

public class TestDrawMoney implements Runnable {
// 说明:此例的背景是【多人】【同一时候】在【同一账户上】取款
// 在统一账户danny的账户上取款
Depositor danny = new Depositor(); public static void main(String[] args) {
TestDrawMoney test = new TestDrawMoney();
// 我取款的操作是一个线程draw1,我老婆取款的操作也是一个线程draw2
Thread draw1 = new Thread(test);
Thread draw2 = new Thread(test);
draw1.setName("我");
draw2.setName("我老婆");
// 启动线程
draw1.start();
draw2.start();
} // 重写run方法
public void run() {
danny.DrawMoney(5000);
}
} // 储户类
class Depositor {
// 剩余金额
private static int deposit = 5000;
// 取钱方法
public void DrawMoney(int money) {
synchronized (this) {//这里为整个线程的操作加上了锁
if (money <= deposit) {
// 模拟银行吐钱过程
System.out.println(Thread.currentThread().getName() + "取款时:成功取款。" + money + "元!");
// 模拟账户扣款过程
deposit = deposit - money;
} else {
System.out.println(Thread.currentThread().getName+"取款时:剩余金额不足!");
}
}
}
}

跟第一个样例唯一不同之处,就是在取钱方法DrawMoney()中加了锁——synchronized,这样,当一个线程执行此方法时,这个对象就会被锁定。其它线程就无法执行此方法中被锁定的代码。

执行结果是,仅仅能有一个人能取出钱来:

   或  

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvaHV5dXlhbmc2Njg4/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt=""> 
(这两个线程,先运行的可以取出钱,但谁先谁后并不一定,所以会有两种结果。

synchronized介绍

当多个线程可能在同一时候訪问同样的资源时。就要考虑是否用synchronized。synchronized用来给对象、方法或者代码块加锁。

同一时刻仅仅能有一个线程能够运行被锁代码,其它不论什么线程都不会打断它。必须等到当前正在运行被锁代码的线程运行完成,其它线程才干够运行。

sychronized的实质是。在运行被锁方法或代码的过程中,锁定当前对象(上例中既然锁定了当前对象“danny”,那么肯定也锁定了当前对象中的静态变量“deposit”)。

synchronized的使用方法

1、用synchronized修饰方法。如上例中能够直接用synchronized修饰DrawMoney()的方法:

	public synchronized void DrawMoney(int money) {
if (money <= deposit) {
// 模拟银行吐钱过程
System.out.println(Thread.currentThread().getName() + "取款时:成功取款" + money + "元!");
// 模拟账户扣款过程
deposit = deposit - money;
} else {
System.out.println(Thread.currentThread().getName() + "取款时:剩余金额不足! ");
}
}

假设一个对象有多个synchronized方法,仅仅要一个线程訪问了当中的一个synchronized方法,其他线程不能同一时候訪问这个对象中不论什么一个synchronized方法。

假设两个线程想同一时候訪问这种方法。那么须要在两个实例对象中訪问。也就是说。不同对象实例中synchronized修饰的方法时互不干扰的。

2、用synchronized锁住代码

正如上文第二个样例。在方法中,用synchronized(this){ //代码片段}锁住同一时刻仅仅同意一个线程訪问的代码。

同一时刻,其它线程不同意訪问此对象的被锁代码段。即运行到此代码段时。此当前对象会被锁住,其它线程不同意訪问。待当前线程运行完成,其它线程才干够訪问。

synchronized修饰成员方法和synchronized(this)两种使用方法的含义是一样的。

synchronized可能会带来的问题

当把程序“锁”住时,还会带来其它问题——死锁。

比方如今Danny和Maria两个人仅仅有一双筷子,想吃大餐的他们仅仅有抢到两仅仅筷子才干够开吃:

public class TestDeadLock2 implements Runnable {
public String name; //姓名
static Object chopsticks1 = new Object(); //第一仅仅筷子
static Object chopsticks2 = new Object(); //第二仅仅筷子 public static void main(String[] args) {
System.out.println("抢筷子開始!");
TestDeadLock2 test1=new TestDeadLock2();
TestDeadLock2 test2=new TestDeadLock2();
test1.name="Danny";
test2.name="Maria";
Thread t1=new Thread(test1);
Thread t2=new Thread(test2);
t1.start();
t2.start();
} public void run() {
if (name.equals("Danny")) {
synchronized (chopsticks1) {
try {
Thread.sleep(500);//这里为了放大效果。让Danny抢到筷子1时停顿一下
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (chopsticks2) {
System.out.println("Danny成功抢到两仅仅筷子。他要开吃啦! ");
}
}
} else if (name.equals("Maria")) {
synchronized (chopsticks2) {
try {
Thread.sleep(500);//这里为了放大效果。让Maria抢到筷子2时停顿一下
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (chopsticks1) {
System.out.println("Maria成功抢到两仅仅筷子。她要开吃啦!");
}
}
}
}
}

样例中,Danny和Maria抢到每一仅仅筷子时都会把筷子握在手里,当Danny抢到筷子1,Maria抢到筷子2时。他们都在等对方让出筷子。互不相让,无限等待下去。这就发生了死锁。

所以,用锁需慎重。涉及到同步时,一定要考虑这个线程是不是应该同步,加了同步,效率变低,不加同步,可能产生类似上例中数据不一致的现象。所以,在保证数据安全的情况下,同步区域(即被锁区域)越小越好。

【J2SE高速进阶】——多线程之synchronized的更多相关文章

  1. java多线程之synchronized(线程同步)

    一.线程同步,主要应用synchronized关键字: public class TraditionalThreadSynchorinzed { public static void main(Str ...

  2. Java多线程之synchronized(五)

    上篇介绍了用synchronized修饰static方式来实现“Class 锁”,今天要介绍另一种实现方式,synchronized(class)代码块,写法不一样但是作用是一样的.下面我附上一段代码 ...

  3. Java多线程之synchronized(四)

    前面几章都是在说synchronized用于对象锁,无论是修饰方法也好修饰代码块也好,然而关键字synchronized还可以应用到static静态方法上,如果这样写,那就是对当前的*.java文件所 ...

  4. Java多线程之synchronized(三)

    在多线程访问同一个对象中的不同的synchronized方法或synchronized代码块的前提下,也就是“对象监控器”为同一个对象的时候,也就是synchronized的锁为同一把锁的时候,调用的 ...

  5. Java多线程之synchronized(二)

    为了解决“非线程安全”带来的问题,上一节中使用的办法是用关键字synchronized修饰多个线程可能同时访问到的方法,但是这样写是存在一定的弊端的,比如线程A调用一个用synchronized修饰的 ...

  6. Java多线程之synchronized(一)

    在上节中已经说过了“非线程安全”是如何出现的,链接如下:http://www.cnblogs.com/chentong/p/5650137.html,那么怎么解决“非线程安全”问题呢,只需要在两个线程 ...

  7. (二)java多线程之synchronized

    本人邮箱: kco1989@qq.com 欢迎转载,转载请注明网址 http://blog.csdn.net/tianshi_kco github: https://github.com/kco198 ...

  8. JAVA多线程之Synchronized关键字--对象锁的特点

    一,介绍 本文介绍JAVA多线程中的synchronized关键字作为对象锁的一些知识点. 所谓对象锁,就是就是synchronized 给某个对象 加锁.关于 对象锁 可参考:这篇文章 二,分析 s ...

  9. 多线程之Synchronized锁的基本介绍

    基本介绍 synchronized是Java实现同步的一种机制,它属于Java中关键字,是一种jvm级别的锁.synchronized锁的创建和释放是此关键字控制的代码的开始和结束位置,锁是有jvm控 ...

随机推荐

  1. Robotium测试报告的生成方法(下)

    7.4 测试报告优化 通过上面的三种方法,我们都可以得到一个Xml格式的测试报告,不过这不是我们想要的,因为这样的报告读起来很费劲,而且这样的报告发给领导们也是不行的.所以我们要美化一下才行,一般都是 ...

  2. CentOS6.5 安装中文输入法

    直接转载他人文章:http://hi.baidu.com/dxqt58592/item/a85b96e72c423cc0baf37d3c centos 6.5使用yum安装中文输入法 1.需要root ...

  3. Solr linux安装

    1.下载安装(自行官网下载) 2.解压安装包 3.进入解压后的bin目录执行命令: ./solr start 出现如下警告: 根据提示修改solr.in.sh中的SOLR_ULIMIT_CHECKS属 ...

  4. 基于 <tx> 和 <aop> 命名空间的声明式事务管理

    环境配置 项目使用SSH架构,现在要添加Spring事务管理功能,针对当前环境,只需要添加Spring 2.0 AOP类库即可.添加方法: 点击项目右键->Build Path->Add ...

  5. [MUTC2013][bzoj3513] idiots [FFT]

    题面 传送门 思路 首先有一个容斥原理的结论:可以组成三角形的三元组数量=所有三元组-不能组成三角形的三元组 也就是说我们只要求出所有不能组成三角形的三元组即可 我们考虑三元组(a,b,c),a< ...

  6. hdu 4096 判断路径

    思路:将每个关系当成一条有向边,查询时就判断之间存在路径. #include<iostream> #include<cstdio> #include<cstring> ...

  7. 计数(count)

    计数(count) 题目描述 既然是萌萌哒 visit_world 的比赛,那必然会有一道计数题啦! 考虑一个 NN个节点的二叉树,它的节点被标上了 1∼N1∼N 的编号. 并且,编号为 ii的节点在 ...

  8. PE508

    真是日了苟了我之前还以为那个递归会炸状态..我真他妈胆小啊.. = = 明确一下,这个可以构成所有高斯整数(唯一),构造方法大概就是先看曼哈顿距离,然后判断要不要减1,然后再/(1-i) 我们考虑在末 ...

  9. ios工程中一天只让显示一次的广告,或是弹出窗,如何实现

    需求: 产品 代码实现: 在首页.m中 //一天之内只能批量邀请一次 NSUserDefaults *userDefault = [NSUserDefaults standardUserDefault ...

  10. 转:mysql 索引

    转:mysql 索引 文章归属:http://feiyan.info/16.html,我想自己去写了,但是发现此君总结的非常详细.直接搬过来了 关于MySQL索引的好处,如果正确合理设计并且使用索引的 ...