泊而至远

导航

 

线程同步

为什么需要同步

①   线程同步是为了防止多个线程访问一个数据对象时,对数据造成破坏。

②   线程的同步是保证多线程安全访问竞争资源的一种手段。

同步和锁

①   Java中每一个对象都有一个内置锁。

②   当程序运行到非静态的synchronized同步方法上时,自动获得与正在执行代码类的当前实例(this实例)有关的锁;当程序运行到synchronized同步代码块时,自动获得锁定对象的锁。

③   获得一个对象的锁也称为获取锁、锁定对象、在对象上锁定或在对象上同步。当程序运行到synchronized同步方法或代码块时该对象锁才起作用。

④   一个对象只有一个锁。所以,如果一个线程获得该锁,就没有其他线程可以获得锁,直到第一个线程释放锁。这也意味着任何其他线程都不能进入synchronized方法或代码块,直到该锁被释放。释放锁是指持锁线程退出了synchronized同步方法或代码块。

 

对于同步,一般而言在Java代码中需要完成两个操作:

①   把竞争访问的资源标识为private。

②   同步那些访问资源的代码,使用synchronized关键字执行完成或发生异常时,会自动释放。

 

示例需求:

①   某银行卡账号上有500元现金。一个人拿着存折去取钱,同时另一个人拿着卡去ATM上取钱,各自取钱400元。

②   要求取钱过程中不能出现资源竞争:比如400元被取出两次,银行卡的账目不能小于0。

银行类及线程

 1 class Bank {
 2     private int money = 500;
 3 
 4     // 取钱的方法,返回取钱的数目
 5     //当一个线程去调用同步方法的时候,这个线程就获取了当前对象的锁。
 6     //其他线程当调用同步方法的时候只能等待,因为无法获取对象的锁,
 7     //只有等第一个线程释放对象的锁方可进入
 8     public synchronized int getMoney(int number) {
 9         synchronized (this) {
10             if (number < 0) {
11                 return -1;
12             } else if (money < 0) {
13                 return -2;
14             } else if (number - money > 0) {
15                 return -3;
16             } else {
17                 try {
18                     Thread.sleep(1000);// 模拟取钱的时间
19                 } catch (InterruptedException e) {
20                     e.printStackTrace();
21                 }
22                 money -= number;
23                 System.out.println("余额:" + money);
24             }
25             return number;
26         }
27     }
28 }
29 
30 class BankThread extends Thread {
31     private Bank bank = null;
32 
33     public BankThread(Bank bank) {
34         this.bank = bank;
35     }
36 
37     @Override
38     public void run() {
39         System.out.println("取钱:" + bank.getMoney(400));
40     }
41 }

主方法:

1         Bank bank=new Bank();
2         BankThread b1=new BankThread(bank);
3         b1.start();//柜台取钱
4         BankThread b2=new BankThread(bank);
5         b2.start();//ATM机上取钱
6     

输出结果:

余额:100

取钱:400

取钱:-3

 

同步产生死锁的原因

当一个线程已经获取了对象1的锁,同时又想获取对象2的锁。而此时另一个线程当前已经持有了对象2的锁,而又想获取对象1的锁。这种互相等待对方释放锁的过程,会导致“死锁”。

 1 class DieThread1 extends Thread {
 2     private Example example = null;
 3 
 4     public DieThread1(Example example) {
 5         super();
 6         this.example = example;
 7     }
 8 
 9     @Override
10     public void run() {
11         example.method1();
12     }
13 }
14 
15 class DieThread2 extends Thread {
16     private Example example = null;
17 
18     public DieThread2(Example example) {
19         super();
20         this.example = example;
21     }
22 
23     @Override
24     public void run() {
25         example.method2();
26     }
27 }
28 
29 class Example {
30     private Object obj1 = new Object();
31     private Object obj2 = new Object();
32 
33     public void method1() {
34         synchronized (obj1) {
35             try {
36                 Thread.sleep(1000);
37             } catch (InterruptedException e) {
38                 e.printStackTrace();
39             }
40             synchronized (obj2) {
41                 System.out.println("method1");
42             }
43         }
44     }
45 
46     public void method2() {
47         synchronized (obj2) {
48             try {
49                 Thread.sleep(1000);
50             } catch (InterruptedException e) {
51                 e.printStackTrace();
52             }
53             synchronized (obj1) {
54                 System.out.println("method2");
55             }
56         }
57     }
58 }

主方法:

1         Example example = new Example();
2         DieThread1 thread1 = new DieThread1(example);
3         thread1.start();
4         DieThread2 thread2 = new DieThread2(example);
5         thread2.start();

并没有输出method1和method2。因为都在互相等待对方释放锁,所以应该尽量不要在同步块中嵌套同步块。

posted on 2016-05-07 21:57  积_跬步  阅读(538)  评论(0编辑  收藏  举报