题目:
请编写一个多线程程序,实现两个线程,其中一个线程完成对某个对象的int类型成员变量的增加操作,即每次加1;另一个线程完成对成员变量的减小操作,即每次减1,同时保证该变量的值不会小于0,不会大于1,该变量的初始值为0。
包含int类型成员变量的类:

public class Test { // int类型成员变量 private int num; // 增加 public synchronized void increase() { if(num != 0) { try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } } num ++; System.out.println("increase :" + num); notify(); } // 减小 public synchronized void decrease() { if(num == 0) { try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } } num --; System.out.println("decrease :" + num); notify(); } }
增加成员变量的线程:

/** * 增加线程,直接继承自Thread,也可实现Runnable接口 * @author Wu * */ public class IncreaseThread extends Thread{ private Test test; public IncreaseThread(Test test) { this.test = test; } @Override public void run() { for(int i = 0; i < 10; i ++) { test.increase(); } } }
减少成员变量的线程:

/** * 减少线程,直接继承自Thread,也可实现Runnable接口 * @author Wu * */ public class DecreaseThread extends Thread{ private Test test; public DecreaseThread(Test test) { this.test = test; } @Override public void run() { for(int i = 0; i < 10; i ++) { test.decrease(); } } }
测试程序:

class Main { public static void main(String[] args) { Test test = new Test(); Thread t1 = new IncreaseThread(test); Thread t2 = new DecreaseThread(test); t1.start(); t2.start(); } }
测试结果:

increase :1 decrease :0 increase :1 decrease :0 increase :1 decrease :0 increase :1 decrease :0 increase :1 decrease :0 increase :1 decrease :0 increase :1 decrease :0 increase :1 decrease :0 increase :1 decrease :0 increase :1 decrease :0
总结:
一、关于wait,notify,notifyAll以及sleep方法的关系。
1、如果一个线程调用了某个对象的wait方法,那么该线程首先必须要拥有该对象的锁(换句话说,一个线程如果调用了某个对象的wait,那么该wait方法必须要在synchronized中)。
2、如果一个线程调用了某个对象的wait方法,那么该线程就会释放该对象的锁。
3、在Java对象中,有两种池(锁池、等待池)。
4、如果一个线程调用了某个对象的wait方法,那么该线程进入该对象的等待池中(释放锁),如果未来某个时刻,另外一个线程调用了相同的对象的notify或者notifyAll方法,那么在该等待池中的等待的线程就会起来进入该对象的 锁池中,去等待获得该对象的锁,如果获得锁成功,那么该线程将继续沿着wait方法之后的路径去执行。
5、如果一个线程调用了sleep方法睡眠,那么在睡眠的同时,它不会失去对象的锁的拥有权。
二、关于synchronized关键字的作用。
1、在某个对象的所有synchronized方法中,在某一时刻,只能有一个唯一的线程去访问这些synchronized方法。

/** * 线程t1先执行,即使睡了2秒钟,线程t2也必须等待t1释放锁,才能访问test对象的synchronized方法 * @author Wu * */ class Main { public static void main(String[] args) { Test test = new Test(); Thread t1 = new Thread(new Thread1(test)); Thread t2 = new Thread(new Thread2(test)); t1.start(); t2.start(); } } class Thread1 implements Runnable { Test test; public Thread1(Test test) { this.test = test; } @Override public void run() { test.hello(); } } class Thread2 implements Runnable { Test test; public Thread2(Test test) { this.test = test; } @Override public void run() { test.world(); } } class Test { public synchronized void hello() { try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("hello"); } public synchronized void world() { System.out.println("world"); } }
2、如果有一个方法时synchronized方法,那么该synchronized关键字表示给当前对象上锁。
3、如果一个synchronized方法是静态的,那么该synchronized关键字表示给当前对象所对应的Class对象上锁(每个类不管生成多少个对象,其对应的Class对象只有一个)。

class Main { public static void main(String[] args) { Test test1 = new Test(); Thread t1 = new Thread(new Thread1(test1)); Test test2 = new Test(); Thread t2 = new Thread(new Thread2(test2)); t1.start(); t2.start(); } } class Thread1 implements Runnable { Test test; public Thread1(Test test) { this.test = test; } @Override public void run() { test.hello(); } } class Thread2 implements Runnable { Test test; public Thread2(Test test) { this.test = test; } @Override public void run() { test.world(); } } class Test { public static synchronized void hello() { try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("hello"); } public static synchronized void world() { System.out.println("world"); } }