Close

JPA - Using Pessimistic Locking

[Last Updated: Dec 22, 2017]

Optimistic locking assumes (as being optimistic) that there will be rare chances of multi users read/write conflicts, so it delays the conflict checking till the commit time, whereas pessimistic assumes that there is a high possibility of conflict and acquires a database lock at begging of the transaction.

In following example we will learn how to implement pessimistic locking by using LockModeType.PESSIMISTIC_READ and LockModeType.PESSIMISTIC_WRITE.

A transaction involving LockModeType.PESSIMISTIC_READ is an intent to just read the entity without modifying it, whereas LockModeType.PESSIMISTIC_WRITE transaction may modify the entity.

A transaction that has acquired PESSIMISTIC_READ lock, prevents any other transaction from acquiring a PESSIMISTIC_WRITE. Multiple PESSIMISTIC_READs can still happen simultaneously, that's the reason it is also known as shared lock.

A transaction that has acquired PESSIMISTIC_WRITE lock, prevents any other transaction from acquiring either of PESSIMISTIC_WRITE or PESSIMISTIC_READ lock. It is also known as exclusive lock.

Example

The Entity

@Entity
public class Article {
    @Id
    @GeneratedValue
    private long id;
    private String content;
    .............
}

Pessimistic write blocking pessimistic read example

In following example, we are going to use threads to simulate two users. First thread will obtain PESSIMISTIC_WRITE lock and will update the Article entity, during that time another thread will try to obtain PESSIMISTIC_READ lock and will block (or it may fail with exception, depending on underlying locking timeout value) till the first transaction is committed:

public class PessimisticLockExample {
    private static EntityManagerFactory entityManagerFactory =
            Persistence.createEntityManagerFactory("example-unit");

    public static void main(String[] args) throws Exception {
        ExecutorService es = Executors.newFixedThreadPool(3);
        try {
            persistArticle();
            es.execute(() -> {
                updateArticle();
            });
            es.execute(() -> {
                //simulating other user by using different thread
                readArticle();
            });
            es.shutdown();
            es.awaitTermination(1, TimeUnit.MINUTES);
        } finally {
            entityManagerFactory.close();
        }
    }

    private static void updateArticle() {
        log("updating Article entity");
        EntityManager em = entityManagerFactory.createEntityManager();
        em.getTransaction().begin();
        Article article = em.find(Article.class, 1L, LockModeType.PESSIMISTIC_WRITE);
        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        article.setContent("updated content .. ");
        log("committing in write thread.");
        em.getTransaction().commit();
        em.close();
        log("Article updated", article);
    }

    private static void readArticle() {
        try {//some delay before reading
            TimeUnit.MILLISECONDS.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        log("before acquiring read lock");
        EntityManager em = entityManagerFactory.createEntityManager();
        em.getTransaction().begin();
        Article article = em.find(Article.class, 1L, LockModeType.PESSIMISTIC_READ);
        log("After acquiring read lock", article);
        em.getTransaction().commit();
        em.close();
        log("Article after read commit", article);
    }

    public static void persistArticle() {
        log("persisting article");
        Article article = new Article("test article");
        EntityManager em = entityManagerFactory.createEntityManager();
        em.getTransaction().begin();
        em.persist(article);
        em.getTransaction().commit();
        em.close();
        log("Article persisted", article);
    }

    private static void log(Object... msgs) {
        System.out.println(LocalTime.now() + " - " + Thread.currentThread().getName() +
                " - " + Arrays.toString(msgs));
    }
}
22:39:33.330 - main - [persisting article]
22:39:33.413 - main - [Article persisted, Article{id=1, content='test article'}]
22:39:33.414 - pool-2-thread-1 - [updating Article entity]
22:39:33.514 - pool-2-thread-2 - [before acquiring read lock]
22:39:35.423 - pool-2-thread-1 - [committing in write thread.]
22:39:35.428 - pool-2-thread-1 - [Article updated, Article{id=1, content='updated content .. '}]
22:39:35.435 - pool-2-thread-2 - [After acquiring read lock, Article{id=1, content='updated content .. '}]
22:39:35.436 - pool-2-thread-2 - [Article after read commit, Article{id=1, content='updated content .. '}]

As seen in above output, read thread blocked till the commit time of the write transaction.

Pessimistic read blocking pessimistic write example

This example reads Article entity first with PESSIMISTIC_READ lock mode, hence the update thread (PESSIMISTIC_WRITE mode) blocks till the read transaction finishes.

public class PessimisticLockExample2 {
    private static EntityManagerFactory entityManagerFactory =
            Persistence.createEntityManagerFactory("example-unit");

    public static void main(String[] args) throws Exception {
        ExecutorService es = Executors.newFixedThreadPool(3);
        try {
            persistArticle();
            es.execute(() -> {
                //simulating other user read by using different thread
                readArticle();
            });
            es.execute(() -> {
                updateArticle();
            });

            es.shutdown();
            es.awaitTermination(1, TimeUnit.MINUTES);
        } finally {
            entityManagerFactory.close();
        }
    }

    private static void updateArticle() {
        try {//some delay before writing
            TimeUnit.MILLISECONDS.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        EntityManager em = entityManagerFactory.createEntityManager();
        em.getTransaction().begin();
        log("write thread before acquiring lock");
        Article article = em.find(Article.class, 1L, LockModeType.PESSIMISTIC_WRITE);
        log("write thread after acquiring lock");
        article.setContent("updated content .. ");
        log("committing in write thread.");
        em.getTransaction().commit();
        em.close();
        log("Article updated", article);
    }

    private static void readArticle() {
        log("before acquiring read lock");
        EntityManager em = entityManagerFactory.createEntityManager();
        em.getTransaction().begin();
        Article article = em.find(Article.class, 1L, LockModeType.PESSIMISTIC_READ);
        log("After acquiring read lock", article);
        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        em.getTransaction().commit();
        em.close();
        log("Article after read commit", article);
    }
    .............
}
22:40:57.654 - main - [persisting article]
22:40:57.742 - main - [Article persisted, Article{id=1, content='test article'}]
22:40:57.743 - pool-2-thread-1 - [before acquiring read lock]
22:40:57.754 - pool-2-thread-1 - [After acquiring read lock, Article{id=1, content='test article'}]
22:40:57.844 - pool-2-thread-2 - [write thread before acquiring lock]
22:40:59.755 - pool-2-thread-1 - [Article after read commit, Article{id=1, content='test article'}]
22:40:59.759 - pool-2-thread-2 - [write thread after acquiring lock]
22:40:59.759 - pool-2-thread-2 - [committing in write thread.]
22:40:59.764 - pool-2-thread-2 - [Article updated, Article{id=1, content='updated content .. '}]

Example Project

Dependencies and Technologies Used:

  • h2 1.4.196: H2 Database Engine.
  • hibernate-core 5.2.12.Final: The core O/RM functionality as provided by Hibernate.
    Implements javax.persistence:javax.persistence-api version 2.1
  • JDK 1.8
  • Maven 3.3.9

Pessimistic Lock Example Select All Download
  • pessimistic-lock-example
    • src
      • main
        • java
          • com
            • logicbig
              • example
                • PessimisticLockExample.java
          • resources
            • META-INF

    See Also