线程系列2---线程同步

2013-12-23 10:31:55 

1. 由于系统的线程调度很随机(其实也是按照一定的策略来调度的,但对于程序来说是随机的,毕竟是无法由程序控制),所以当多个线程访问共享资源时就有可能产生线程同步问题。

2. 为了解决线程的同步问题,可以使用如下几种方式:

    2.1 同步代码块,用synchronized(obj) { }将需要同步的代码括起来,obj指的是同步监视器,含义是线程在开始执行这段代码之前需要先获得同步监视器的锁。

          1> 任何时刻,只有一个线程可以获得对同步监视器的锁定,当同步代码执行完成后释放锁。

          2> 虽然Java程序允许使用任何对象作为同步监视器,但通常推荐使用可能被并发访问的共享资源充当同步监视器。

    2.2 同步方法:指的是用synchronized修饰的方法,无需显式的指定同步监视器,因为同步监视器就是this,使用同步方法可以构造线程安全的类。线程安全的类有如下特征:

          1> 该类的对象可以被多个线程同时安全的访问;

          2> 每个线程调用该对象的任意方法均能得到正确的结果;

          3> 每个线程调用该对象的任意方法之后,该对象状态依然保持在合理状态;

3. 可变类的线程安全是以降低程序的运行效率为代价的,所以:

          1> 不要对线程安全类的所有方法都进行同步,只对那些会改变竞争资源的方法同步;

          2> 如果可变类有两种运行环境,那么最好为这个类提供不同的版本,如StringBuffer和StringBuilder。

4. 释放同步监视器的锁定:

          1> 当前线程的同步方法或者同步代码块执行结束即释放该同步监视器;

          2> 当前线程在同步方法或者同步代码块中遇到了break、return或者未捕获的Error or Exception终止该代码块;

          3> 当前线程执行同步代码块或者同步方法时,程序执行了同步监视器对象的wait()方法。

5. 以下情况,线程不会释放同步监视器:

          1> 执行时,程序调用sleep(),yield()放暂停当前线程的执行;

          2> 执行时,其他线程调用该线程的suspend()方法将该线程挂起;

6. Java 5开始提供了一种更强大的线程同步机制---通过显式定义同步锁对象实现同步,同步锁使用Lock对象充当。

7. 死锁:当两个线程互相等待对方释放同步监视器时就会发生死锁。一旦出现死锁,程序既不会发生异常,也不会给出任何提示,只是所有线程处于Block状态无法继续。

8. 线程通信:

    传统的线程通信:可借助Object对象的三个方法wait(), notify()和notifyAll()。这三个方法不属于Thread类,且必须由同步监视器对象来调用。
    关于三个方法的解释:
          wait(): 导致当前线程暂停,释放锁,直到其他线程调用该同步监视器的notify()方法来唤醒这个线程。
          notify(): 唤醒此同步监视器上等待的单个线程,如果有多个,则会随机唤醒一个。只有当前线程放弃对该同步监视器的锁定,才会执行被唤醒的线程。
          notifyAll():唤醒在此同步监视器上等待的所有线程,只有当前线程放弃对该同步监视器的锁定,才会执行被唤醒的线程。

9. 线程同步实例:模拟存款和取款,要求:存一次,取一次,不允许连存或者连取,代码如下:

Account.java

 1 public class Account {
 2     private String mAccountNo;
 3     private int mBalance;
 4     private boolean flag;
 5    
 6     public Account() {
 7     }
 8    
 9     public Account(String accountNo, int balance) {
10         this.mAccountNo = accountNo;
11         this.mBalance = balance;
12     }
13    
14     public int getBalance() {
15         return mBalance;
16     }
17    
18     public synchronized void draw(int drawAccount) {
19         try {
20             if (!flag) {
21                 this.wait();
22             } else {
23                 System.out.println(Thread.currentThread().getName() + " 取款 :" + drawAccount);
24                 mBalance -= drawAccount;
25                 System.out.println("--------余额是 :" + mBalance);
26                 flag = false;
27                 notifyAll();
28             }
29         } catch (InterruptedException e) {
30             e.printStackTrace();
31         }
32     }
33    
34     public synchronized void despoit(int despoiteAccount) {
35         try {
36             if (flag) {
37                 this.wait();
38             } else {
39                 System.out.println(Thread.currentThread().getName() + " 存款 :" + despoiteAccount);
40                 mBalance += despoiteAccount;
41                 System.out.println("=======余额是 :" + mBalance);
42                 flag = true;
43                 notifyAll();
44             }
45         } catch (InterruptedException e) {
46             e.printStackTrace();
47         }
48     }
49 }

DespoiteThread.java

 1 public class DespoiteThread extends Thread {
 2     Account mAccount;
 3     private int mDespioteAccount;
 4   
 5     public DespoiteThread(String name, Account account, int despioteAccount) {
 6         super(name);
 7         mAccount = account;
 8         mDespioteAccount = despioteAccount;
 9     }
10   
11     @Override
12     public void run() {
13         for (int i = 0; i < 100; i++) {
14             mAccount.despoit(mDespioteAccount);
15         }
16     }
17 }

DrawThread.java

 1 public class DrawThread extends Thread {
 2     Account mAccount;
 3     private int mDrawAccount;
 4   
 5     public DrawThread(String name, Account account, int drawAccount) {
 6         super(name);
 7         mAccount = account;
 8         mDrawAccount = drawAccount;
 9     }
10   
11     @Override
12     public void run() {
13         for (int i = 0; i < 100; i++) {
14             mAccount.draw(mDrawAccount);
15         }
16     }
17 }

MainTest.java

1 public class MainTest {
2     public static void main(String[] args) {
3         Account account = new Account("DaWei", 1000);
4         new DrawThread("取款者", account, 600).start();
5         new DespoiteThread("存款者", account, 800).start();
6     }
7 }

 

原文地址:https://www.cnblogs.com/wlrhnh/p/3487032.html