一、同步问题的引出
1、问题
以卖火车票为例,如果现在要是想买大车票的话可以去火车站买或者去各个售票点,但是不管有多少个地方可以买火车票。最终一趟列车的车票数量是固定的, 如果把各个售票点理解为各个线程的话,则所有线程应该共同拥有同一份的票数。
代码实现如下:
class BuyTicketThread implements Runnable{
// 假设一共有5张票
private int ticket = 5 ;
public void run(){
//有这些人进行抢票
for(int i=0;i<100;i++){
//判断是否有票
if(ticket>0){ // 还有票
try{
Thread.sleep(300) ; // 加入延迟
}catch(InterruptedException e){
e.printStackTrace() ;
}
ticket--;
//输出目前的票数
System.out.println("得到一张票,现在票数:ticket = " + ticket );
}
}
}
}
public class BuyTicket {
public static void main(String[] args) {
// 定义线程对象
BuyTicketThread mt = new BuyTicketThread() ;
// 定义Thread对象代表不同的售票点
Thread t1 = new Thread(mt) ;
Thread t2 = new Thread(mt) ;
Thread t3 = new Thread(mt) ;
t1.start() ;
t2.start() ;
t3.start() ;
}
}
输出结果每次运行都不一样,此处选取一种结果如下。
得到一张票,现在票数:ticket = 3
得到一张票,现在票数:ticket = 2
得到一张票,现在票数:ticket = 3
得到一张票,现在票数:ticket = 1
得到一张票,现在票数:ticket = -1
得到一张票,现在票数:ticket = 0
此时,我们将发现结果完全不符合实际情况,数据出现混乱。这是因为多个线程访问同一个共享变量并对其进行修改,发生数据争用。
如果想解决这样的问题,就必须使用同步,所谓的同步就是指多个操作在同一时间段内只有一个线程运行,其他线程要等待此线程完成之后才可以继续执行。
2、解决问题——使用同步代码块
在代码块上加上“” 关键字的话,则此代码块就被称为同步代码块。
同步代码块格式:
synchronized(同步对象){
}
同步的时候,必须指明同步的对象,一般情况下会将当前对象作为同步对象,使用this表示。
代码如下:
class BuyTicketThread implements Runnable{ // 假设一共有5张票 private int ticket = 5 ; public void run(){ //有这些人进行抢票 for(int i=0;i<100;i++){ //判断是否有票 synchronized (this) {// 要对当前对象进行同步 if(ticket>0){ // 还有票 try{ Thread.sleep(300) ; // 加入延迟 }catch(InterruptedException e){ e.printStackTrace() ; } ticket--; //输出目前的票数 System.out.println("得到一张票,现在票数:ticket = " + ticket ); } } } } } public class BuyTicket { public static void main(String[] args) { // 定义线程对象 BuyTicketThread mt = new BuyTicketThread() ; // 定义Thread对象代表不同的售票点 Thread t1 = new Thread(mt) ; Thread t2 = new Thread(mt) ; Thread t3 = new Thread(mt) ; t1.start() ; t2.start() ; t3.start() ; } }
得到一张票,现在票数:ticket = 4 得到一张票,现在票数:ticket = 3 得到一张票,现在票数:ticket = 2 得到一张票,现在票数:ticket = 1 得到一张票,现在票数:ticket = 0