5.1 TestThread8.java
package com.thread;
public class TestThread8 {
public static void main(String[] args) {
ThreadDemo8 t = new ThreadDemo8();
new Thread(t).start();
new Thread(t).start();
new Thread(t).start();
new Thread(t).start();
}
}
class ThreadDemo8 implements Runnable{
int tickets = 100 ;
public void run(){
while(tickets>0){
/*线程休眠10毫秒,模拟出线程不安全的情况出现。不加sleep()也是线程不安全的,加了sleep()则不安全的结果会更容易出现。*/
try{Thread.sleep(10);}catch(Exception e){e.printStackTrace();};
System.out.println("run():"+Thread.currentThread().getName()+"is saling ticket "+tickets--);
}
}
}
结果截图:

为了确保while(tickets>0)和{system.out.println(tickets--)}代码块操作的原子性,用synchronized(对象)来实现,看下面程序。
5.2 TestThread9.java
package com.thread;
public class TestThread9 {
public static void main(String[] args) {
ThreadDemo9 t = new ThreadDemo9();
new Thread(t).start();
new Thread(t).start();
new Thread(t).start();
new Thread(t).start();
}
}
class ThreadDemo9 implements Runnable{
int tickets = 100 ;
String str = "";
public void run(){
synchronized (str) { /* synchronized (任意对象)可实现代码块的原子性*/
while(tickets>0){
//线程休眠10毫秒,模拟出线程不安全的情况出现。不加sleep()也是线程不安全的,加了sleep()则不安全的结果会更容易出现。
try{Thread.sleep(10);}catch(Exception e){e.printStackTrace();};
System.out.println("run():"+Thread.currentThread().getName()+"is saling ticket "+tickets--);
}
}
}
}
运行结果:

注意 :用synchronized(任一对象名){代码块} ,可实现代码块原子性操作,确保代码块的同步。因为每一个对象都有一个标志位,0或者1.
当str标志位为1时,线程进入代码块,str标志位立刻变为0,变为阻塞状态,其他线程就被阻塞。直到synchronized代码块执行完,str的标志位变为1,才会取消阻塞状态,java程序才能切换到其他线程执行。
str的标志位又称:监视器的锁旗标。
当其他线程访问到str监视器时,若str监视器的锁旗标为0,则其他线程将会进入一个因str监视器锁旗标而产生的等待线程池中。直到str监视器的锁旗标变为1时,才可能享有str监视器的锁旗标。
注意:String str = "" ; 要放在run()方法外。
还可以使用同步函数达到同步效果,看代码:
5.3 TestThread10.java
package com.thread;
public class TestThread10 {
public static void main(String[] args) {
ThreadDemo10 t = new ThreadDemo10();
new Thread(t).start();
new Thread(t).start();
new Thread(t).start();
new Thread(t).start();
}
}
class ThreadDemo10 implements Runnable{
int tickets = 100 ;
String str = "";
public void run(){
sale();
}
public synchronized void sale(){
while(tickets>0){
//线程休眠10毫秒,模拟出线程不安全的情况出现。不加sleep()也是线程不安全的,加了sleep()则不安全的结果会更容易出现。
try{Thread.sleep(10);}catch(Exception e){e.printStackTrace();};
System.out.println("run():"+Thread.currentThread().getName()+"is saling ticket "+tickets--);
}
}
}
注意: 在 方法名前加上 synchronized 关键字,则 sale()方法是线程安全的,监视器是 this 对象。
即可使用同步代码块来实现线程之间的同步,也可以使用同步函数来实现线程之间的同步。
怎样使得同步代码块和同步函数保持同步? 使用同一个监视器this即可。看下面的程序。
5.4 TestThread11.java
package com.thread;
public class TestThread11 {
public static void main(String[] args) {
ThreadDemo11 t = new ThreadDemo11();
new Thread(t).start();
try { Thread.sleep(1);} catch (Exception e) { e.printStackTrace();}
t.str = "method";
new Thread(t).start();
}
}
class ThreadDemo11 implements Runnable{
int tickets = 100 ;
String str = "";
public void run(){
if (str.equals("method")) {
sale();
}else {