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 }