在Java中咱们可使用synchronized关键字来控制一段代码的并发访问。使用synchronized关键字修饰的代码在同一时间只有一个线程能够访问,其余要访问这个代码块的线程将被挂起。java
使用synchronized关键字能够控制并发访问同时修改一份数据形成数据不一致的问题,可是synchronized会下降系统性能,由于即便你启动再多的线程,同一时间仍是只有一个线程能访问,其余线程都在挂起,因此使用了synchronized也就没有真正意义上的并发了。并发
咱们可使用synchronized关键字修饰方法,那么这个方法就是同步方法。咱们也可使用synchronized来修饰代码款,那么这个代码块就是同步代码块。ide
下面咱们看一个经典的银行帐号的例子,在这个例子中咱们模拟一个银行帐号的存取款过程,同时开启多个线程模拟屡次并发的存取款操做,最后咱们来看下帐户余额。性能
建立Account类来模拟银行帐户,其中修改帐户金额的方法使用synchronized关键字修饰,打印帐户变更并模拟每次帐户修改操做须要1秒钟完成。this
public class Account { private int amount; public Account(int amount) { this.amount = amount; } public int getAmount() { return amount; } public void setAmount(int amount) { this.amount = amount; } //public int modify(int amount) { public synchronized int modify(int amount) { if (amount > 0) { System.out.printf("%s: 向帐户存款%d元.\n", Thread.currentThread().getName(), amount); } else if (amount < 0 && this.amount >= -amount) { System.out.printf("%s: 余额充足,从帐户取款%d元.\n", Thread.currentThread().getName(), amount); } else if (amount < 0 && this.amount < -amount) { System.out.printf("%s: 余额不足,取款失败.\n", Thread.currentThread().getName()); return this.amount; } else { return this.amount; } try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } this.amount = this.amount + amount; return amount; } }
新建线程类模拟用户的存取款操做spa
public class MyRunnable implements Runnable{ private Account account; private int amount; public MyRunnable(Account account, int amount) { this.account = account; this.amount = amount; } @Override public void run() { account.modify(amount); } }
最后再主方法类中,新建一个余额为1000元的帐户,而后使用4个线程分别模拟1次存款操做,3次取款操做,正常状况应该是有一次取款失败,由于最后帐户余额已经不够1000元了。线程
public class Main { public static void main(String[] args) { Account account = new Account(1000); Thread[] threads = new Thread[4]; threads[0] = new Thread(new MyRunnable(account, 1000)); threads[1] = new Thread(new MyRunnable(account, -1000)); threads[2] = new Thread(new MyRunnable(account, -1000)); threads[3] = new Thread(new MyRunnable(account, -1000)); try { for (int i = 0; i < 4; i++) { threads[i].start(); Thread.sleep(50); } for (int i = 0; i < 4; i++) { threads[i].join(); } } catch (InterruptedException e) { e.printStackTrace(); } System.out.printf("Main: 帐户余额:%d\n", account.getAmount()); } }
查看日志,咱们发现有一次取款失败,而且最后帐户余额为0.日志
Thread-0: 向帐户存款1000元. Thread-3: 余额充足,从帐户取款-1000元. Thread-2: 余额充足,从帐户取款-1000元. Thread-1: 余额不足,取款失败. Main: 帐户余额:0
咱们若是去掉synchronized关键字修饰,你会发现三次取款都成功了,而且最后帐户余额为-1000元。code
Thread-0: 向帐户存款1000元. Thread-1: 余额充足,从帐户取款-1000元. Thread-2: 余额充足,从帐户取款-1000元. Thread-3: 余额充足,从帐户取款-1000元. Main: 帐户余额:-1000