一个简单的例子,10个人抢8张票,结果肯定是有2个人抢不到票的
public class Ticket {
private static CountDownLatch ctl =new CountDownLatch(1);
private ReentrantLock rentLock=new ReentrantLock();
Integer stock=8;//总票数
public void reduce(int num){
if((stock-num)>=0){
try {
ctl.await();//模拟并发场景,相当于所用线程同时到达才会执行
} catch (Exception e) {
e.printStackTrace();
}
stock-=num;
System.out.println(Thread.currentThread().getName()+
"成功卖出:"+num+"张,库存剩余:"+stock+"张");
}else {
System.out.println(Thread.currentThread().getName()+
"失败,库存不足:"+num+"张,库存剩余:"+stock+"张");
}
}
public static void main(String[] args) throws InterruptedException {
final Ticket ticket=new Ticket();
for (int i = 0; i <10; i++) {
new Thread(new Runnable() {
public void run() {
ticket.reduce(1); #只有10个线程同时开启时,才会执行该方法,因为reduce方法中调用了ctl.await方法
//ticket.reduceByLock(1);
}
}).start();;
}
Thread.sleep(1000);
ctl.countDown(); //启动一个线程,计数器减一
}
}
运行结果:
出现的负数,这就是线程安全引起的,只要把Ticket 中的reduce 方法加上synchronized 修饰,就可以解决这个问题
一 同步方法 方法加用 synchronized 修饰
public synchronized void reduce(int num){
if((stock-num)>=0){
try {
ctl.await();//模拟并发场景,相当于所用线程同时到达才会执行
} catch (Exception e) {
e.printStackTrace();
}
stock-=num;
System.out.println(Thread.currentThread().getName()+
"成功卖出:"+num+"张,库存剩余:"+stock+"张");
}else {
System.out.println(Thread.currentThread().getName()+
"失败,库存不足:"+num+"张,库存剩余:"+stock+"张");
}
}
再次运行结果:
为什么呢,因为加了synchronized 关键字后,会保证每次只会用一个线程去调用reduce这个方法,当一个线程1在执行这个方法的时候,会获得锁,其他线程阻塞等待,线程1执行完之后,释放锁,其他线程竞争锁。得到锁的线程执行reduce方法,其他线程阻塞等待,以此类推,直到10个线程都执行完
加在方法上的锁(也叫同步方法),锁的都是对象,例子中执行时锁的就是这个 ticket 对象(也就是调用reduce这个方法的对象)
同步方法的缺点:因为锁在方法上,导致了锁的范围过大,会使单个线程串行执行时间变长,其他线程等待时间变长;影响系统性能