以一段简单的代码来展示一下多线程会出现的问题
public class blog {
public static void main(String[] args) {
GoodsNum goodsNum=new GoodsNum(10);
Operate operate=new Operate(goodsNum);
new Thread(()->{
while (goodsNum.getNum()>0){
operate.sale();
}
},"1").start();
new Thread(()->{
while (goodsNum.getNum()>0){
operate.sale();
}
},"2").start();
}
}
class GoodsNum {
private int num;
public GoodsNum() {
}
public GoodsNum(int num) {
this.num = num;
}
public int getNum() {
return num;
}
public void setNum(int num) {
this.num = num;
}
}
class Operate {
private GoodsNum goods;
public Operate(GoodsNum goods) {
this.goods = goods;
}
public void sale(){
if(goods.getNum()>0) {
System.out.println(Thread.currentThread().getName() + ":第" + goods.getNum() + "件卖出剩余" + (goods.getNum() - 1) + "件");
goods.setNum(goods.getNum() - 1);
}
}
}
由于多线程执行的结果是不可控的,所以代码执行结果为其中的一次
1:第10件卖出剩余9件
2:第10件卖出剩余9件
2:第8件卖出剩余7件
1:第9件卖出剩余8件
2:第7件卖出剩余6件
1:第6件卖出剩余5件
2:第5件卖出剩余4件
1:第4件卖出剩余3件
2:第3件卖出剩余2件
1:第2件卖出剩余1件
2:第1件卖出剩余0件
从执行结果中我们可以发现出现了几处不合理的地方,例如:
1:第10件卖出剩余9件
2:第10件卖出剩余9件
第一个线程已经卖了第10件商品但是第二个线程又卖了第10件商品,同一件商品被卖了两次是不合理的,这种情况的出现,实际上是cpu调度的问题,线程是否执行,执行多久都是由cpu决定的,可能一个线程并没有执行完,cpu就让下一个线程开始执行,让刚才未执行完的线程先处于就绪状态,这就会导致上述问题的出现。
用代码来解释一下出现两个线程卖同一个商品的情况,线程中执行的实际上是sale方法,所以用sale方法来说明
public void sale(){
if(goods.getNum()>0) {
System.out.println(Thread.currentThread().getName() + ":第" + goods.getNum() + "件卖出剩余" + (goods.getNum() - 1) + "件");
goods.setNum(goods.getNum() - 1);
}
}
如果第一个线程在执行时,sale方法执行到一半时cpu开始调度第二个线程,此时第一个线程会突然停止执行处于就绪状态,并且由于未执行完打印语句导致并未打印,等到再次被调度时才会打印一开始的结果,此时就会出现同一个商品被卖两次的情况。
用代码来展示锁(synchronized)的运用
class Operate {
private GoodsNum goods;
public Operate(GoodsNum goods) {
this.goods = goods;
}
public synchronized void sale() {
int num= goods.getNum();
if (goods.getNum() > 0) {
System.out.println(Thread.currentThread().getName() + ":第" + num + "件卖出剩余" + --num + "件");
goods.setNum(num);
}
}
}
在实例对象中加锁实际上是在给对象加锁而不是给实例方法加锁,在加锁后当一个线程拿到对象锁后可以执行它方法,而其它线程就拿不到锁也就无法执行对象的方法需要等待拿锁线程执行完方法,才有机会拿锁并执行方法,这样线程会更加安全,不会出现以下结果
1:第10件卖出剩余9件
2:第10件卖出剩余9件
2:第8件卖出剩余7件
1:第9件卖出剩余8件
可保证商品不重卖和顺序卖出,但加锁后会降低代码执行效率,因为在一个线程在执行时其它线程处于阻塞状态。