After reading you will know the optimistic and pessimistic locking

Java optimistic locking and pessimistic locking lock it

Java is divided into lock optimistic and pessimistic locking, optimistic and pessimistic locking is not a real lock according to achieve, but a design idea, optimistic and pessimistic locking for the understanding of multi-threaded Java and databases to shut important, then this article will explore in detail the concept of these two locks and implementation.

Pessimistic locking

悲观锁Is a kind of pessimistic thinking, always think the worst case it may appear, it considers the data is likely to be modified by someone else, so pessimistic locking when the data held by the Federation 资源or 数据locked, so that other thread wants the resource request when it will block until the lock until the pessimistic resources released. Traditional relational database inside to use a lot of this locking mechanism, such as row locks, table locks, etc., read lock, write lock, are locked before doing the first operation. Realization often rely on pessimistic locking lock function to achieve database itself.

In Java Synchronizedand ReentrantLockso on exclusive lock (exclusive lock) is also a pessimistic locks for thought, because regardless of whether they hold ReetrantLock Synchronzied and resources, it will try to lock, for fear of his beloved baby was taken away by anyone.

Optimistic locking

Optimistic and pessimistic locking thought of the idea of locking the contrary, it is always considered the resources and data will not be modified by others, so the reading will not be locked, but when optimistic locking during write operation will determine whether the current data is modified (specifically, how we judge the following to say). Optimistic locking implementations general there are two: 版本号机制and CAS实现. Optimistic locking and more suitable for many types of applications, which can improve throughput.

In Java java.util.concurrent.atomicpackages following atomic variable classes is the use of one implementation of the optimistic locking CAS achieved.

Other kinds of locks usage scenarios

The above describes the basic concepts of other kinds of locks and referred to the applicable lock two scenarios, in general, pessimistic locking will not only have a write lock on the read operation locking, pessimistic locking a typical call:

select * from student where name="cxuan" for update

This sql statement select from the Student table name = record "cxuan" and its lock, then other write operations will not operate these pieces of data prior to re-submit the transaction, played a role in the exclusive and exclusive.

Because reading and writing are pessimistic locking lock, so its performance is relatively low, for now, the Internet advocated 三高(high performance, high availability, high concurrency), achieving pessimistic locking with less and less, but more generally read the case still need to use pessimistic locking, because although locked performance is relatively low, but also prevent the lock as optimistic as encountered in the case of inconsistencies have been written retry time.

In contrast, optimistic locking situation for reading and writing less, that is rarely the scene of conflict, so can save lock overhead and increase system throughput.

Applicable scene optimistic locking There are many, such as the cost of a typical system, the teller to make changes to a sum of money, in order to ensure the accuracy and effectiveness data, the use pessimistic locking lock certain data, and then meet other needs modification operational data, then this operation can not be completed modify the amount of product is disastrous moment, a version number of optimistic locking mechanism can solve this problem, we say below.

Optimistic locking implementation

Optimistic Locking There are two general ways: adopt 版本号机制and CAS(Compare-and-Swap,即比较并替换)算法implement.

The version number of mechanisms

The version number is in the data table mechanism adding a versionfield to achieve, indicates the number of data is modified, when the write operation is performed and the write was successful, version = version + 1, when the thread A to update the data, read at the same time the data will be read version value, at the time of submitting the update, if just to read the version when the update is version equal value in the current database, or retry the update until the update is successful.

Our financial system with the above example, to describe briefly the process.

file

  • The cost of the system has a data table, the table has two fields are 金额and version, to be able to attribute the amount of changes in real time, and version represents the amount of each version changes, the general policy is that when the amount of change, version every time an incremental strategy on the basis of a version of the + 1.
  • After understanding the basic situation and the basic information, we look at the process: After the company received payment, you need to put the money in the coffers, if kept there 100 dollars
    • The following open a transaction: When a write operation is performed before the back section male teller, he will first see (read) about how much money in the treasury, treasury read at this time there are 100 yuan, you can perform a write operation, and the database the money is updated to 120 yuan, commit the transaction, the money from the treasury 100 -> 120, version version number from 0 -> 1.
    • Open Affairs II: female teller receipt of the request made to employees after wages, you need to perform a read request to see how much money in the treasury, this time the version number, and then remove the employee's salary be paid from the treasury, commit the transaction, after the success of version + 1, this time from the version 1 -> 2.

The above two cases are the most optimistic scenario, the above two transactions are executed sequentially, that is a transaction and the transaction two interfere with each other, then the transaction to be executed in parallel will happen then?

file

  • An open transaction, the teller first man to perform a read operation, withdrawals and version number, the write operation

    begin
    update 表 set 金额 = 120,version = version + 1 where 金额 = 100 and version = 0

    At this point the amount was changed to 120, the version number is 1, the transaction has not yet submitted

    Two open affairs, female teller to perform a read operation, withdrawals and version number, the write operation

    begin
    update 表 set 金额 = 50,version = version + 1 where 金额 = 100 and version = 0

    At this point the amount was changed to 50, the version number to 1, uncommitted transactions

    Now a transaction is committed, the amount was changed to 120, version becomes 1 and commits the transaction. Ideally it should become Amount = 50, version = 2, but the two actually update transaction is based on the amount of 100 and version number 0 is based on, so the transaction will not be submitted two successful, should re-read the amount and version number, write again.

    In this way, it is possible to avoid the female teller covering male operator operating results with the results of a modified version = 0 based on old data.

CAS algorithm

First look at a classic problem of concurrent execution of 1000 after the increment and decrement:

public class Counter {

    int count = 0;

    public int getCount() {
        return count;
    }

    public void setCount(int count) {
        this.count = count;
    }

    public void add(){
        count += 1;
    }

    public void dec(){
        count -= 1;
    }
}
public class Consumer extends Thread{

    Counter counter;

    public Consumer(Counter counter){
        this.counter = counter;
    }


    @Override
    public void run() {
        for(int j = 0;j < Test.LOOP;j++){
            counter.dec();
        }
    }
}

public class Producer extends Thread{

    Counter counter;

    public Producer(Counter counter){
        this.counter = counter;
    }

    @Override
    public void run() {
        for(int i = 0;i < Test.LOOP;++i){
            counter.add();
        }
    }
}

public class Test {

    final static int LOOP = 1000;

    public static void main(String[] args) throws InterruptedException {

        Counter counter = new Counter();
        Producer producer = new Producer(counter);
        Consumer consumer = new Consumer(counter);

        producer.start();
        consumer.start();

        producer.join();
        consumer.join();

        System.out.println(counter.getCount());

    }
}

The results of several tests are not 0, that is to say after the emergence of inconsistent data concurrency problem, because the count - = 1 and the count + = 1 are non-atomic operation, the step of performing the three steps are:

  • Count value read from memory, put it in a register
  • + 1 performs OR - 1 Operation
  • Implementation of the completion of the results and then copied into memory

If you want evidence of their atoms must be locked, use Synchronziedor ReentrantLock, as we introduce them to achieve pessimistic locking, we are talking about is optimistic locking, then in what way to ensure that their atoms of it? please watch the following part

CAS That compare and swap(比较与交换)is a well-known lock-free algorithms. That is achieved without the use of a variable lock synchronization between multiple threads, which is synchronized variable in the absence of the thread is blocked, it is also called non-blocking synchronization (Non-blocking Synchronization

CAS involves three elements:

  • The need to read and write memory value V
  • Comparing value A
  • B intends to write the new value

If and only if the expected value of the A and V are the same memory value, the memory value V revised to B, or do nothing.

JAVA support for the CAS: new java.util.concurrent added in JDK1.5 (JUC) is built on top of the CAS. This blockage for synchronized algorithm, CAS is a non-blocking algorithms. So JUC in performance has been greatly improved.

We java.util.concurrent in AtomicInteger, for example, look at the case without the lock is how to ensure thread-safe

public class AtomicCounter {

    private AtomicInteger integer = new AtomicInteger();

    public AtomicInteger getInteger() {
        return integer;
    }

    public void setInteger(AtomicInteger integer) {
        this.integer = integer;
    }

    public void increment(){
        integer.incrementAndGet();
    }

    public void decrement(){
        integer.decrementAndGet();
    }

}

public class AtomicProducer extends Thread{

    private AtomicCounter atomicCounter;

    public AtomicProducer(AtomicCounter atomicCounter){
        this.atomicCounter = atomicCounter;
    }

    @Override
    public void run() {
        for(int j = 0; j < AtomicTest.LOOP; j++) {
            System.out.println("producer : " + atomicCounter.getInteger());
            atomicCounter.increment();
        }
    }
}

public class AtomicConsumer extends Thread{

    private AtomicCounter atomicCounter;

    public AtomicConsumer(AtomicCounter atomicCounter){
        this.atomicCounter = atomicCounter;
    }

    @Override
    public void run() {
        for(int j = 0; j < AtomicTest.LOOP; j++) {
            System.out.println("consumer : " + atomicCounter.getInteger());
            atomicCounter.decrement();
        }
    }
}

public class AtomicTest {

    final static int LOOP = 10000;

    public static void main(String[] args) throws InterruptedException {

        AtomicCounter counter = new AtomicCounter();
        AtomicProducer producer = new AtomicProducer(counter);
        AtomicConsumer consumer = new AtomicConsumer(counter);

        producer.start();
        consumer.start();

        producer.join();
        consumer.join();

        System.out.println(counter.getInteger());

    }
}

After testing available, no matter how many times the circulation final result is 0, which is the case of multiple threads in parallel using AtomicInteger can guarantee thread safety. incrementAndGet and decrementAndGet are atomic operations. This article temporarily to explore how they are implemented.

Shortcoming optimistic locking

Anything advantages and disadvantages, the software industry is not only the perfect solution for the best solution, so optimistic lock also has its weaknesses and shortcomings:

ABA problem

ABA says the problem is that if the value of a variable is first read is A, needs to be ready to write A, I found the value or A, then in this case, can be considered the value of A has not been changed ? It may be the A -> B -> A in this case, but it will not AtomicInteger think so, only to see it to believe it to see what it is.

JDK 1.5 after AtomicStampedReferenceclass provides such a capability, which compareAndSet 方法is to first check if the current reference equal to the expected reference and the current mark is equal to the expected flag, if all equal Atomically and the reference value of this flag is set given updated value.

It can also be used a variant of DCAS CAS to solve this problem.
DCAS, is represented for each of a number of modified V increases a reference marker. For each V, if the reference modification once, this counter is incremented. Then the variables that need to update the time, also check the values of variables and counters.

Large loop overhead

Do we know the optimistic lock during a write operation when the judge will be able to write a successful, if unsuccessful write the trigger wait -> retry mechanism, this case is a spin lock, is simply Get the short term less than, waiting to retry the lock, it does not apply to long-term not obtain the lock case, in addition, the spin cycle for the performance overhead is relatively large.

CAS and synchronized usage scenarios

Simply put under CAS apply to write relatively few cases (read more scenes, usually less conflict), synchronized apply to write more cases under (write a scene, the conflict usually more)

  • Competition for fewer resources (threads lighter conflict) situation, the use of synchronized synchronization locks thread is blocked and additional wake-switching operation between user mode and kernel mode switching wasteful consumption of cpu resources; and the CAS hardware-based, not need to enter the kernel, without switching thread, the spin operation is less chance, higher performance can be obtained.
  • For serious competition for resources (threads serious conflict) situation, the probability of CAS spin will be relatively large, thus wasting more CPU resources, efficiency is lower than synchronized.

Supplementary: Java concurrent programming in this area has been the role of the synchronized keyword veteran, and before long many people will call it "heavyweight lock." But after becoming carried out mainly in order to reduce to obtain and release locks bring performance overhead introduced by biased locking and lightweight locks and other various optimization after JavaSE 1.6 in some cases not so heavy . Underlying synchronized implementation mainly depends Lock-Free queue, the basic idea is the spin obstruction, continue to compete lock switch after the competition, a little at the expense of fairness, but to obtain high throughput. In the case of conflict fewer threads, and CAS can get similar performance; and severe thread conflict situations, the performance is much higher than CAS.

Welcome attention to my own public number, male No. 002 replies have everything you want
file

Related reference:

Java multi-threading of pessimistic locking and optimistic locking

https://baike.baidu.com/item/ pessimistic locking

Guess you like

Origin www.cnblogs.com/cxuanBlog/p/11595526.html
Recommended