同步方法
由于我们可以通过private
关键字来保证数据对象只能被方法访问,所以我们只需要针对方法提出一套机制,这套机制就是synchronized
关键字,它包括两种用法:
synchronized
方法 和 synchronized
块。
1 2 3 4 5
| 同步方法:
public synchronized void method(int args){
}
|
synchronized方法控制对“对象”的访问,每个对象对应一把锁,每个 synchronized
方法都必须获得调用该方法的对象的锁才能执行,否则线程会阻塞,方法一旦执行,就独占该锁,直到该方法返回才释放锁,后面被阻塞的线程才能获得这个锁,继续执行
同步方法缺点
缺点:若将一个大的方法申明为synchronized
将会影响效率。
方法里需要修改的内容才需要锁,锁太多,浪费资源
同步块
1 2 3 4
| 同步块: 同步块可以锁任何对象 synchronized (Obj){
}
|
- Obj称之为同步监视器
- Obj可以是任何对象,但是推荐使用共享资源作为同步监视器
- 同步方法中无需指定同步监视器,因为同步方法的同步监视器就是this,就是这个对象本身,或者是class 。
- 同步监视器的执行过程
1.第一个线程访问,锁定同步监视器,执行其中代码。
2.第二个线程访问 ,发现同步监视器被锁定,无法访问。
3. 第一个线程访问完毕,解锁同步监视器。
4.第二个线程访问,发现同步监视器没有锁,然后锁定并访问。
解决不安全问题
买票
利用同步方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| //买票 //synchronized 同步方法,锁的是this private synchronized void buy() { //判断是否有票 if(ticketNum <= 0){ flag = false; return; } try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "拿到第" + ticketNum-- + "张票"); }
|
账户
在账户用同步方法发现数据还是出现问题了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| @Override public synchronized void run() { if (account.money - drawingMoney <0){ System.out.println(Thread.currentThread().getName()+"钱不够,取不了"); return; }
try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); }
account.money = account.money - drawingMoney;
nowmoney = nowmoney + account.money;
System.out.println(account.name+"余额为:"+account.money);
System.out.println(this.getName()+"手里的钱为:"+account.money); }
|
为什么呢?
因为修改银行余额,修改的不是银行类(当前类),而是账户类(用户)的余额。所以应该锁用户才对。
但是同步方法默认锁的是当前类(this),所以我们应该用同步块,它可以锁任意对象。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| @Override public void run() { synchronized (account) { if (account.money - drawingMoney < 0) { System.out.println(Thread.currentThread().getName() + "钱不够,取不了"); return; }
try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); }
account.money = account.money - drawingMoney;
nowmoney = nowmoney + account.money;
System.out.println(account.name + "余额为:" + account.money);
System.out.println(this.getName() + "手里的钱为:" + account.money); } }
|
集合
1 2 3 4 5
| new Thread(()->{ synchronized (list) { list.add(Thread.currentThread().getName()); } }).start();
|