一、首先看下面一个例子:
已知车票售卖系统中有1000张车票,有3个售票点在随机售卖,请模拟出这3个售票点卖出这些车票的过程。
根据以上题目,编程如下:
/**
* 模拟3个售票点,共同卖出1000张票
*
*/
public class Test1 {
public static void main(String[] args) {
SellTicket st = new SellTicket();
Thread t1 = new Thread(st);
Thread t2 = new Thread(st);
Thread t3 = new Thread(st);
t1.start();
t2.start();
t3.start();
}
}
class SellTicket implements Runnable
{
int total=1000;
int sold=0;
@Override
public void run() {
// TODO Auto-generated method stub
while(true){
if(total>0){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
sold++;
System.out.println(Thread.currentThread().getName()+
"正在售出第"+sold+"张车票");
total--;
}else if(total<0){
break;
}
}
}
}
输出结果
Thread-1正在售出第2张车票
Thread-2正在售出第3张车票
Thread-0正在售出第1张车票
Thread-0正在售出第5张车票
Thread-2正在售出第5张车票
Thread-1正在售出第6张车票
Thread-2正在售出第7张车票
Thread-0正在售出第8张车票
Thread-1正在售出第9张车票
从结果中可以看出
1、没有卖出第4张车票。 2、第5张车票被卖出了2次。
这明显有悖于常理“1张车票,怎么能被两个人同时买到呢?”
分析原因:
从以上代码中可以看到,st对象被3个线程同时启动,因此st对象中是变量是对于这3个线程而言是共享的,任何一个线程都可以调用。
在执行 total- -之前,存在sleep(1000)。
线程-0在售出第4张车票的时候,即执行到sold++完毕后,此时在st对象中 sold=4;同时线程-2 刚好等待了1s,也马上执行到sold++,执行完成sold++后,st对象中sold=5;由于3个线程共享st对象中的变量,当线程-0开始执行System.out.println( )时,它调取st对象中的sold变量=5,因此线程-0输出5;线程-2同样马上执行System.out.println()
,它也调取st对象中的sold变量=5,线程-2输出5。在这期间,线程-1还在等待。
这就是多线程并发,带来的线程安全问题。
二、怎样处理线程并发造成的影响?
办法很简单,即想办法保证“一个线程在执行某段代码的时候,其他线程需要等待它执行完成后才能继续执行”,即保证代码的"原子性"。
具体怎样操作呢?在需要保证“原子性”的代码外加上synchronized(Object){ …}。
为以上代码增加Synchronized后,如下:
/**
* 模拟3个售票点,共同卖出1000张票
*
*/
public class Test1 {
public static void main(String[] args) {
SellTicket st = new SellTicket();
Thread t1 = new Thread(st);
Thread t2 = new Thread(st);
Thread t3 = new Thread(st);
t1.start();
t2.start();
t3.start();
}
}
class SellTicket implements Runnable
{
int total=1000;
public void run() {
// TODO Auto-generated method stub
while(true){
synchronized(this){
if(total>0){
System.out.println(Thread.currentThread().getName()+
"正在售出第"+total+"张车票");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
total--;
}else if(total<0){
break;
}
}
}
}
}