Java并发编程之原子性-synchronized关键字

原子性提供了互斥访问,同一个时刻只能有一个线程对其进行操作,Java里能够保持原子性的除了atomic包之外还有锁

synchronized关键字:主要依赖jvm来实现锁,在这个关键字的作用对象的作用范围内只能有一个线程来执行操作。

Lock:jdk提供的代码层面的锁,依赖特殊的cpu指令,比较有代表性的有ReentrantLock

 

synchronized是一种同步锁,它修饰的对象主要有四种:

修饰代码块(同步语句块):大括号括起来的代码,作用于调用的对象

修饰方法(同步方法):整个方法,作用于调用的对象

同步语句块及同步方法的作用仅局限于调用的对象。即该锁存在于同一个实例当中。演示如下:

@Slf4j
public class SynchronizedExample1 {

    // 修饰一个代码块
    public void test1(int j) {
        synchronized (this) {
            for (int i = 0; i < 10; i++) {
                log.info("test1 {} - {}", j, i);
            }
        }
    }


    public static void main(String[] args) {
        SynchronizedExample1 example1 = new SynchronizedExample1();
        SynchronizedExample1 example2 = new SynchronizedExample1();
        ExecutorService executorService = Executors.newCachedThreadPool();
        executorService.execute(() -> {
            example1.test1(1);
        });
        executorService.execute(() -> {
            example2.test1(2);
        });
    }
}

查看执行结果如下:

可以看到执行的结果是test1 0-9,test2 0-9。

分别创建两个对象:

@Slf4j
public class SynchronizedExample1 {

    // 修饰一个代码块
    public void test1(int j) {
        synchronized (this) {
            for (int i = 0; i < 10; i++) {
                log.info("test1 {} - {}", j, i);
            }
        }
    }


    public static void main(String[] args) {
        SynchronizedExample1 example1 = new SynchronizedExample1();
        SynchronizedExample1 example2 = new SynchronizedExample1();
        ExecutorService executorService = Executors.newCachedThreadPool();
        executorService.execute(() -> {
            example1.test2(1);
        });
        executorService.execute(() -> {
            example2.test2(2);
        });
    }
}

执行结果:

可以看到线程一和二是乱序执行的。说明锁只存在于调用的对象里

同理修饰一个方法执行代码及结果如下:

@Slf4j
public class SynchronizedExample1 {

    // 修饰一个方法
    public synchronized void test2(int j) {
        for (int i = 0; i < 10; i++) {
            log.info("test2 {} - {}", j, i);
        }
    }

    public static void main(String[] args) {
        SynchronizedExample1 example1 = new SynchronizedExample1();
        ExecutorService executorService = Executors.newCachedThreadPool();
        executorService.execute(() -> {
            example1.test2(1);
        });
        executorService.execute(() -> {
            example1.test2(2);
        });
    }
}

执行结果如下:

分别创建两个对象:

@Slf4j
public class SynchronizedExample1 {

    // 修饰一个方法
    public synchronized void test2(int j) {
        for (int i = 0; i < 10; i++) {
            log.info("test2 {} - {}", j, i);
        }
    }

    public static void main(String[] args) {
        SynchronizedExample1 example1 = new SynchronizedExample1();
        SynchronizedExample1 example2 = new SynchronizedExample1();
        ExecutorService executorService = Executors.newCachedThreadPool();
        executorService.execute(() -> {
            example1.test2(1);
        });
        executorService.execute(() -> {
            example2.test2(2);
        });
    }
}

 

执行结果如下:

 

修饰静态方法:整个静态方法,作用于所有对象

修饰类:括号括起来的部分,作用于所有对象

修饰类示例如下:

@Slf4j
public class  SynchronizedExample2 {

    // 修饰一个类
    public static void test1(int j) {
        synchronized (SynchronizedExample2.class) {
            for (int i = 0; i < 10; i++) {
                log.info("test1 {} - {}", j, i);
            }
        }
    }


    public static void main(String[] args) {
        SynchronizedExample2 example1 = new SynchronizedExample2();
        SynchronizedExample2 example2 = new SynchronizedExample2();
        ExecutorService executorService = Executors.newCachedThreadPool();
        executorService.execute(() -> {
            example1.test1(1);
        });
        executorService.execute(() -> {
            example2.test1(2);
        });
    }
}

执行结果如下:

修饰静态方法示例如下:

@Slf4j
public class  SynchronizedExample2 {

    // 修饰一个静态方法
    public static synchronized void test2(int j) {
        for (int i = 0; i < 10; i++) {
            log.info("test2 {} - {}", j, i);
        }
    }

    public static void main(String[] args) {
        SynchronizedExample2 example1 = new SynchronizedExample2();
        SynchronizedExample2 example2 = new SynchronizedExample2();
        ExecutorService executorService = Executors.newCachedThreadPool();
        executorService.execute(() -> {
            example1.test2(1);
        });
        executorService.execute(() -> {
            example2.test2(2);
        });
    }
}

执行结果如下:

可以看到,即使是不同实例,它们之间仍然能顺序执行。所以修饰静态方法和修饰类,它作用于所有对象

注意:子类继承父类是带不上synchronized关键字,synchronized是不属于方法声明的一部分。如果子类也想用synchronized需要自己显示声明。

synchronized、Lock、Atomic原子性对比:

synchronized:不可中断锁,一旦代码执行到其作用范围内是必须等待代码执行完毕。适合竞争不激烈,可读性好。在竞争激烈时,性能急剧下降。

Lock:可中断锁,多样化同步,比如有时间限制同步。竞争不激烈时,性能稍比synchronized差。竞争激烈时能够维持常态(对于相同的代码,不断提高请求(或并发)时,平均处理速度上变化不大)

Atomic包:竞争不激烈时,性能稍比synchronized差,竞争激烈时能维持常态。比lock性能好,但是只能同步一个值。

所以,写同步的时候,优先考虑synchronized,如果有特殊需要,再进一步优化。ReentrantLock和Atomic如果用的不好,不仅不能提高性能,还可能带来灾难。 

想要查看各测试结果,可以参看https://blog.csdn.net/z69183787/article/details/48420643

猜你喜欢

转载自blog.csdn.net/qq_34871626/article/details/81488266