Optimistic lock/pessimistic lock learning notes, if you have questions, welcome to correct

Optimistic locking

  • The data is not locked because the concurrency of write operations is low. The data is not modified when the data is fetched. When the data is updated, it checks whether the data is modified by other threads. If the data is modified by other threads, the data is not updated. Update if it has not been modified by another thread.
  • This mode is applicable to the scenario where read is excessive and write is insufficient to improve system throughput.
  • Usage Scenarios:
  1. CAS used by atomic classes such as AtomicLong
  2. Version number mechanism

Add the version field to the database table. When fetching data, retrieve the version and check whether the version is consistent during update. The general SQL is as follows:

    update t_table set status = #{status}, version = version + 1 where id = #{id} and version = #{version}
Copy the code

The sample

Enable 10 threads to update the same data in the table

    @Test
    public void optimisticLock(a) throws InterruptedException {
        for (int i = 0; i < 10; i++) {
            Thread myThread = new Thread(new MyThread());
            myThread.setName("Thread" + i);
            myThread.start();
        }
        // Wait for the thread to finish and print the result on the console
        Thread.sleep(5000);
    }

    class MyThread implements Runnable {
        @Override
        public void run(a) {
            MemberDO memberDO = memberDOMapper.selectByPrimaryKey(1L);
            log.info(Thread.currentThread().getName() + "Retrieved version:{}", memberDO.getVersion());
            memberDO.setIsDelete((byte) 2);
            int re = memberDOMapper.updateWithVersion(memberDO);
            if (re > 0) {
                log.error(Thread.currentThread().getName() + "Update successful");
            } else {
                // Todo self-retry or other exception handling mechanism
                log.error(Thread.currentThread().getName() + "Update failed"); }}}Copy the code

UpdateWithVersion in mapper is written as:

 update 
    t_member
 set 
    is_delete = #{isDelete}, version = version + 1
 where 
    id = #{id}" and version = #{version}")
Copy the code

Observe the console output:

2021-04-14 10:59:38.158  INFO 8840- [Thread0] tech. Spring. SpringBootBaseTest: Thread0 get version:1
2021-04-14 10:59:38.158  INFO 8840- [Thread6] tech. Spring. SpringBootBaseTest: Thread6 get version:1
2021-04-14 10:59:38.158  INFO 8840- [Thread5] tech. Spring. SpringBootBaseTest: Thread5 get version:1
2021-04-14 10:59:38.158  INFO 8840- [Thread9] tech. Spring. SpringBootBaseTest: Thread9 get version:1
2021-04-14 10:59:38.158  INFO 8840- [Thread8] tech. Spring. SpringBootBaseTest: Thread8 get version:1
2021-04-14 10:59:38.158  INFO 8840- [Thread2] tech. Spring. SpringBootBaseTest: Thread2 get version:1
2021-04-14 10:59:38.158  INFO 8840- [Thread1] tech. Spring. SpringBootBaseTest: Thread1 get version:1
2021-04-14 10:59:38.158  INFO 8840- [Thread3] tech. Spring. SpringBootBaseTest: Thread3 get version:1
2021-04-14 10:59:38.158  INFO 8840- [Thread7] tech. Spring. SpringBootBaseTest: Thread7 get version:1
2021-04-14 10:59:38.158  INFO 8840- [Thread4] tech. Spring. SpringBootBaseTest: Thread4 get version:1

2021-04-14 10:59:38.178 ERROR 8840- [Thread5] tech. Spring. SpringBootBaseTest: Thread5 update failure2021-04-14 10:59:38.179 ERROR 8840- [Thread6] tech. Spring. SpringBootBaseTest: Thread6 update failure2021-04-14 10:59:38.179 ERROR 8840- [Thread4] tech. Spring. SpringBootBaseTest: Thread4 update failure2021-04-14 10:59:38.180 ERROR 8840- [Thread3] tech. Spring. SpringBootBaseTest: Thread3 update failure2021-04-14 10:59:38.180 ERROR 8840- [Thread9] tech. Spring. SpringBootBaseTest: Thread9 updated2021-04-14 10:59:38.180 ERROR 8840- [Thread1] tech. Spring. SpringBootBaseTest: Thread1 update failure2021-04-14 10:59:38.182 ERROR 8840- [Thread8] tech. Spring. SpringBootBaseTest: Thread8 update failure2021-04-14 10:59:38.182 ERROR 8840- [Thread2] tech. Spring. SpringBootBaseTest: Thread2 update failure2021-04-14 10:59:38.184 ERROR 8840- [Thread7] tech. Spring. SpringBootBaseTest: Thread7 update failure2021-04-14 10:59:38.185 ERROR 8840- [Thread0] tech. Spring. SpringBootBaseTest: Thread0 update failureCopy the code

You can see that only one thread was successfully updated

Pessimistic locking

  • It is considered that there are too many concurrent write operations and the data will be modified each time the data is fetched, so the lock is added.
  • This applies to scenarios where you write more and read less.
  • Usage scenarios
  1. Java synchronized
  2. Select * from t_xxx for update;

The sample

Write a service method, plus a transaction. The SQL query statement for UPDATE is used to query the data. After the data is queried, the transaction is submitted for 1s.

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void testPessimisticLock(a) {
        long begin = System.currentTimeMillis();
        MemberDO memberDO = memberDOMapper.selectByIdForUpdate(1L);
        if (null! = memberDO &&null! = memberDO.getId()) { log.error(Thread.currentThread().getName() +"Get the data");
        }
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        log.error(Thread.currentThread().getName() + " 耗时: " + (System.currentTimeMillis() - begin));
    }
Copy the code
select id from t_member where id  = #{id} for update
Copy the code

Write a single test that calls the service concurrently with five threads

    @Test
    public void pessimisticLock(a) throws InterruptedException {
        for (int i = 0; i < 5; i++) {
            Thread thread = new Thread(new MyPessimisticThread());
            thread.setName("Thread" + i);
            thread.start();
        }
        // Wait for the thread to finish and print the result on the console
        Thread.sleep(10000);
    }

    class MyPessimisticThread implements Runnable {

        @Override
        public void run(a) { orderOperService.testPessimisticLock(); }}Copy the code

Run the single test and observe the console output

2021-04-14 14:11:44.377 ERROR 6348- [hread3] T.S.S.O rder. Impl. OrderOperServiceImpl: Thread3 get data2021-04-14 14:11:45.380 ERROR 6348- [hread3] T.S.S.O rder. Impl. OrderOperServiceImpl: Thread3 time-consuming:1275
2021-04-14 14:11:45.383 ERROR 6348- [hread2] T.S.S.O rder. Impl. OrderOperServiceImpl: Thread2 get data2021-04-14 14:11:46.388 ERROR 6348- [hread2] T.S.S.O rder. Impl. OrderOperServiceImpl: Thread2 time-consuming:2283
2021-04-14 14:11:46.390 ERROR 6348- [hread0] T.S.S.O rder. Impl. OrderOperServiceImpl: Thread0 get data2021-04-14 14:11:47.393 ERROR 6348- [hread0] T.S.S.O rder. Impl. OrderOperServiceImpl: Thread0 time-consuming:3288
2021-04-14 14:11:47.396 ERROR 6348- [hread1] T.S.S.O rder. Impl. OrderOperServiceImpl: Thread1 get data2021-04-14 14:11:48.408 ERROR 6348- [hread1] T.S.S.O rder. Impl. OrderOperServiceImpl: Thread1 time-consuming:4303
2021-04-14 14:11:48.409 ERROR 6348- [hread4] T.S.S.O rder. Impl. OrderOperServiceImpl: Thread4 get data2021-04-14 14:11:49.425 ERROR 6348- [hread4] T.S.S.O rder. Impl. OrderOperServiceImpl: Thread4 time-consuming:5320
Copy the code

As can be seen, at the same time, only one thread can fetch data, the rest threads enter the wait. The last thread (Thread4) takes the longest

Pay attention to

For UPDATE needs to happen in a transaction. In the code above, if the @Transactional annotation on testpessimism () is removed, multiple threads can fetch data at the same time. If for Update does not take effect, threads are not safe.