многопоточность в java --синхронизация

Java задняя часть база данных Безопасность

Синхронизация нескольких потоков

Зачем внедрять механизм синхронизации

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

Предположим, что учетная запись пользователя в банке имеет 1000 юаней, поток A считывает 1000 юаней и хочет забрать 1000 юаней, а в стеке он изменяется на 0, но не сбрасывается в кучу, а поток B также читает 1000 юаней. , это Когда счет обновляется в банковской системе, деньги на счету становятся 0. В это время я также хочу удалить 1000 и снова обновить банковскую систему, и деньги на счете становятся 0. В это время , и A, и B снимают по 1000 юаней, но на счете всего 1000 юаней, что-то явно пошло не так. Для вышеуказанных проблем, если мы добавим механизм синхронизации, это может быть легко решено.

Как решить эту проблему — заблокировать ресурс, когда поток его использует.После того как первый поток, получивший доступ к ресурсу, заблокирует его, другие потоки больше не смогут использовать этот ресурс, пока он не будет разблокирован.

Код:

package com.java.test;

/**
 * Created by xiaofandiy03 on 2018/4/14.
 */
public class     DrawMoneyTest {
    public static void main(String[] args)
    {
        Bank bank = new Bank();

        Thread t1 = new MoneyThread(bank);// 从银行取钱
        Thread t2 = new MoneyThread(bank);// 从取款机取钱

        t1.start();
        t2.start();

    }
}
class Bank{
    private int money =1000;
    public int getMoney(int number)
    {
        if(number <0)
        {
            return -1;
        }else if(number >money) {
            return  -2;
        }else if(money <0)
        {
            return -3;
        }else {
            try {
                Thread.sleep(1000);
            }catch (InterruptedException e)
            {
                e.printStackTrace();
            }
        }
            money-=number;
        System.out.println("Left Money :" +money);
        return  number;
    }
}
class MoneyThread extends Thread
{
    private Bank bank;

    public MoneyThread(Bank bank)
    {
        this.bank = bank;
    }

    @Override
    public void run()
    {
        System.out.println(bank.getMoney(1000));
    }
}

Как решить эту проблему, решение естьзамок.

Вы хотите работать с набором заблокированных кодов?想的话就先拿到锁,拿到锁之后就可以操作被加锁的代码,倘若拿不到锁的话就只能等着,因为等的线程太多了,这就是线程的阻塞。

Условия гонки и видимость памяти

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

состояние гонки

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

Наиболее распространенные условия гонки:

  1. Сначала проверьте, а потом выполняйте. Выполнение зависит от результата обнаружения, а результат обнаружения зависит от времени выполнения нескольких потоков, а время выполнения нескольких потоков обычно не является фиксированным и неразрешимым, что приводит к различным проблемам в результатах выполнения.
  2. Ленивая инициализация (чаще всего одноэлементная)

Упомянутая выше блокировка предназначена для решения этой проблемы.Обычные решения:

  • Используйте синхронизированное ключевое слово
  • Использовать явную блокировку (Lock)
  • Используйте атомарные переменные

видимость памяти

Что касается видимости памяти, мы должны сначала начать с взаимодействия между памятью и ЦП. Память — это аппаратное обеспечение, и скорость ее выполнения в несколько сотен раз медленнее, чем у ЦП. Поэтому в компьютере, когда ЦП выполняет операцию, он не соответствует памяти для каждой операции.Для взаимодействия данных сначала запишите некоторые данные в область кеша (регистры и все уровни кеша) в ЦП, и запишите их в память после окончания. Этот процесс чрезвычайно быстр, и с одним потоком проблем не возникает.

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

Это проблема видимости памяти.

Распространенный способ решения этой проблемы:

  • Используйте ключевое слово volatile
  • Используйте ключевое слово synchronized или явную синхронизацию блокировки

метод синхронизации потоков

Метод синхронизации:

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

class Bank{
    private int money =1000;
    public synchronized int getMoney(int number)
    {
        if(number <0)
        {
            return -1;
        }else if(number >money) {
            return  -2;
        }else if(money <0)
        {
            return -3;
        }else {
            try {
                Thread.sleep(1000);
            }catch (InterruptedException e)
            {
                e.printStackTrace();
            }
        }
            money-=number;
        System.out.println("Left Money :" +money);
        return  number;
    }
}

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

Блок синхронизированного кода

Это синхронизированное модифицированное блок. Ключевое слово изменено, блок оператора будет автоматически добавлять встроенный замок, чтобы добиться синхронизации

class Bank{
    private int money =1000;
    public  int getMoney(int number)
    {
        synchronized (this) {
            if (number < 0) {
                return -1;
            } else if (number > money) {
                return -2;
            } else if (money < 0) {
                return -3;
            } else {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            money -= number;
        }
        System.out.println("Left Money :" +money);
        return  number;
    }
}

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

Синхронизация потоков с использованием повторных блокировок

Добавлен пакет java.util.concurrent для поддержки синхронизации в Java SE 5.0. Класс ReentrantLock — это реентерабельная взаимоисключающая блокировка, реализующая интерфейс Lock.Он имеет то же базовое поведение и семантику, что и метод synchronized, но имеет расширенные возможности.
Общие методы класса ReenreantLock:
ReentrantLock(): создать экземпляр ReentrantLock.
Замок (): приобрести замок
разблокировать() : снять блокировку
Примечание: ReentrantLock() также имеет метод построения, который может создать справедливую блокировку, но использовать его не рекомендуется, так как это может значительно снизить эффективность программы.

EntrantLock имеет функцию, аналогичную синхронизированной, но более гибкую и мощную.

Это реентерабельная блокировка (тоже синхронизированная).Так называемая реентерабельность означает, что вы можете многократно входить в одну и ту же функцию.Что толку от этого?

Предположим сценарий, рекурсивная функция, если блокировка функции разрешена только один раз, что должен делать поток, когда ему нужно рекурсивно вызвать функцию? Отступления нет, а есть функции, которые нельзя повторно ввести в блокировку, что формирует новый тупик.

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

Неустойчивое ключевое слово

Когда общая переменная изменяется с помощью volatile, это гарантирует, что переменная будет обновлена ​​в памяти сразу после ее изменения, и другой поток должен будет прочитать новое значение в памяти, когда он извлечет значение.

Volatile может гарантировать видимость переменных в памяти, но не может гарантировать атомарность.Для b++ это не одношаговая операция, а разделенная на несколько шагов.Читаем белые двойки, определяем константу 1 и прибавляем 1 к переменной b. , результат синхронизируется с памятью. Хотя на каждом шаге получается самое последнее значение переменной, атомарность b++ не гарантируется, и, естественно, невозможно добиться потокобезопасности.

Синхронизация потоков с использованием локальных переменных

Если ThreadLocal используется для управления переменными, каждый поток, использующий переменную, получает копию переменной, и эти копии не зависят друг от друга, так что каждый поток может изменять свою собственную копию переменной по своему желанию, не затрагивая другие потоки. Теперь я понимаю, получается, что каждый поток запускает копию, то есть ввод денег и вывод денег - это два аккаунта с одним и тем же именем знания. Таким образом, вышеописанный эффект будет иметь место.

ThreadLocal и механизм синхронизации
Механизмы A. Входные и синхронизации являются оба для решения проблемы конфликта доступа одинаковой переменной в нескольких потоках
б) В первом методе используется «пространство для времени», во втором — «время для пространства».