synchronized随笔
synchronized随笔

synchronized随笔

synchronized(互斥锁/悲观锁),实现线程同步,让多个线程排队依次获取某个资源,保证数据不会出错,区别于乐观锁,常用在写操作较多的场景,如金融、交易等业务。

修饰方法

  • 非静态方法 – 锁定的是方法的调用者,即类new出来的实例
class Data{
    public synchronized void fun1(){
        try {
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println("1...");
    }

    public synchronized void fun2(){
        System.out.println("2...");
    }
}
public class Main {
    public static void main(String[] args) {
        Data data = new Data();
        new Thread(()->{
            data.func1();
        },"A").start();
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        new Thread(()->{
            data.func2();
        },"B").start();
}

①锁定两个非静态方法,且使用的是同一个类的实例的情况下,则会触发synchronized,需要等func1执行完成后释放锁,func2才执行(锁调用者)

②锁定一个方法则不构成锁的竞争关系,所以func2会直接执行,无需等待func1释放锁

①输出结果:

1…

2…

②输出结果:

2…

1…

  • 静态方法 – 锁定的是类
class Data{
    public synchronized static void fun1(){
        try {
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println("1...");
    }

    public synchronized static void fun2(){
        System.out.println("2...");
    }
}
public class Main {
    public static void main(String[] args) {
        Data data1 = new Data();
        Data data2 = new Data();
        new Thread(()->{
            data1.func1();
        },"A").start();
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        new Thread(()->{
            data2.func2();
        },"B").start();
}

①锁定两个静态方法,即使调用的是两个不同的实例,也会需要等待锁(锁类)

②如果指只锁定一个静态方法,则调用另一个方法不需要等待锁

①输出结果:

1…

2…

②输出结果:

2…

1…

修饰代码块 – 锁定的是传入对象

  • 例1:传入(this)
class Data {
   public void fun(){
       synchronized (this){//锁对当前的实例化对象(this)起作用
           System.out.println("start...");
           try {
               TimeUnit.SECONDS.sleep(1);
           } catch (InterruptedException e) {
               throw new RuntimeException(e);
           }
           System.out.println("end...");
       }
   }
}
①public static void main(String[] args) {
     Data data = new Data();
     for (int i = 0; i < 5; i++) {
         new Thread(() -> {
             data.fun();
         },"A").start();
     }
}
②public static void main(String[] args) {
      for (int i = 0; i < 5; i++) {
          Data data = new Data();
          new Thread(() -> {
              data.fun();
          },"A").start();
      }
}

传入对象为this时,会锁住当前类的实例化对象。

在①中,因为只有一个实例化对象,所以五个线程需要排队,输出结果是start…\n end………交替输出。

在②中,因为每次创建线程的同时也会new一个新的对象,所以不需要排队,输出结果是start….\n start….……直到最后一个end…。

  • 例2:传入类(Data.class)
class Data {
    public void fun(){
        synchronized (Data.class){  //锁类
            System.out.println("start...");
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println("end...");
        }
    }
}
①public static void main(String[] args) {
     Data data = new Data();
     for (int i = 0; i < 5; i++) {
         new Thread(() -> {
             data.fun();
         },"A").start();
     }
}
②public static void main(String[] args) {
     for (int i = 0; i < 5; i++) {
         Data data = new Data();
         new Thread(() -> {
             data.fun();
         },"A").start();
     }
}

传入对象为Data.class时,锁的是传入类,新实例化的对象也属于这个类,所以①②都需要排队等待锁,输出结果是start…\n end………交替输出。

  • 例3 :传入一个变量(Integer num)
class Data {
    public void fun(Integer num){
        //Integer num = 1; 这种情况和传参结果一致
        synchronized (num){ /
            System.out.println("start...");
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println("end...");
        }
    }
}
public static void main(String[] args) {
    for (int i = 0; i < 3; i++) {
        Data data = new Data();
        new Thread(() -> {
            Integer num = 1;
            data.fun(num);
        },"A").start();
    }
}

传入对象为一个变量时,由于对象只有一个,所以线程还会去争夺锁。这里不难发现,synchronized修饰代码块的情况下,是根据锁的对象的是单个还是多个来确定是否需要排队的(一个则线程需要排队争夺锁,多个则线程都会拿到锁)但需要注意的是,Integer的范围为-128~127时是同一个对象(从常量池里取),所以当Integer=128时,每次传入都会是一个新的对象,即也不需要排队。

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注