synchronized 的使用

137 阅读2分钟

我正在参与掘金创作者训练营第6期,点击了解活动详情

synchronized 关键字的作用就是只允许同一时刻只有一个线程操作资源。主要用以解决多个线程同时访问时可能出现的问题。synchronized 关键字用于三个位置:

  • 用于修饰代码块中
  • 用于修饰实例方法
  • 用于修饰静态方法
synchronized(锁对象){}
synchronized void method(){}
synchronized static void method(){}

修饰代码块

public class Learn1 implements Runnable{
    static Learn1 ll = new Learn1();
    @Override
    public void run() {
        synchronized (this){
            System.out.println("我是线程" + Thread.currentThread().getName());
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "结束");
        }
    }
}

synchronized 修饰代码块可以指定加锁对象,可以是this、对象、类加锁。从上面代码块可以看到使用线程时,线程的锁都是同一个,所以每个线程都需要等待其他线程释放锁后才能执行。

synchronized(this|object) 表示进入同步代码库前要获得给定对象的锁。synchronized(类.class) 表示进入同步代码前要获得 当前 class 的锁。

//以下使用对象也是一样,只有释放锁后其他线程才能执行
public class Learn1 implements Runnable{
   Object lock1 = new Object()
   @Override
    public void run() {
        synchronized (lock1){
        ... ...
      }
  }
}

// 所有线程需要的锁也是同一把   
public class Learn1 implements Runnable{
   @Override
    public void run() {
        synchronized (Learn1.class){
        ... ...
      }
  }
}

修饰实例方法

相当于是对当前实例加锁,默认锁是this。

public class Learn1 implements Runnable{
    static Learn1 ll = new Learn1();
    @Override
    public void run() {
       sayHi();
    }

    public synchronized void sayHi(){
        System.out.println("我是线程" + Thread.currentThread().getName());
    }
}

修饰静态方法

是给当前类加锁,会作用于类的所有对象实例,所以进入同步代码前要获得当前 class 的锁。静态成员知识类的一种成员,不属于任何实例对象。当线程A 调用实例对象的非静态 synchronized 方法,而线程 B 需要调用这个实例对象所属类的静态 synchronized 方法,是允许的,不会发生互斥现象。

public class Learn1 implements Runnable{
    static Learn1 lockObj1 = new Learn1();
    static Learn1 lockObj2 = new Learn1();

    public static synchronized void sayHi(){
        System.out.println("我是线程" + Thread.currentThread().getName());
    }
    public static void main(String[] args) {
     Thread t1 = new Thread(instence1);
     Thread t2 = new Thread(instence2);
     t1.start();
     t2.start()
   }
}

以上的源码执行后的结果可以看出,无论是哪个线程访问它,需要的锁都只有一个。因为用的是类锁。

注意:类锁和实例对象锁(其中 this 就是当前实例对象)是不一样的。修饰代码块时是可以自己指定锁对象的;修饰方法时,静态方法是类锁,非静态方法是对象锁

使用 Synchronized 保证线程安全

加锁和释放锁的原理,可重入原理,保证可见性原理。

参考资料: