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 }