在两个线程共卖100张票的时候,也会出现问题,同时卖同一张票。
程序如下:
public class TestThread implements Runnable{
private int j=100;
public void run(){
for(int i = 0 ;i<100;i++){
if(j>0){
try {
Thread.sleep(300);//注意这里休眠了300毫秒
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("卖第"+j+"张票!");
j--;
}
}
}
}
public static void main(String[] args) {
TestThread m=new TestThread();
new Thread(m).start();
new Thread(m).start();
}
结果:
卖第100张票!
卖第100张票!
卖第98张票!
卖第98张票!
......
造成此问题的根本原因在于,判断剩余票数和修改票数之间加入了延迟操作。
如果想解决这样一个问题,就必须使用同步,所谓的同步就是指多个操作在同一时间段内只能有一个线程进行,其他线程要等待此线程完成之后才可以继续执行。
在Java中可以通过同步代码的方法进行代码的加锁操作,同步的实现由两种方式:同步代码块和同步方法
1. 同步代码块:使用synchronized关键字进行同步代码块的声明,但是在使用此操作时必须明确的指出到底要锁定的是哪个对象,一般都是以当前对象为主:
synchronized(对象){//一般都是将this进行锁定
需要同步的代码;
}
使用同步代码块修改之前的程序:
public class TestThread implements Runnable{
private int j=100;
public void run(){
for(int i = 0 ;i<100;i++){
synchronized (this) {//同步代码块,要将if判断条件包括在其中,否则会出现卖出第0张票的问题
if(j>0){
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("卖第"+j+"张票!");
j--;
}
}
}
}
}
2. 同步方法:将要同步的代码块抽取成方法
public class TestThread implements Runnable {
private int j = 100;
public void run() {
for (int i = 0; i < 100; i++) {
sale();
}
}
public synchronized void sale() {//同步方法
if (j > 0) {
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("卖第" + j + "张票!");
j--;
}
}
}