Synchronized 互斥块(对象锁) 一个对象和一个monitor 的关系
Synchronized :锁住的是对象,出现synchronized表示随后的代码块要用到共享数据了,要锁住了。
一、3种形式。
1、synchronized(obj):可以任意指定对象.
2、synchronized(this):当前对象:当一个类加到内存时,常量池有一个地址直接指向当前正在执行的对象.
3、public synchronized void run():当前对象(this),这是实例方法,每一方法里存着一个This.方法
对象:可以是普通类 public class Test {}, 也可以是干活线程类 ,只要是new 类名 的所有对象。
**所以synchronized(this){} 和synchronized 方法差不多,反正是锁当前对象。
二、对于锁类其实也一样,因为一个类入内存后,还是java.lang.Class 的类对象。所以还是锁对象。
三、锁的数据:一个类成员数据:对象实例变量,类变量
1、object instance variables:对象实例变量,存在堆中。
2、class variables:类变量,存在method area。
如果您正在写一个变量,这个变量有可能随后被另一个线程读,
或您正在读一个变量,这个变量有可能随后被另一个线程写,
这时你必须用到变量的地方放一个synchronized,
四、例子
1、简单需求
例如:银行个人账户(对象):卡号,余额
如果一个小店主,顾客很多,如果都用微信付款的话,有可能同一时刻访问店主的银行个人账户的问题。
当一个顾客对店主的账号余额进行加减时,锁住账号,退出后,另一个顾客才能继续付钱,
顾客是看不见这些过程,只管提交就行,银行应该是相应的服务软件接收用户的请求。
2、代码说明
1>有4个线程,main()主线程,3个顾客子线程
因为是几个顾客有可能同时付款,所以需要线程。
2>一个店主银行个人账户的对象(对象里面有共享资源,余额)
银行个人账户的对象(peraccount)对应一个monitor。
3>执行步骤(只取其中一个结果分析)
第一:main线程执行到threads[0].start(),threads[1].start(),threads[2].start();
即3个线程入就绪对象,这是main代码执行完,cpu空
第二:调度程序选择threads[0]进入cpu。
第三:
(一)当jvm行到synchronized peraccount 这一句时,表示peraccount 对象里面有共享数据,
去取一把钥匙,看人家是否已经锁住共享数据了.去哪里取呢?monitor大楼。
(二)这时cpu空,调度程序安排thread[2]入cpu,thread[0]去monitor大楼取钥匙去了
两个过程同时执行。
(三)当jvm 执行到threads[2]到synchronized peraccount 时,又该去peraccount monitor大楼取钥匙了。
同时cpu还是空。
(四)调度程序安排thread[1]入cpu执行,在monitor大厅等待,
同时cpu空,这时,thread[0]已经占用特殊房间,表示取得钥匙,获得一把锁,锁住后面的代码。
1》调度程序安排thread[0]进入Cpu,因为后面的代码已经被thread[0]锁住,所以一直占cpu执行完。
cpu空,同时特殊房间空,thread[1]和thread[2]竞争,thread[1]进入特殊房间,获得锁。
2》thread[1]占用特殊房间,表示取得钥匙,获得一把锁,锁住后面的代码。所以一直占cpu执行完,
cpu空,同时特殊房间空,thread[2]进入特殊房间。
3》thread[2]已经占用特殊房间,表示取得钥匙,获得一把锁,锁住后面的代码。所以一直占cpu执行完,cpu空
整个过程完
3、图形
4、代码
package concurrency; /** * 银行个人账户,共享数据, * cardnumber,balance * 一个卡号,多人可以对余额进行修改。 * */ class Account { private String cardnumber;//卡号 private float balance; //余额 public Account(String cardnumber, float balance) { this.cardnumber = cardnumber; this.balance = balance; } // 存钱 public void deposit(float sum) { balance= balance+sum; try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } //得到当前钱数。 public float getBalance() { return balance; } } /** * PayToAccount 现在是静态类 * 可以new 多个对象,表示多人用微信付款 * 每一支付的人和店主的个人账号相连 */ public class PayToAccount implements Runnable { private Account peraccount; //表示和店主个人账号对象相连接 private float sum; public PayToAccount(Account peraccount,float sum) { this.peraccount = peraccount; this.sum=sum; } public void run() { System.out.println(Thread.currentThread().getName()+" run()"); synchronized (peraccount) {//到这里表示要锁住店主的个人账号对象了,后面涉及对同一对象进行操作。 System.out.println(Thread.currentThread().getName()+" 取得钥匙"); peraccount.deposit(this.sum); System.out.println(Thread.currentThread().getName() + ":" +peraccount.getBalance()); } } /**客户微信软件---扫一下,哪么微信软件应该提取了店主的个人账号,点支付之后,应该是执行PayToAccount的run操作。 * 下面是模拟顾客扫微信的功能,只用了固定的3个顾客,但是现实顾客数无法确定。 */ public static void main(String[] args) { //店主墙上的微信号(也就是店主的银行个人账号) Account account = new Account("yang", 50.0f);// //相当客户扫一扫:把自己和店主的银行个人账号相连, //账号,存的钱数。 PayToAccount buyer1 = new PayToAccount(account,50.0f); PayToAccount buyer2 = new PayToAccount(account,125.0f); PayToAccount buyer3 = new PayToAccount(account,310.0f); /** * 相当3个客户都点了支付按钮,付钱, * 涉及同时对一个账号进行操作, * 建立三个新的驱动线程带上三个新的干活线程(客户付款线程) *在这个例子中,三个驱动线程是new Thread, *干活线程是buyer1,buyer2,buyer3,锁住的对象是Account(共享资源) */ Thread threads[] = new Thread[3]; Thread threads[] = new Thread[3]; threads[0] = new Thread(buyer1, "第一个顾客" ); threads[1] = new Thread(buyer2, "第二个顾客" ); threads[2] = new Thread(buyer3, "第三个顾客" ); /** /** * 付钱,客户任务已经完成,就是等银行返回消息了。 */ threads[0].start(); threads[1].start(); threads[2].start(); } } |
只是为了理解monitor,所以选择
其中的一个运行结果:
第一个顾客 取得钥匙
第一个顾客:100.0
第二个顾客 取得钥匙
第二个顾客:225
第三个顾客 取得钥匙
第三个顾客:535.0
转自:http://blog.****.net/monica12/article/details/72953156