совать Java в точке боли - (8) синхронизируется анализ глубины

Обзор:

  1. Введение: роль, статус, не влияет на параллелизм контроля
  2. Применение: Объект класса замки и замок
  3. Семь видов многопоточного способа синхронизации доступа
  4. Природа: возвратный, источник бесперебойного
  5. Принцип: принцип запирания и отпирания, принцип возвратного, принцип наглядности
  6. Дефекты: неэффективные, негибкие, не в силах предсказать, будет ли или не замыкаться успешно приобрели
  7. Как выбрать блокировку или Синхронное
  8. Как повысить производительность виртуальной машины Java, как определить, какой поток получает блокировку
  9. резюме

Там будет последующий код демонстрирует, в тестовой среде JDK8, IDEA

I. Введение

1, роль

Для того, чтобы максимально только один поток для выполнения кода в то же время == ==, чтобы обеспечить безопасность одновременного эффекта.

2, статус

  • Синхронное ключевое слово Java, Java нативная поддержка
  • Самые основные средства синхронизации взаимно исключают друг друга
  • Параллельное программирование уровень ветерана

3, не влияет на управление параллелизмом

Тест: два потока, в то же время ++, думаю, что результаты

package cn.jsonshare.java.base.synchronizedtest;

/**
 * 不使用synchronized,两个线程同时a++
 *
 * @author JSON
 * @date 2019-08-29
 */
public class SynchronizedTest1 implements Runnable{
    static SynchronizedTest1 st = new SynchronizedTest1();

    static int a = 0;

    /**
     * 不使用synchronized,两个线程同时a++
     */
    public static void main(String[] args) throws Exception{
        Thread t1 = new Thread(st);
        Thread t2 = new Thread(st);
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        System.out.println(a);
    }

    @Override
    public void run(){
        for(int i=0; i<10000; i++){
            a++;
        }
    }
}

Ожидается, что 20000, но результаты менее чем 20000 несколько раз

10108
11526
10736
...

Во-вторых, использование: класс и блокировки объекта блокировки

1, замок объекта

  • Блок Форма: замок Объект, заданный вручную
  • Блокировка форма метода: синхронизированные способы модификации, блокировки объектов по умолчанию к этому
package cn.jsonshare.java.base.synchronizedtest;

/**
 * 对象锁实例: 代码块形式
 *
 * @author JSON
 * @date 2019-08-29
 */
public class SynchronizedTest2 implements Runnable{
    static SynchronizedTest2 st = new SynchronizedTest2();

    public static void main(String[] args) {
        Thread t1 = new Thread(st);
        Thread t2 = new Thread(st);
        t1.start();
        t2.start();
        while(t1.isAlive() || t2.isAlive()){

        }
        System.out.println("run over");

    }

    @Override
    public void run(){
        synchronized (this){
            System.out.println("开始执行:" + Thread.currentThread().getName());
            try {
                // 模拟执行内容
                Thread.sleep(3000);
            }catch (Exception e){
                e.printStackTrace();
            }
            System.out.println("执行结束:" + Thread.currentThread().getName());
        }
    }
}
package cn.jsonshare.java.base.synchronizedtest;

/**
 * 对象锁实例:synchronized方法
 * @author JSON
 * @date 2019-08-29
 */
public class SynchronizedTest3 implements Runnable{
    static SynchronizedTest3 st = new SynchronizedTest3();

    public static void main(String[] args) throws Exception{
        Thread t1 = new Thread(st);
        Thread t2 = new Thread(st);
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        System.out.println("run over");
    }

    @Override
    public void run(){
        method();
    }

    public synchronized void method(){
        System.out.println("开始执行:" + Thread.currentThread().getName());
        try {
            // 模拟执行内容
            Thread.sleep(3000);
        }catch (Exception e){
            e.printStackTrace();
        }
        System.out.println("执行结束:" + Thread.currentThread().getName());
    }
}

Результаты:

开始执行:Thread-0
执行结束:Thread-0
开始执行:Thread-1
执行结束:Thread-1
run over

2, замки

== Концепция: класс Java может иметь более одного объекта, а только объект класса ==

== характер: так называемый замок класса, но объект блокировки класса только ==

== Использование и эффективность: блокировка класса может быть только объектом, в то же время есть ==

Форма 1: синхронизирована нагрузка на статический метод

Форма 2: (. * Класс) синхронизируются блок

package cn.jsonshare.java.base.synchronizedtest;

/**
 * 类锁:synchronized加载static方法上
 *
 * @author JSON
 * @date 2019-08-29
 */
public class SynchronizedTest4 implements Runnable{

    static SynchronizedTest4 st1 = new SynchronizedTest4();
    static SynchronizedTest4 st2 = new SynchronizedTest4();

    public static void main(String[] args) throws Exception{
        Thread t1 = new Thread(st1);
        Thread t2 = new Thread(st2);
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        System.out.println("run over");
    }

    @Override
    public void run(){
        method();
    }

    public static synchronized void method(){
        System.out.println("开始执行:" + Thread.currentThread().getName());
        try {
            // 模拟执行内容
            Thread.sleep(3000);
        }catch (Exception e){
            e.printStackTrace();
        }
        System.out.println("执行结束:" + Thread.currentThread().getName());
    }
}

package cn.jsonshare.java.base.synchronizedtest;

/**
 * 类锁:synchronized(*.class)代码块
 *
 * @author JSON
 * @date 2019-08-29
 */
public class SynchronizedTest5 implements Runnable{
    static SynchronizedTest4 st1 = new SynchronizedTest4();
    static SynchronizedTest4 st2 = new SynchronizedTest4();

    public static void main(String[] args) throws Exception{
        Thread t1 = new Thread(st1);
        Thread t2 = new Thread(st2);
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        System.out.println("run over");
    }

    @Override
    public void run(){
        method();
    }

    public void method(){
        synchronized(SynchronizedTest5.class){
            System.out.println("开始执行:" + Thread.currentThread().getName());
            try {
                // 模拟执行内容
                Thread.sleep(3000);
            }catch (Exception e){
                e.printStackTrace();
            }
            System.out.println("执行结束:" + Thread.currentThread().getName());
        }
    }
}

Результаты:

开始执行:Thread-0
执行结束:Thread-0
开始执行:Thread-1
执行结束:Thread-1
run over

В-третьих, многопоточный способ синхронизации доступа 7 случаев

  1. Два потока одновременно получить доступ к объекту одного и того же метода синхронизированного
  2. Те же синхронизированный метод двух потоков одновременно доступ к двум объектам
  3. Два потока одновременно получить доступ два объекта одного и того же статического синхронизированного метода
  4. синхронизированный метод два потоков одновременно получать доступ и тот же объект с не-синхронизированным методом
  5. Различные способы синхронизированных двух потоков доступ к одному объекту
  6. Два потока одновременно получить доступ и тот же объект синхронизирован статические методы и нестатический синхронизированный метод
  7. После того, как метод генерирует исключение, он будет освободить замок

См следующий пример выходного результата близкий результат кода, время выходной интервал внимание прогностические выводы

Сценарий 1:

package cn.jsonshare.java.base.synchronizedtest;

/**
 * 两个线程同时访问一个对象的相同的synchronized方法
 *
 * @author JSON
 * @date 2019-08-29
 */
public class SynchronizedScene1 implements Runnable{
    static SynchronizedScene1 ss = new SynchronizedScene1();

    public static void main(String[] args) throws Exception{
        Thread t1 = new Thread(ss);
        Thread t2 = new Thread(ss);
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        System.out.println("run over");
    }

    @Override
    public void run(){
        method();
    }

    public synchronized void method(){
        System.out.println("开始执行:" + Thread.currentThread().getName());
        try {
            // 模拟执行内容
            Thread.sleep(3000);
        }catch (Exception e){
            e.printStackTrace();
        }
        System.out.println("执行结束:" + Thread.currentThread().getName());
    }
}

Сценарий 2:

package cn.jsonshare.java.base.synchronizedtest;

/**
 * 两个线程同时访问两个对象的相同的synchronized方法
 *
 * @author JSON
 * @date 2019-08-29
 */
public class SynchronizedScene2 implements Runnable{
    static SynchronizedScene2 ss1 = new SynchronizedScene2();
    static SynchronizedScene2 ss2 = new SynchronizedScene2();

    public static void main(String[] args) throws Exception{
        Thread t1 = new Thread(ss1);
        Thread t2 = new Thread(ss2);
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        System.out.println("run over");
    }

    @Override
    public void run(){
        method();
    }

    public synchronized void method(){
        System.out.println("开始执行:" + Thread.currentThread().getName());
        try {
            // 模拟执行内容
            Thread.sleep(3000);
        }catch (Exception e){
            e.printStackTrace();
        }
        System.out.println("执行结束:" + Thread.currentThread().getName());
    }
}

Сценарий 3:

package cn.jsonshare.java.base.synchronizedtest;

/**
 * 两个线程同时访问两个对象的相同的static的synchronized方法
 *
 * @author JSON
 * @date 2019-08-29
 */
public class SynchronizedScene3 implements Runnable{
    static SynchronizedScene3 ss1 = new SynchronizedScene3();
    static SynchronizedScene3 ss2 = new SynchronizedScene3();

    public static void main(String[] args) throws Exception{
        Thread t1 = new Thread(ss1);
        Thread t2 = new Thread(ss2);
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        System.out.println("run over");
    }

    @Override
    public void run(){
        method();
    }

    public synchronized static void method(){
        System.out.println("开始执行:" + Thread.currentThread().getName());
        try {
            // 模拟执行内容
            Thread.sleep(3000);
        }catch (Exception e){
            e.printStackTrace();
        }
        System.out.println("执行结束:" + Thread.currentThread().getName());
    }
}

Сценарий 4:

package cn.jsonshare.java.base.synchronizedtest;

/**
 * 两个线程同时访问同一对象的synchronized方法与非synchronized方法
 *
 * @author JSON
 * @date 2019-08-29
 */
public class SynchronizedScene4 implements Runnable{
    static SynchronizedScene4 ss1 = new SynchronizedScene4();

    public static void main(String[] args) throws Exception{
        Thread t1 = new Thread(ss1);
        Thread t2 = new Thread(ss1);
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        System.out.println("run over");
    }

    @Override
    public void run(){
        // 模拟两个线程同时访问 synchronized方法与非synchronized方法
        if(Thread.currentThread().getName().equals("Thread-0")){
            method1();
        }else{
            method2();
        }
    }

    public void method1(){
        System.out.println("method1开始执行:" + Thread.currentThread().getName());
        try {
            // 模拟执行内容
            Thread.sleep(3000);
        }catch (Exception e){
            e.printStackTrace();
        }
        System.out.println("method1执行结束:" + Thread.currentThread().getName());
    }

    public synchronized void method2(){
        System.out.println("method2开始执行:" + Thread.currentThread().getName());
        try {
            // 模拟执行内容
            Thread.sleep(3000);
        }catch (Exception e){
            e.printStackTrace();
        }
        System.out.println("method2执行结束:" + Thread.currentThread().getName());
    }
}

Сценарий 5:

package cn.jsonshare.java.base.synchronizedtest;

/**
 * 两个线程访问同一对象的不同的synchronized方法
 *
 * @author JSON
 * @date 2019-08-29
 */
public class SynchronizedScene5 implements Runnable{
    static SynchronizedScene5 ss1 = new SynchronizedScene5();

    public static void main(String[] args) throws Exception{
        Thread t1 = new Thread(ss1);
        Thread t2 = new Thread(ss1);
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        System.out.println("run over");
    }

    @Override
    public void run(){
        // 模拟两个线程同时访问不同的synchronized方法
        if(Thread.currentThread().getName().equals("Thread-0")){
            method1();
        }else{
            method2();
        }
    }

    public synchronized void method1(){
        System.out.println("method1开始执行:" + Thread.currentThread().getName());
        try {
            // 模拟执行内容
            Thread.sleep(3000);
        }catch (Exception e){
            e.printStackTrace();
        }
        System.out.println("method1执行结束:" + Thread.currentThread().getName());
    }

    public synchronized void method2(){
        System.out.println("method2开始执行:" + Thread.currentThread().getName());
        try {
            // 模拟执行内容
            Thread.sleep(3000);
        }catch (Exception e){
            e.printStackTrace();
        }
        System.out.println("method2执行结束:" + Thread.currentThread().getName());
    }
}

Сценарий 6:

package cn.jsonshare.java.base.synchronizedtest;

/**
 * 两个线程同时访问同一对象的static的synchronized方法与非static的synchronized方法
 *
 * @author JSON
 * @date 2019-08-29
 */
public class SynchronizedScene6 implements Runnable{
    static SynchronizedScene6 ss1 = new SynchronizedScene6();

    public static void main(String[] args) throws Exception{
        Thread t1 = new Thread(ss1);
        Thread t2 = new Thread(ss1);
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        System.out.println("run over");
    }

    @Override
    public void run(){
        // 模拟两个线程同时访问static的synchronized方法与非static的synchronized方法
        if(Thread.currentThread().getName().equals("Thread-0")){
            method1();
        }else{
            method2();
        }
    }

    public static synchronized void method1(){
        System.out.println("method1开始执行:" + Thread.currentThread().getName());
        try {
            // 模拟执行内容
            Thread.sleep(3000);
        }catch (Exception e){
            e.printStackTrace();
        }
        System.out.println("method1执行结束:" + Thread.currentThread().getName());
    }

    public synchronized void method2(){
        System.out.println("method2开始执行:" + Thread.currentThread().getName());
        try {
            // 模拟执行内容
            Thread.sleep(3000);
        }catch (Exception e){
            e.printStackTrace();
        }
        System.out.println("method2执行结束:" + Thread.currentThread().getName());
    }
}

Сцена 7:

package cn.jsonshare.java.base.synchronizedtest;

/**
 * 方法抛出异常后,会释放锁吗
 *
 * @author JSON
 * @date 2019-08-29
 */
public class SynchronizedScene7 implements Runnable{
    static SynchronizedScene7 ss1 = new SynchronizedScene7();

    public static void main(String[] args) throws Exception{
        Thread t1 = new Thread(ss1);
        Thread t2 = new Thread(ss1);
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        System.out.println("run over");
    }

    @Override
    public void run(){
        method1();
    }

    public synchronized void method1(){
        System.out.println("method1开始执行:" + Thread.currentThread().getName());
        try {
            // 模拟执行内容
            Thread.sleep(3000);
        }catch (Exception e){
            e.printStackTrace();
        }
        // 模拟异常
        throw new RuntimeException();
        //System.out.println("method1执行结束:" + Thread.currentThread().getName());
    }
}
Краткое описание:

1, два потока одновременно получить доступ к объекту одного и того же метода синхронизированного

同一实例拥有同一把锁,其他线程必然等待,顺序执行

2, два потока одновременно доступ к тем же синхронизированный метод двух объектов

不同的实例拥有的锁是不同的,所以不影响,并行执行

3, два потока одновременно получить доступ к тому же синхронизированному статическому методу двух объектов

静态同步方法,是类锁,所有实例是同一把锁,其他线程必然等待,顺序执行

4, синхронизированный метод два потоков одновременно получать доступ и тот же объект с не-синхронизированным методом

非synchronized方法不受影响,并行执行

5, два потока доступ к тем же объектам различных методов синхронизированы

同一实例拥有同一把锁,所以顺序执行(说明:锁的是this对象==同一把锁)

6, два потока одновременно получить доступ и тот же объект синхронизирован статические методы и нестатический синхронизированный метод

static同步方法是类锁,非static是对象锁,原理上是不同的锁,所以不受影响,并行执行

7, метод генерирует исключение, он будет освободить замок

会自动释放锁,这里区别Lock,Lock需要显示的释放锁
Три основные идеи:

1, блокировка может быть только один поток одновременно приобрели, не получила нить блокировки должна ждать (что соответствует 1,5 сценарию)

2, каждый экземпляр, соответствующий собственный замок, независимо друг от друга между различными экземплярами;

例外:锁对象是*.class以及synchronized被static修饰的时候,所有对象共用同一把锁(对应2、3、4、6情景)

3, является ли она будет закончена, или обычный метод генерирует исключение, выпустит блокировку (соответствующую 7 Сценария)

Он добавил:

Вопрос: В настоящее время синхронизируется с модифицированным методом, этот метод вызывает внутри не-синхронизированный метод поточно?

package cn.jsonshare.java.base.synchronizedtest;

/**
 * 目前进入到被synchronized修饰的方法,这个方法里边调用了非synchronized方法,是线程安全的吗?
 *
 * @author JSON
 * @date 2019-08-29
 */
public class SynchronizedScene8 {
    public static void main(String[] args) {
        new Thread(() -> {
            method1();
        }).start();

        new Thread(() -> {
            method1();
        }).start();
    }

    public static synchronized void method1() {
        method2();
    }

    private static void method2() {
        System.out.println(Thread.currentThread().getName() + "进入非Synchronized方法");
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println(Thread.currentThread().getName() + "结束非Synchronized方法");
    }
}

Заключение: Это потокобезопасно

В-четвертых, характер

1, реентерабельным

Обратитесь к одной и той же нити, чтобы получить блокировку после внешней функции, внутренняя функция непосредственно получить блокировку снова

Типичная Java замок возвратный: синхронизированы, ReentrantLock

Преимущества: избежать тупиковой ситуации, улучшить герметизацию

Размер: нить вместо вызова

情况1:证明同一方法是可重入的
情况2:证明可重入不要求是同一方法
情况3:证明可重入不要求是同一类中的

2, не может быть прервана

После того, как замок приобретается другим потоком, если я хочу, чтобы сейчас, я могу выбрать только ждать или блок, пока другой поток не освободит блокировку, если другой поток не освободит блокировку, то я всегда могу подождать.

В отличие от этого, класс блокировки может иметь возможность прервать первую точку: Я думаю, если я слишком долго ждать, теперь получила право прерывать для блокировки потоков исполнения, второй пункт: Я думаю, что если я подожду не хотите ждать слишком долго, вы можете также бросить курить.

В-пятых, принцип

1, а также принцип разблокировки (явление, выбор времени, углубленный вид виртуальной машины Java-байт-код)

Феномен: каждый экземпляр класса, соответствующего замку, каждый синхронизирована метод должен сначала получить экземпляр замка класса вызывает метод для того, чтобы выполнять, в противном случае обструкции, или метод полного выполнения генерирует исключение, блокировка снимается , был заблокирован поток может получить блокировку для выполнения.

Сроки приобретения и выпуская замки: внутренние замки или замки монитора

package cn.jsonshare.java.base.synchronizedtest;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * method1 等价于 method2
 *
 * @author JSON
 * @date 2019-08-29
 */
public class SynchronizedToLock1 {
    Lock lock = new ReentrantLock();

    public synchronized void method1(){
        System.out.println("执行method1");
    }

    public void method2(){
        lock.lock();
        try {
            System.out.println("执行method2");
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }

    public static void main(String[] args) {
        SynchronizedToLock1 sl = new SynchronizedToLock1();

        // method1 等价于 method2
        sl.method1();
        sl.method2();
    }
}

В глубокий взгляд на JVM байт-код:

...
monitorenter指令
...
monitorexit指令
...

2, принцип возвратных (блокировка номер счетчика)

JVM отвечает за отслеживание количества объектов, которые будут заблокированы

Первый поток, чтобы заблокировать объект, когда счетчик становится 1, всякий раз, когда тот же самый поток, чтобы получить блокировку на этот объект снова, счетчик получает приращение

Всякий раз, когда задача выходит, обратный отсчет, когда счетчик равен 0, то замок полностью освобожден

3, принцип видимости (модель памяти)

Модель памяти Java

аватар

Процесс (JMM управление) передает данные в поток Поток B

аватар

== синхронизировано ключевое слово Осознайте Видимость: ==

Модификация синхронизировано, поэтому после осуществления завершена, любые изменения должны быть сделаны на объект, прежде чем отпустить замок, нить должна быть записана из памяти в основной памяти, данные в основной памяти находится в актуальном состоянии.

В-шестых, дефект

1, неэффективность

1), чтобы освободить корпус замка меньше (выполнение потока завершено или нарушения релиз)

2) не может быть установлено время ожидания при попытке получить блокировку (ждать)

3) не может прервать поток, который пытается получить блокировку (не прерывается)

2, не является достаточно гибкой

Блокировка и времени выпуска относительно просто, каждая блокировка только одно условие (объект), он не может быть достаточно

Например: чтение-запись блокировки более гибкой

3, не может предсказать, будет ли успешно приобрел замок

Семь Часто задаваемые вопросы

1, синхронизированные Меры по ключевым словам:

  1. Блокировка объект не может быть пустым
  2. Объем не должен быть слишком большим
  3. Избегайте затор

2, как выбрать блокировки и синхронизированной ключевое слово?

Основная рекомендация (принцип приоритета избежать ошибок):

  1. Если возможно, попробуйте не использовать java.util.concurrent различного класса приоритета (нет необходимости рассмотреть вопрос о синхронизации, подверженные ошибки)
  2. Предпочтительно используется синхронизированным, так что количество кодирования может быть уменьшено, что может снизить частоту ошибок
  3. Если замок или б уникальных характеристик, использовать только блокировку или состояние

Восемь, резюме

Заявив синхронизировано:

JVM для автоматической блокировки и разблокировки с помощью монитора, убедитесь в то же время только один поток может выполнять заданный код для обеспечения безопасности потока, но также имеет реентерабельные и прерываемые характеристики.

рекомендация

отwww.cnblogs.com/JsonShare/p/11433302.html