zoukankan      html  css  js  c++  java
  • Java并发编程--2.synchronized

    前言

    synchronized关键字是互斥锁,也称为内部锁

    每个对象都有自己的monitor(锁标记),它是为了分配给线程,最多一个线程可以拥有对象的锁

    使用

    synchronized修饰成员方法,锁的是当前实例对象

    下面是一个例子:

    class Thread2 implements Runnable{
        private int count;
        
        //修饰成员方法,锁的是调用它的对象,该例中也即是调用它的线程
        public synchronized void run() {
             for (int i = 0; i < 5; i ++) {
                  try {
                     System.out.println(Thread.currentThread().getName() + ":" + (count++));
                     
                     Thread.sleep(100);
                  } catch (InterruptedException e) {
                     e.printStackTrace();
                  }
               }
        }
    }

    调用:

    Thread2 t2 = new Thread2();
    new Thread(t2).start();
    new Thread(t2).start();

    synchronized修饰静态方法,锁的是该类的Class对象

    下面是一个例子:

    class Thread3 implements Runnable {
        private static int count;
        
        //修饰静态方法, 锁的是这个类的所有对象
        public static synchronized void getCounter() {
            for (int i = 0; i < 5; i ++) {
                 try {
                    System.out.println(Thread.currentThread().getName() + ":" + (count++));
                    Thread.sleep(100);
                 } catch (InterruptedException e) {
                    e.printStackTrace();
                 }
              }
        }
        
        @Override
        public void run() {
            getCounter();
        }
    }

    调用:

    Thread3 t3_0 = new Thread3();
    Thread3 t3_1 = new Thread3();
    new Thread(t3_0).start(); new Thread(t3_1).start(); 

    synchronized修饰代码块,锁的是()中的配置对象

     下面是一个例子:

    public class Synchronized {
        private int count;
        
        public void getCount(){
            for (int i = 0; i < 5; i ++) {
                  try {
                     System.out.println(Thread.currentThread().getName() + ":" + (count++));
                     
                     Thread.sleep(100);
                  } catch (InterruptedException e) {
                     e.printStackTrace();
                  }
               }
        }
        
        public static void main(String[] args) throws Exception {
            Thread1 t1 = new Thread1(new Synchronized());
            new Thread(t1).start();
            new Thread(t1).start();
        }
    }
    
    class Thread1 implements Runnable{
        private Synchronized s;
        
        public Thread1(Synchronized s) {
            this.s = s;
        }
        
        @Override
        public void run() {
             //修饰代码块: 锁的是()中配置的对象
             synchronized(s) {
                 s.getCount();
                 
                 try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
             }  
        }
    }

    synchronized机制

    Jvm需要对两类共享数据进行保护:

    1.堆中的实例变量
    
    2.本地方法区类变量

    对于对象来说 , 有一个监视器保护实例变量; 对于类来说, 有一个监视器保护类变量

    Jvm为每个对象和类关联一个锁,如果某个线程获取了锁, 在它释放锁之前其它的线程时不能获得同样的锁的;

    对于一个对象,Jvm会维护一个加锁计数器,线程每获得一次该对象, 计数器+1, 每释放一次, 计数器-1,当计数器=0时,锁就完全释放了

    死锁

    当线程需要持有多个锁时, 就有可能发生死锁的情况, 比如下面这个情形:

    A线程首先获得lock1,在获得lock2;   B线程首先获得lock2,在获得lock1, 
    当A线程获得lock1时,B线程获得lock2, 于是A线程等待lock2, B线程等待lock1,
    两个线程会无限的等待,这就发生了死锁现象

    下面是一个例子:

    public class Deadlock {
        //监视器对象
        private final Object lock1 = new Object();
        private final Object lock2 = new Object();
        
        public void instanceMethod1(){
            synchronized(lock1){
                System.out.println("线程1: 获得lock1,等待lock2");
                synchronized(lock2)    {
                    System.out.println("线程1:获得lock2");
                }
            }
        }
        
        public void instanceMethod2(){
            synchronized(lock2){
                System.out.println("线程2: 获得lock2,等待lock1");
                synchronized(lock1){
                    System.out.println("线程2: 获得lock1");
                }
            }
        }
        
        public static void main(String[] args){
            final Deadlock dld = new Deadlock();
            
            Runnable r1 = new Runnable(){
                @Override
                public void run(){
                    while(true){
                        dld.instanceMethod1();
                        try{
                            System.out.println("线程1: 睡眠");
                            Thread.sleep(1000);
                        }
                        
                        catch (InterruptedException ie){
                        }
                    }
                }
            };
            
            Runnable r2 = new Runnable(){
                @Override
                public void run(){
                    while(true) {
                        dld.instanceMethod2();
                        try {
                            System.out.println("线程2: 睡眠");
                            Thread.sleep(1000);
                        }
                        catch (InterruptedException ie){
                        }
                    }
                }
            };
            
            Thread thdA = new Thread(r1);
            Thread thdB = new Thread(r2);
            
            thdA.start();
            thdB.start();
        }
        
    }

    控制台输出:

    线程1: 获得lock1,等待lock2
    线程2: 获得lock2,等待lock1

    避免发生死锁

    生产中,死锁现象一旦发生,很可能会造成灾难性的后果,我们在编码中应该避免死锁现象发生

    、尽量不要编写在同一时刻需要持有多个锁的代码;
    
    、创建和使用一个大锁来代替若干小锁,并把这个锁用于互斥,而不是用作单个对象的对象级别锁;

    锁的优化

    synchronize采取独占的方式,它属于悲观锁,它假设了最坏的情况,如果持有锁的线程延迟,其他等待程序就会测试,程序停滞不前

    锁自旋

    在等待锁时,线程不会立即进入阻塞状态,而是先等一段时间看锁是否被释放

    偏向锁

    一个线程获得了锁,如果在接下来没有别的线程获得该锁,这个锁会偏向第一个获得它的线程,使一个线程多次获得锁的代价更低 

    锁膨胀

    多次调用粒度太小的锁, 不如一次调用粒度大的锁

    轻量级锁

    如果存在锁的竞争,除了互斥量的开销外,还会发生CAS操作,在同步期间如果没有锁竞争,使用轻量级锁避免互斥量的开销

  • 相关阅读:
    python-flask-SQLAlchemy
    python-flask-script定制manage命令
    python-flask框架
    python-flask-wtforms
    python-思路整理-虚拟环境
    python--django-admin定制页面流程:
    友盟iOS微信登陆没有回调的原因
    用同一个工程创建两个不同版本的应用
    简单方法实现手势解锁
    iOS开发中地图开发的简单应用
  • 原文地址:https://www.cnblogs.com/liuconglin/p/6671066.html
Copyright © 2011-2022 走看看