同步,加锁保证线程安全,可以分为对象锁和类锁

1.对象锁
synchronized写在非静态方法上或者synchronized(this)块
锁的只是单个对象,如果有两个对象同时操作,那还是线程不安全的
比如下面代码,结果都小于2000000

public class Sync {

    private static int in = 0;

    public synchronized void get(int name) {

//        synchronized (Sync.class) {
            in++;
            System.out.println(in);
//        }
    }
}

class T {

    @SneakyThrows
    public static void main(String[] args) {

        Sync sync = new Sync();
        Sync sync2 = new Sync();
        Thread thread = new Thread(() -> {

            for (int i = 0; i < 1000000; i++) {
                sync.get(2);
            }
        });

        Thread thread2 = new Thread(() -> {
            for (int i = 0; i < 1000000; i++) {
                sync2.get(1);
            }
        });

        thread.start();
        thread2.start();
    }
}

修改为使用同一个对象,结果是正确的2000000

class T {

    @SneakyThrows
    public static void main(String[] args) {

        Sync sync = new Sync();
//        Sync sync2 = new Sync();
        Thread thread = new Thread(() -> {

            for (int i = 0; i < 1000000; i++) {
                sync.get(2);
            }
        });

        Thread thread2 = new Thread(() -> {
            for (int i = 0; i < 1000000; i++) {
                sync.get(1);
            }
        });

        thread.start();
        thread2.start();
    }
}

再举一个项目中会遇到的场景,这样也是无法做到线程安全的
测试的时候使用了jmeter,1000个线程轮询1000次,结果为996859

@PostMapping("/test")
public void test(HttpServletRequest request) {

    Sync sync = new Sync();
    sync.get(2);
}
public class Sync {

    private static int in = 0;

    public synchronized void get(int name) {

//        synchronized (Sync.class) {
            in++;
            System.out.println(in);
        }
//    }
}

而如果将代码修改为下面这样,就可以做到线程安全,原理猜测是Controller是单例的,this指当前对象,而当前对象只存在一个单例对象,所以是同步的

@PostMapping("/test")
public void test(HttpServletRequest request) {
    this.get(2);
}

private static int in = 0;

public synchronized void get(int name) {

//        synchronized (this) {
        in++;
        System.out.println(in);
//        }
}

为验证上述猜测,写代码如下,发现是线程不安全的,debug了两种写法,Controller中的this一直是同一个对象,所以上述猜测是正确的。对象锁关键点在于锁的是哪个“对象”

@PostMapping("/test")
public void test(HttpServletRequest request) {
    WssController wssController = new WssController();
    wssController.get(2);
}

private static int in = 0;

public synchronized void get(int name) {

//        synchronized (this) {
        in++;
        System.out.println(in);
//        }
}

另外,代码里使用synchronized(this)会不同程度地提升执行效率,因为发生同步阻塞的只是代码块,在该代码块之外的代码不会阻塞,可以最小范围内对线程不安全的代码进行同步

代码里同时存在多个synchronized方法或者代码块时,这些代码之间是同步阻塞的,因为获得的是对象锁,锁住的是整个对象

2.类锁
synchronized写在静态方法或者用synchronized(class)块的方式
锁的是类,也就是该类的所有对象同时操作,也是线程安全的
比如下面代码结果为正确的2000000

public class Sync {

    private static int in = 0;

    public void get(int name) {

        synchronized (Sync.class) {
            in++;
            System.out.println(in);
        }
    }
}

class T {

    @SneakyThrows
    public static void main(String[] args) {

        Sync sync = new Sync();
        Sync sync2 = new Sync();
        Thread thread = new Thread(() -> {

            for (int i = 0; i < 1000000; i++) {
                sync.get(2);
            }
        });

        Thread thread2 = new Thread(() -> {
            for (int i = 0; i < 1000000; i++) {
                sync2.get(1);
            }
        });

        thread.start();
        thread2.start();
    }
}

当对象锁和类锁同时存在时,不会发生阻塞,因为是两种锁
另外,锁的对象不同时不会发生阻塞,对象锁的范围只是单个对象