[b]3个人玩游戏一台手柄游戏,一次只能有一个人玩 示例代码1[/b]
public class Player implements Runnable {
private final int id;
private Game game;
public Player(int id, Game game) {
this.id = id;
this.game = game;
}
public String toString() {
return "Athlete<" + id + ">";
}
public int hashCode() {
return new Integer(id).hashCode();
}
public void playGame() throws InterruptedException{
System.out.println(this.toString() + " ready!");
game.play(this);
}
public void run() {
try {
playGame();
} catch (InterruptedException e) {
System.out.println(this + " quit the game");
}
}
}
public class Game implements Runnable {
private boolean start = false;
public void play(Player player) throws InterruptedException {
synchronized (this) {
System.out.println(player.toString()+"获得锁");
while (!start){
System.out.println(player.toString()+"在等待");
wait();
System.out.println( player.toString()+"被唤醒了");
}
if (start)
System.out.println(player + " have played!");
}
}
//通知所有玩家
public synchronized void beginStart() {
start = true;
notifyAll();
}
public void run() {
start = false;
System.out.println("Ready......");
System.out.println("Ready......");
System.out.println("game start");
beginStart();//通知所有玩家游戏准备好了
}
public static void main(String[] args) throws InterruptedException {
Set<Player> players = new HashSet<Player>();
//实例化一个游戏
Game game = new Game();
//实例化3个玩家
for (int i = 0; i < 3; i++)
players.add(new Player(i, game));
//启动3个玩家
Iterator<Player> iter = players.iterator();
while (iter.hasNext())
new Thread(iter.next()).start();
Thread.sleep(100);
//游戏启动
new Thread(game).start();
}
}
[b]执行结果:[/b]
Athlete<0> ready!
Athlete<1> ready!
Athlete<2> ready!
Athlete<0>获得锁
Athlete<0>在等待
Athlete<2>获得锁
Athlete<2>在等待
Athlete<1>获得锁
Athlete<1>在等待
Ready......
Ready......
game start
Athlete<1>被唤醒了
Athlete<1> have played!
Athlete<2>被唤醒了
Athlete<2> have played!
Athlete<0>被唤醒了
Athlete<0> have played!
游戏对象game只有1个,synchronized (this)锁的实际就是game对象, 三个玩家线程都依次得到锁并在wait等待时释放锁。启动游戏线程时,通知所有线程,三个玩家线程需要竞争同步锁,得到同步锁的线程执行完synchronized(this)代码块后,自动释放同步锁,处于同步锁阻塞的剩下的2个线程再次竞争,直至都执行完毕。
[b]总结:[/b]
wait()会释放锁,下一次被唤醒并获得锁时,会紧接着wait的下一行开始执行。
obj.notifyAll()是唤醒所有在obj上等待的线程,但是notifyAll 不会释放锁,执行完synchronized 代码块时,才会释放锁。(注意:obj.notifyAll()只是唤醒所有在obj上等待的线程,并不会唤醒应用中无关的线程)
notify()是通知一个线程,notifyAll()时通知所有线程,如果上面的例子beginStart方法的notifyAll()改为notify,那么就只有一个人能玩游戏,wait状态的线程如果没被唤醒,会一直的等待,即使锁没被占用。
wait() notify() notifyAll()方法必须写在同步方法或者同步块之内,并且同步的锁必须与调用等待方法和通知方法是同一个对象(比如:synchronized(obj1){},那么调用等待方法的也必须是obj1),否则会报java.lang.IllegalMonitorStateException异常,就是因为没有拥有锁导致的。例如上例的beginStart方法,去掉synchronized ,就会报这个错误。
[b]用三个线程打印出连续10次的ABC,要求1个线程打A,1个线程打B,1个线程打C, 示例代码:[/b]
public class PrintABC {
public static Boolean isThreadA = true;
public static Boolean isThreadB = false;
public static Boolean isThreadC = false;
public static void main(String[] args) {
final PrintABC abc = new PrintABC();
new Thread(new Runnable() {
public void run() {
for (int i = 0; i < 10; i++) {
synchronized (abc) {
while(!isThreadA) {
try {
abc.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.print("A");
isThreadA = false;
isThreadB = true;
isThreadC = false;
abc.notifyAll();
}
}
}
}).start();
new Thread(new Runnable() {
public void run() {
for (int i = 0; i < 10; i++) {
synchronized (abc) {
while(!isThreadB) {
try {
abc.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.print("B");
isThreadA = false;
isThreadB = false;
isThreadC = true;
abc.notifyAll();
}
}
}
}).start();
new Thread(new Runnable() {
public void run() {
for (int i = 0; i < 10; i++) {
synchronized (abc) {
while(!isThreadC) {
try {
abc.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.print("C");
isThreadA = true;
isThreadB = false;
isThreadC = false;
abc.notifyAll();
}
}
}
}).start();
}
}
[b]执行结果:[/b]
ABCABCABCABCABCABCABCABCABCABC
abc对象是三个线程共同的锁,存在竞争。for循环中刚好可以使每个字母打印10次,for循环每次都需要竞争abc锁,竞争得到abc锁时,条件不符合则等待并释放锁,让其他线程竞争,条件符合则打印字母,打印完成后,会通知所有的线程,在执行完synchronized 块时,会释放锁。
注意:notifyAll 和wait方法的调用对象是锁,所以写成abc.notifyAll();abc.wait(); 3人玩游戏的那个例子,之所以能不写调用的对象,是因为锁就是this ,不写也是等价。
public class Player implements Runnable {
private final int id;
private Game game;
public Player(int id, Game game) {
this.id = id;
this.game = game;
}
public String toString() {
return "Athlete<" + id + ">";
}
public int hashCode() {
return new Integer(id).hashCode();
}
public void playGame() throws InterruptedException{
System.out.println(this.toString() + " ready!");
game.play(this);
}
public void run() {
try {
playGame();
} catch (InterruptedException e) {
System.out.println(this + " quit the game");
}
}
}
public class Game implements Runnable {
private boolean start = false;
public void play(Player player) throws InterruptedException {
synchronized (this) {
System.out.println(player.toString()+"获得锁");
while (!start){
System.out.println(player.toString()+"在等待");
wait();
System.out.println( player.toString()+"被唤醒了");
}
if (start)
System.out.println(player + " have played!");
}
}
//通知所有玩家
public synchronized void beginStart() {
start = true;
notifyAll();
}
public void run() {
start = false;
System.out.println("Ready......");
System.out.println("Ready......");
System.out.println("game start");
beginStart();//通知所有玩家游戏准备好了
}
public static void main(String[] args) throws InterruptedException {
Set<Player> players = new HashSet<Player>();
//实例化一个游戏
Game game = new Game();
//实例化3个玩家
for (int i = 0; i < 3; i++)
players.add(new Player(i, game));
//启动3个玩家
Iterator<Player> iter = players.iterator();
while (iter.hasNext())
new Thread(iter.next()).start();
Thread.sleep(100);
//游戏启动
new Thread(game).start();
}
}
[b]执行结果:[/b]
Athlete<0> ready!
Athlete<1> ready!
Athlete<2> ready!
Athlete<0>获得锁
Athlete<0>在等待
Athlete<2>获得锁
Athlete<2>在等待
Athlete<1>获得锁
Athlete<1>在等待
Ready......
Ready......
game start
Athlete<1>被唤醒了
Athlete<1> have played!
Athlete<2>被唤醒了
Athlete<2> have played!
Athlete<0>被唤醒了
Athlete<0> have played!
游戏对象game只有1个,synchronized (this)锁的实际就是game对象, 三个玩家线程都依次得到锁并在wait等待时释放锁。启动游戏线程时,通知所有线程,三个玩家线程需要竞争同步锁,得到同步锁的线程执行完synchronized(this)代码块后,自动释放同步锁,处于同步锁阻塞的剩下的2个线程再次竞争,直至都执行完毕。
[b]总结:[/b]
wait()会释放锁,下一次被唤醒并获得锁时,会紧接着wait的下一行开始执行。
obj.notifyAll()是唤醒所有在obj上等待的线程,但是notifyAll 不会释放锁,执行完synchronized 代码块时,才会释放锁。(注意:obj.notifyAll()只是唤醒所有在obj上等待的线程,并不会唤醒应用中无关的线程)
notify()是通知一个线程,notifyAll()时通知所有线程,如果上面的例子beginStart方法的notifyAll()改为notify,那么就只有一个人能玩游戏,wait状态的线程如果没被唤醒,会一直的等待,即使锁没被占用。
wait() notify() notifyAll()方法必须写在同步方法或者同步块之内,并且同步的锁必须与调用等待方法和通知方法是同一个对象(比如:synchronized(obj1){},那么调用等待方法的也必须是obj1),否则会报java.lang.IllegalMonitorStateException异常,就是因为没有拥有锁导致的。例如上例的beginStart方法,去掉synchronized ,就会报这个错误。
[b]用三个线程打印出连续10次的ABC,要求1个线程打A,1个线程打B,1个线程打C, 示例代码:[/b]
public class PrintABC {
public static Boolean isThreadA = true;
public static Boolean isThreadB = false;
public static Boolean isThreadC = false;
public static void main(String[] args) {
final PrintABC abc = new PrintABC();
new Thread(new Runnable() {
public void run() {
for (int i = 0; i < 10; i++) {
synchronized (abc) {
while(!isThreadA) {
try {
abc.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.print("A");
isThreadA = false;
isThreadB = true;
isThreadC = false;
abc.notifyAll();
}
}
}
}).start();
new Thread(new Runnable() {
public void run() {
for (int i = 0; i < 10; i++) {
synchronized (abc) {
while(!isThreadB) {
try {
abc.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.print("B");
isThreadA = false;
isThreadB = false;
isThreadC = true;
abc.notifyAll();
}
}
}
}).start();
new Thread(new Runnable() {
public void run() {
for (int i = 0; i < 10; i++) {
synchronized (abc) {
while(!isThreadC) {
try {
abc.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.print("C");
isThreadA = true;
isThreadB = false;
isThreadC = false;
abc.notifyAll();
}
}
}
}).start();
}
}
[b]执行结果:[/b]
ABCABCABCABCABCABCABCABCABCABC
abc对象是三个线程共同的锁,存在竞争。for循环中刚好可以使每个字母打印10次,for循环每次都需要竞争abc锁,竞争得到abc锁时,条件不符合则等待并释放锁,让其他线程竞争,条件符合则打印字母,打印完成后,会通知所有的线程,在执行完synchronized 块时,会释放锁。
注意:notifyAll 和wait方法的调用对象是锁,所以写成abc.notifyAll();abc.wait(); 3人玩游戏的那个例子,之所以能不写调用的对象,是因为锁就是this ,不写也是等价。