Java关键字-synchronized理解

1.作用及版本

  • 多线程环境下用来控制资源同步访问的,同步控制的代码块是原子操作。
  • JDK1.6以前的synchronized是一把重量级锁,监视器锁(monitor),当一个线程获取到锁之后,其他线程想要获取锁就必须等待,也就是其他线程是阻塞状态(Blocked)。当前线程获取到锁执行完之后释放锁,这时阻塞队列里面的线程会去竞争锁,竞争到锁的线程又继续执行。由于这个时候发生了线程切换,因此操作系统由用户态变成核心态,这个过程是比较耗时的,因此在JDK1.6以后对synchronized关键字进行了优化。

2.作用域

  • 方法
    • 静态方法:相当于类锁,所有对象共享类锁。

    • package com.sap.leo;
      
      
      public class Concurrency extends  Thread{
          private static int data = 0;
      
          public Concurrency(String name)
          {
              super(name);
          }
          public static void main(String[] args) throws InterruptedException {
              Concurrency c1 = new Concurrency("c1");
              Concurrency c2 = new Concurrency("c2");
              Concurrency c3 = new Concurrency("c3");
              Concurrency c4 = new Concurrency("c4");
              c1.start();
              c2.start();
              c3.start();
              c4.start();
              Thread.sleep(60*2*1000);
      
          }
      
          @Override
          public void run() {
              synchronized (Concurrency.class)
              {
                  try {
                      if(Thread.currentThread().getName().equals("c1"))
                      {
                          Thread.currentThread().sleep(60000);
                      }
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }
              }
          }
      }
      
    • Java关键字-synchronized理解_第1张图片

    •  Java关键字-synchronized理解_第2张图片

    • 非静态方法:对象锁;

    • package com.sap.leo;
      
      
      public class Concurrency extends  Thread{
          public static void main(String[] args) throws InterruptedException {
              Concurrency c = new Concurrency();
              Thread t1 = new Thread(c,"t1");
              Thread t2 = new Thread(c,"t2");
              Thread t3 = new Thread(c,"t3");
              Thread t4 = new Thread(c,"t4");
              t1.start();
              t2.start();
              t3.start();
              t4.start();
              Thread.sleep(10000);
          }
      
          @Override
          public synchronized  void run()
          {
              if("t1".equals(Thread.currentThread().getName()))
              {
                  System.out.println("t1 get lock...");
                  System.out.println("t1 sleep 5s...");
                  try {
                      Thread.sleep(5000);
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }
                  System.out.println("t1 release lock...");
              }
              else
              {
                  System.out.println(Thread.currentThread().getName());
              }
          }
      
      }
      

      Java关键字-synchronized理解_第3张图片

  • 代码块

3.字节码层面分析

package com.sap.leo;

public class Concurrency {
    private int data = 0;
    public static void main(String[] args) throws InterruptedException {
        Concurrency c = new Concurrency();
        for(int i=0;i<1000;i++)
        {
            new Thread(()->{
               c.manipulateData();
            }).start();
        }
        Thread.sleep(5000);
        System.out.println(c.data);

    }

    public int manipulateData()
    {
        synchronized (this)
        {
            data++;
        }
        return this.data;
    }
}
  • synchronized语句同步代码块是使用的monitorenter和monitorexit指令,monitorenter表示进入同步代码块,当线程获取到锁之后,锁的计数器就加1,当执行到monitorexit的时候,锁的计数器减1。synchronized的对象锁是可以重入的,没重入一次对象锁的计数器就加1.
  • 下图有两个monitorexit,第一个表示锁释放之后正常退出,第二个monitorexit表示异常情况的退出。
  • Java关键字-synchronized理解_第4张图片

4.synchronized滥用:死锁

  •  
    package com.sap.leo.concurrency;
    
    
    public class DeadLock
    {
        private static String resource1 = "r1";
        private static String resource2 = "r2";
        public static void main(String[] args)
        {
            Thread t1 = new Thread(new Runnable()
            {
                
                @Override
                public void run()
                {
                    synchronized (resource1)
                    {
                        System.out.println("Thread "+ Thread.currentThread().getName() + " got resource1..." );
                        long startTime = System.currentTimeMillis();
                        System.out.println("Wait for getting resource2... ");
                        try
                        {
                            Thread.currentThread().sleep(1000);
                        }
                        catch (InterruptedException e)
                        {
                              
                            // TODO Auto-generated catch block  
                            e.printStackTrace();  
                            
                        }
                        synchronized (resource2)
                        {
                            System.out.println("Thread "+ Thread.currentThread().getName() + " got resource2..." );
                        }
                        
                    }
                    
                }
            }, "t1");
            t1.start();
            
            
            Thread t2 = new Thread(new Runnable()
            {
                
                @Override
                public void run()
                {
                    Thread.currentThread().yield();
                    synchronized (resource2)
                    {
                        System.out.println("Thread "+ Thread.currentThread().getName() + " got resource2..." );
                        try
                        {
                            System.out.println("Wait for getting resource1... ");
                            Thread.currentThread().sleep(1000);
                            
                        }
                        catch (InterruptedException e)
                        {
                            e.printStackTrace();  
                        }
                        synchronized (resource1)
                        {
                            System.out.println("Thread "+ Thread.currentThread().getName() + " got resource1..." );
                        }
                    }
                    
                }
            }, "t2");
            t2.start();
            
        }
    }
     
    

     

5.synchronized锁优化技术(JDK1.6以后)

  • 偏向锁:在多线程不存在竞争的情况下,第一次拿到锁的线程在后面更有机会再次获取到锁。如果有锁竞争,那么就会膨胀成轻量级锁。采用CAS算法。偏向锁在Mark Word(32位)中锁标志位(2字节)为01,1位用来专门标志它是标志位。
  • 轻量级锁:当多线程竞争执行时,线程A尝试使用CAS去修改Mark Word,修改成功,锁标志位设置为00;线程B修改Mark Word失败则自旋,自旋还是失败的话,锁会膨胀为重量级锁。
  • 重量级锁:线程A获取到锁,其他线程都得阻塞,不需要自旋,整个处理时间比较缓慢。

你可能感兴趣的:(Java,基础知识)