线程系列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 }

 

你可能感兴趣的:(线程同步)