Java并发编程实战(使用synchronized实现同步方法)

  本文介绍java最基本的同步方式,即使用synchronized关键字来控制一个方法的并发访问,如果一个对象已用synchronized关键字声明,那么只有一个执行线程允许去访问它,其它试图访问这个对象的线程将被挂起,直到第一个线程访问完毕。

   下面通过一个小例子来学习这个概念,公司向银行存钱,取钱场景。

  1:创建Account的账号类,它是银行账户的模型,只有一个双精度浮点型属性,balance.

  2:实现balance的get set 方法。

  3:实现AddAmount()方法,将传入的数量加到余额balance中,并且在同一时间只允许一个线程去改变这个值,使用synchronized关键字。

  4:实现SubtractAmount()方法,将传入的数量从余额balance中扣除,并且在同一时间只允许一个线程去改变这个值。

  具体代码:

    

 1 public class Account {
 2
 3     /**
 4      * Balance of the bank account
 5      */
 6     private double balance;
 7
 8     /**
 9      * Returns the balance of the account
10      * @return the balance of the account
11      */
12     public double getBalance() {
13         return balance;
14     }
15
16     /**
17      * Establish the balance of the account
18      * @param balance the new balance of the account
19      */
20     public void setBalance(double balance) {
21         this.balance = balance;
22     }
23
24     /**
25      * Add an import to the balance of the account
26      * @param amount import to add to the balance
27      */
28     public synchronized void addAmount(double amount) {
29             double tmp=balance;
30             try {
31                 Thread.sleep(10);
32             } catch (InterruptedException e) {
33                 e.printStackTrace();
34             }
35             tmp+=amount;
36     }
37
38     /**
39      * Subtract an import to the balance of the account
40      * @param amount import to subtract to the balance
41      */
42     public synchronized void subtractAmount(double amount) {
43             double tmp=balance;
44             try {
45                 Thread.sleep(10);
46             } catch (InterruptedException e) {
47                 e.printStackTrace();
48             }
49             tmp-=amount;
50             balance=tmp;
51     }
52
53 }

  5:实现一个ATM模拟类Bank,它使用subtractAmount()方法对账户的余额进行扣除,实现Runabl接口。

   具体代码:

    

 1 public class Bank implements Runnable {
 2
 3     /**
 4      * The account affected by the operations
 5      */
 6     private Account account;
 7
 8     /**
 9      * Constructor of the class. Initializes the account
10      * @param account The account affected by the operations
11      */
12     public Bank(Account account) {
13         this.account=account;
14     }
15
16
17     /**
18      * Core method of the Runnable
19      */
20     public void run() {
21         for (int i=0; i<100; i++){
22             account.subtractAmount(1000);
23         }
24     }
25
26 }

  6:实现公司模拟类,调用addAmount()方法进行存钱,实现Runabl接口。

  具体代码:

  

 1 public class Company implements Runnable {
 2
 3     /**
 4      * The account affected by the operations
 5      */
 6     private Account account;
 7
 8     /**
 9      * Constructor of the class. Initializes the account
10      * @param account the account affected by the operations
11      */
12     public Company(Account account) {
13         this.account=account;
14     }
15
16     /**
17      * Core method of the Runnable
18      */
19     public void run() {
20         for (int i=0; i<100; i++){
21             account.addAmount(1000);
22         }
23     }

  7:在主方法中调用测试:通过线程的join方法,在存期那,取钱线程模拟完毕后打印出结构。

  

 1 public class Main {
 2
 3     /**
 4      * Main method of the example
 5      * @param args
 6      */
 7     public static void main(String[] args) {
 8         // Creates a new account ...
 9         Account    account=new Account();
10         // an initialize its balance to 1000
11         account.setBalance(1000);
12
13         // Creates a new Company and a Thread to run its task
14         Company    company=new Company(account);
15         Thread companyThread=new Thread(company);
16         // Creates a new Bank and a Thread to run its task
17         Bank bank=new Bank(account);
18         Thread bankThread=new Thread(bank);
19
20         // Prints the initial balance
21         System.out.printf("Account : Initial Balance: %f\n",account.getBalance());
22
23         // Starts the Threads
24         companyThread.start();
25         bankThread.start();
26
27         try {
28             // Wait for the finalization of the Threads
29             companyThread.join();
30             bankThread.join();
31             // Print the final balance
32             System.out.printf("Account : Final Balance: %f\n",account.getBalance());
33         } catch (InterruptedException e) {
34             e.printStackTrace();
35         }
36     }
37 }

  结果,相同时间内,存与取执行后应该是相等的。如果我们在方法中不去使用synchronized关键字,那么得出的结果就不对了。

  

  Account : Initial Balance: 1000.000000
  Account : Final Balance: 1000.000000

    

时间: 2024-10-22 03:19:03

Java并发编程实战(使用synchronized实现同步方法)的相关文章

【Java并发编程实战】—–synchronized

在我们的实际应用其中可能常常会遇到这样一个场景:多个线程读或者.写相同的数据,訪问相同的文件等等.对于这样的情况假设我们不加以控制,是非常easy导致错误的. 在java中,为了解决问题,引入临界区概念.所谓临界区是指一个訪问共用资源的程序片段,而这些共用资源又无法同一时候被多个线程訪问. 在java中为了实现临界区提供了同步机制.当一个线程试图訪问一个临界区时,他将使用一种同步机制来查看是不是已经有其它线程进入临界区. 假设没有则他就能够进入临界区,否则他就会被同步机制挂起,指定进入的线程离开

【Java并发编程实战】-----synchronized

在我们的实际应用当中可能经常会遇到这样一个场景:多个线程读或者.写相同的数据,访问相同的文件等等.对于这种情况如果我们不加以控制,是非常容易导致错误的.在java中,为了解决这个问题,引入临界区概念.所谓临界区是指一个访问共用资源的程序片段,而这些共用资源又无法同时被多个线程访问. 在java中为了实现临界区提供了同步机制.当一个线程试图访问一个临界区时,他将使用一种同步机制来查看是不是已经有其他线程进入临界区.如果没有则他就可以进入临界区,否则他就会被同步机制挂起,指定进入的线程离开这个临界区

JAVA并发编程实战 读书笔记(二)对象的共享

<java并发编程实战>读书摘要 birdhack 2015年1月2日 对象的共享 JAVA并发编程实战读书笔记 我们已经知道了同步代码块和同步方法可以确保以原子的方式执行操作,但一种常见的误解是,认为关键之synchronized只能用于实现原子性或者确定临界区.同步还有另一个重要的方面:内存可见性. 1.可见性 为了确保多个线程之间对内存写入操作的可见性,必须使用同步机制. 在没有同步的情况下,编译器.处理器以及运行时等都可能对操作的执行顺序进行一些意想不到的调整.在缺乏足够同步的多线程程

《java并发编程实战》笔记(一)

最近在看<java并发编程实战>,希望自己有毅力把它读完. 线程本身有很多优势,比如可以发挥多处理器的强大能力.建模更加简单.简化异步事件的处理.使用户界面的相应更加灵敏,但是更多的需要程序猿面对的是安全性问题.看下面例子: public class UnsafeSequence { private int value; /*返回一个唯一的数值*/ public int getNext(){ return value++; } } UnsafeSequence的问题在于,如果执行时机不对,那么

《Java并发编程实战》第十一章 性能与可伸缩性 读书笔记

造成开销的操作包括: 1. 线程之间的协调(例如:锁.触发信号以及内存同步等) 2. 增加的上下文切换 3. 线程的创建和销毁 4. 线程的调度 一.对性能的思考 1 性能与可伸缩性 运行速度涉及以下两个指标: 某个指定的任务单元需要"多快"才能处理完成.计算资源一定的情况下,能完成"多少"工作. 可伸缩性: 当增加计算资源时(例如:CPU.内存.存储容器或I/O带宽),程序的吞吐量或者处理能力能相应地增加. 2 评估各种性能权衡因素 避免不成熟的优化.首先使程序正

【Java并发编程实战】—– AQS(三):阻塞、唤醒:LockSupport

在上篇博客([Java并发编程实战]-– AQS(二):获取锁.释放锁)中提到,当一个线程加入到CLH队列中时,如果不是头节点是需要判断该节点是否需要挂起:在释放锁后,需要唤醒该线程的继任节点 lock方法,在调用acquireQueued(): if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()) interrupted = true; 在acquireQueued()中调用parkAndCheckIn

《Java并发编程实战》要点笔记及java.util.concurrent 的结构介绍

买了<java并发编程实战>这本书,看了好几遍都不是很懂,这个还是要在实战中找取其中的要点的,后面看到一篇文章笔记做的很不错分享给大家!! 原文地址:http://blog.csdn.net/cdl2008sky/article/details/26377433 Subsections  1.线程安全(Thread safety) 2.锁(lock) 3.共享对象 4.对象组合 5.基础构建模块 6.任务执行 7.取消和关闭 8.线程池的使用 9.性能与可伸缩性 10.并发程序的测试 11.显

《Java并发编程实战》第十五章 原子变量与非阻塞同步机制 读书笔记

一.锁的劣势 锁定后如果未释放,再次请求锁时会造成阻塞,多线程调度通常遇到阻塞会进行上下文切换,造成更多的开销. 在挂起与恢复线程等过程中存在着很大的开销,并且通常存在着较长时间的中断. 锁可能导致优先级反转,即使较高优先级的线程可以抢先执行,但仍然需要等待锁被释放,从而导致它的优先级会降至低优先级线程的级别. 二.硬件对并发的支持 处理器填写了一些特殊指令,例如:比较并交换.关联加载/条件存储. 1 比较并交换 CAS的含义是:"我认为V的值应该为A,如果是,那么将V的值更新为B,否则不需要修

《Java并发编程实战》第十章 避免活跃性危险 读书笔记

一.死锁 所谓死锁: 是指两个或两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去. 百科百科 当两个以上的运算单元,双方都在等待对方停止运行,以取得系统资源,但是没有一方提前退出时,这种状况,就称为死锁.维基百科 1. 顺序死锁 最少有两个锁,一个线程获取到A锁需要获取B锁才能进行操作,而另外一个线程获取到了B锁,需要获取A锁才能执行操作,这种情况下容易出现顺序死锁. public class LeftRightDeadlock { priva

【java并发编程实战】-----线程基本概念

学习Java并发已经有一个多月了,感觉有些东西学习一会儿了就会忘记,做了一些笔记但是不系统,对于Java并发这么大的"系统",需要自己好好总结.整理才能征服它.希望同仁们一起来学习Java并发编程,共同进步,互相指导. 在学习Java并发之前我们需要先理解一些基本的概念:共享.可变.线程安全性.线程同步.原子性.可见性.有序性. 共享和可变 要编写线程安全的代码,其核心在于对共享的和可变的状态进行访问. "共享"就意味着变量可以被多个线程同时访问.我们知道系统中的资