Java并发synchronized详解

今天和大家一起学习下并发编程,先举一个简单的生活例子,我们去医院或者银行排队叫号,那每个工作人员之间如何保证不会叫重号呢?

public class TicketDemo extends Thread {   
    private int index = 1;    
    private static final int MAX = 10;    
    @Override    
    public void run() {        
        while (index <= MAX) {            
                        System.out.println(Thread.currentThread().getName() + "呼叫的号码是:" + (index++));       
        }   
   }    
   
   public static void main(String[] args) {        
        TicketDemo t1 = new TicketDemo();        
        TicketDemo t2 = new TicketDemo();        
        TicketDemo t3 = new TicketDemo();        
        TicketDemo t4 = new TicketDemo();        
        t1.start();        
        t2.start();        
        t3.start();        
        t4.start();    
  }
}

运行结果
在这里插入图片描述

我们发现,每一个线程都拥有自己的index变量,我们需要将index改成共享变量,有static

public class TicketDemo extends Thread {   
    private static int index = 1;    
    private static final int MAX = 10;    
    @Override    
    public void run() {        
        while (index <= MAX) {            
            System.out.println(Thread.currentThread().getName() + "呼叫的号码是:" + (index++));       
        }   
   }    
   
   public static void main(String[] args) {        
        TicketDemo t1 = new TicketDemo();        
        TicketDemo t2 = new TicketDemo();        
        TicketDemo t3 = new TicketDemo();        
        TicketDemo t4 = new TicketDemo();        
        t1.start();        
        t2.start();        
        t3.start();        
        t4.start();    
  }
}

运行结果
在这里插入图片描述

但是我们如果将号码增到5000,50000万时,会出现跳号、重号、超过最大值的情况
在这里插入图片描述

我们可以通过synchronized将并发的代码加锁

public class TicketDemo extends Thread {   
    private static int index = 1;    
    private static final int MAX = 10;    
    @Override    
    public void run() {        
        while (index <= MAX) {            
            synchronized (this) {
                System.out.println(Thread.currentThread().getName() + "呼叫的号码是:" + (index++));       
        }   
            }
   }    
   
   public static void main(String[] args) {        
        TicketDemo t1 = new TicketDemo();        
        TicketDemo t2 = new TicketDemo();        
        TicketDemo t3 = new TicketDemo();        
        TicketDemo t4 = new TicketDemo();        
        t1.start();        
        t2.start();        
        t3.start();        
        t4.start();    
  }
}

结果输出
在这里插入图片描述

在Java中,每个对象都会有一个monitor对象,监视器。

  1. 某一线程占有这个对象的时候,先monitor的计数器是不是0,如果是0说明还没有线程占有,这个时候线程占有这个对象,并且这个对象的monitor+1,;如果不为0,表示这个线程已经被其他线程占有,这个线程等待。当线程释放占有权的时候,monitor-1

  2. 使用synchronized对代码块进行加锁,是使用monitorenter和monitorexit配合使用
    [外链图片转存失败(img-ZyQooRdd-1569138105576)(en-resource://database/783:1)]

  3. 使用synchronized对方法进行加锁,使用ACC_SYNCHRONIZED
    [外链图片转存失败(img-oCTOEBj3-1569138105577)(en-resource://database/785:0)]

锁:
jdk1.6以前,都是重量锁
Java虚拟机对synchronized的优化:

  • 偏向锁:在对象第一次被某一线程占有的时候,是否偏向锁职位1,锁表01,写入线程号;当其他的线程访问的时候,竞争;竞争的结果有两种,成功或失败。很多次被第一次占有它的线程获取次数多
    CAS算法:compare and set
    竞争不激烈的时候适用
  • 轻量级锁:线程有交替使用,互斥性不是很强,CAS失败,置为00
  • 重量级锁(等待时间长):强互斥,置为10
  • 自旋锁:竞争失败的时候,不是马上转化级别,而是执行几次空循环
  • 锁消除:JIT进行编译的时候,把不必要的锁去掉
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值