举个生活比较常见的例子,咱们生活中过年回家的时候常常会出现需要抢票的情况,然后有一些平台会帮助我们代抢票的服务,这个时候往往会出现多个平台抢票的情况,其实这就是多线程的一种应用,我们先按照常规多线程实现一下
代码如下
public class Ticket extends Thread {
static int total=10;//这里的static是为了共享数据,因为有多个对象,不然会出现30张票,
static int buy=0;
@Override
public void run() {//模拟购票
while(total>0){
total--;
buy++;
System.out.println("已卖"+buy+"张票,剩余"+total);
}
}
public static void main(String[] args) {
Ticket t1=new Ticket();
t1.setName("携程");
Ticket t2=new Ticket();
t2.setName("飞猪");
Ticket t3=new Ticket();
t3.setName("12306");
t1.start();
t2.start();
t3.start();
}
}
乍一看好像么有什么逻辑性的错误,但是一运行问题就来了
我们会发现出现了数据混乱的问题
原因其实是就是一个线程还没有结束的时候,第二个线程就已经抢占进来了,我门来画个图可能会好懂一点
图画的有一点丑,请见谅,如上图第一个线程刚刚使用方法准备返回9,1的时候,第二个线程就在第一个线程回来的途中,开始占用方法,这时候第二个线程就会返回8,2,而第一个线程这时候还没有成功打印,这样第二个线程就会返回一个错误的信息,造成数据的混乱,也就是咱们常说的多线程安全问题;
那咱们如何解决呢?
方法就是把读取和返回绑定起来,当成一个完整的过程,这里咱们可以用synchronized()这个方法来实现,它的作用相当于一把锁,把读取和返回锁起来,实现线程信息的完整
代码实现如下
这里要注意synchronized()括号中不能用this,一定要用Ticket.class,因为这里三个对象,都要使用循环里面的方法
public class Ticket extends Thread { static int total=100; static int buy=0; @Override public void run() {//模拟购票 while (total > 0) { synchronized (Ticket.class) { if (total<=0){return;} --total; ++buy; System.out.println(Thread.currentThread().getName()+"已卖" + buy + "张票,剩余" + total); } } } public static void main(String[] args) { Ticket t1=new Ticket(); t1.setName("携程"); Ticket t2=new Ticket(); t2.setName("飞猪"); Ticket t3=new Ticket(); t3.setName("12306"); t1.start(); t2.start(); t3.start(); } }
最终的运行结果如下
当然这里的synchronized()方法不一定要放在循环里面,也可以构造buy方法,然后放在访问修饰符前面或者访问修饰符和返回类型的之间