Synchronized 互斥块(对象锁) 一个对象和一个monitor 的关系

来源:互联网 发布:mac看照片怎么下一张 编辑:程序博客网 时间:2024/06/14 14:08

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






阅读全文
0 0