Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

The concept and function of Lock and synchronized in java

2025-04-13 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Internet Technology >

Share

Shulou(Shulou.com)06/02 Report--

This article mainly explains "the concept and function of lock and synchronized in java". Interested friends may wish to have a look. The method introduced in this paper is simple, fast and practical. Let the editor take you to learn "the concept and function of lock and synchronized in java".

Common concepts of locks

Mutex: only one thread executes at a time

Critical section: a piece of code that needs to be mutually exclusive

Fine-grained locks: fine-grained management of protected resources with different locks. Fine-grained locks can improve parallelism and is an important means of performance optimization.

Deadlock: a phenomenon in which a group of competing threads wait for each other, resulting in "permanent" blocking.

Best practices for using locks

Lock only when the member variables of the object are always updated.

Always lock only when accessing mutable member variables.

Never lock when calling methods of other objects.

Reduce the holding time of the gain and reduce the granularity of the lock.

Synchronous and asynchronous

The calling method is synchronous if it needs to wait for the result, or asynchronous if it does not need to wait for the result.

Synchronization is the default way of handling Java code.

How to implement program support for asynchrony:

Asynchronous invocation: the caller creates a child thread and executes the method call in the child thread.

Asynchronous method: the callee; when the method is implemented, create a thread that appears to execute the main logic, and the main thread directly return.

Synchronizedclass X {/ / modify non-static method synchronized void foo () {/ / critical section} / / modify static method synchronized static void bar () {/ / critical section} / / modify code block Object obj = new Object () Void baz () {synchronized (obj) {/ / critical section}

The Java compiler will automatically add lock lock () and unlock unlock () before and after synchronized-decorated methods or blocks of code. The advantage of this is that locking lock () and unlocking unlock () must occur in pairs. After all, forgetting to unlock unlock () can be a deadly Bug (meaning that other threads will have to wait).

Modify static methods: / / modify static methods with the bytecode file of the current class as locks class X {/ / modify static methods synchronized (X.class) static void bar () {/ / critical section}} modify non-static methods: / / modify non-static methods using current objects as locks class X {/ / modify non-static methods Synchronized (this) static void bar () {/ / critical region}}

How to protect multiple resources with one lock

The reasonable relationship between protected resources and locks should be the relationship of NR1, that is, multiple resources can be protected with one lock, but not one resource with multiple locks.

The correct posture for using the lock

According to the transfer business as an example

Example 1:

Public class Account {/ * lock: protect account balance * / private final Object balLock = new Object (); / * * account balance * / private Integer balance / * * wrong practice * the lock of the non-static method is this, * this this lock can protect your own balance this.balance, but cannot protect other people's balance target.balance * * / synchronized void transfer (Account target,int amt) {if (this.balance > amt) {this.balance-= amt; target.balance + = amt / / Thread safety occurs in this code. To ensure thread safety, use the same lock}.

Example 2:

Public class Account {/ * lock: protect account balance * / private final Object balLock = new Object (); / * * account balance * / private Integer balance / * correct, but will cause the serial Account.class of the entire transfer system to be shared by all Account objects, * and this object is created by the Java virtual machine when the Account class is loaded * so we don't have to worry about its uniqueness * * there is another drawback: all transfers are serial * / void transfer2 (Account target,int amt) {synchronized (Account.class) {if (this.balance > amt) {this.balance-= amt Target.balance + = amt;}

In this way, the transfer operation becomes serial, and the normal logic should only lock the transfer account and be transferred to the account; it does not affect other transfer operations. Make a slight modification:

Example 3:

Public class Account {/ * lock: protect account balance * / private final Object lock; / * * account balance * / private Integer balance; / / privatize non-parameter construction private Account () {} / / set a parameter construction private Account (Object lock) {this.lock = lock that passes lock } / * transfer * / void transfer (Account target,int amt) {/ / check here all objects share lock synchronized (lock) {if (this.balance > amt) {this.balance-= amt; target.balance + = amt;}

Although this method can solve the problem, it requires that the same object must be passed in when the Account object is created

In addition, it is too troublesome to transfer the object, and the writing method is tedious and lack of feasibility.

Example 4:

Public class Account {/ * * account balance * / private Integer balance; / * transfer * / void transfer (Account target,int amt) {/ / check here all objects share lock synchronized (Account.class) {if (this.balance > amt) {this.balance-= amt Target.balance + = amt;}

Using Account.class as a shared lock, the scope of locking is too large. Account.class is shared by all Account objects, and this object is created by the Java virtual machine when the Account class is loaded, so we don't have to worry about its uniqueness. Using Account.class as the shared lock, we don't need to pass in the Account object when we create it.

In this way, a new problem arises, although Account.class is used as a mutex to solve the problem of money transfer in banking business, although there is no concurrency problem in this scheme, the transfer operations of all accounts are serial, such as account A to account B and account C to account D. the two transfer operations can be parallel in the real world, but they are serialized in this scheme, so the performance is too poor. Therefore, this method will not work if we consider the concurrency.

The correct way to write is like this (using fine-grained locks):

Example 5:

Public class Account {/ * * account balance * / private Integer balance / * transfer * / void transfer (Account target,int amt) {/ / Lock the transfer account synchronized (this) {/ / Lock the transfer account synchronized (target) {if (this.balance > amt) {this.balance-= amt; target.balance + = amt }}}

Let's just imagine that in ancient times, without information, the existence form of the account was really an account book, and each account had an account book, which were all stored on the file shelf. When the bank teller transfers money to us, he has to go to the file shelf to get both the transferred account book and the transferred account book, and then make the transfer. * * this teller may encounter the following three situations when taking the account book:

There happens to be transfer out of the account book and transfer to the account book on the file shelf, so take it away at the same time

If there is only one ledger transferred out and transferred to the file shelf, the teller will first get the ledger on the file shelf and wait for the other teller to send back another ledger.

If there is no transfer out of the account book or transfer to the account book, the teller is waiting for both account books to be sent back.

Fine-grained locks may lead to deadlocks

Deadlock: a phenomenon in which a group of competing threads wait for each other, resulting in "permanent" blocking.

If two threads hold each other's resources and do not release them, it will lead to a deadlock.

Using fine-grained locks can lead to deadlocks

If a customer asks a teller Zhang San to do a transfer business: account A transfers account B 100 yuan, another customer asks teller Li Si to do a transfer business: account B transfers account A 100 yuan, so both Zhang San and Li Si go to the file shelf to get the account book at the same time. At this time, it is possible that Zhang San got the account book An and Li Si got the account book B. After Zhang San got the ledger A, he waited for the ledger B (the ledger B has been taken away by Li Si), while Li Si got the ledger B and waited for the ledger A (the ledger A has been taken by Zhang San). How long do they have to wait? They will wait forever... Because Zhang San will not return ledger A, Li Si will not send ledger B back. Let's call it death and wait.

How to avoid deadlock

Mutually exclusive, shared resources X and Y can only be occupied by one thread

Hold and wait, thread T1 has acquired the shared resource X, while waiting for the shared resource Y, the shared resource x will not be released

Cannot be preempted. Other threads cannot forcibly preempt resources occupied by thread T1.

Loop wait, thread 1 waits for the resources occupied by thread T2, and thread T2 waits for resources occupied by thread T1, which is loop waiting.

As long as you break one of them, you can avoid deadlocks.

Wait-notification mechanism

Implementation of wait-Notification Mechanism with synchronized

Synchronized with wait (), notif (), notifyAll () these three methods can be easily implemented.

Wait (): the current thread releases the lock and enters the blocking state

Notif (), notifAll (): notifies the blocked thread that it can continue to execute and the thread enters the executable state

Notif () randomly notifies the waiting team of at least one thread

NotifyAll () notifies all threads in the waiting queue, and it is recommended to use notifAll ()

The difference between wait and sleep:

Sleep is a method in Object, and wait is a method in Thread

Wait releases the lock, sleep does not release the lock

Wait needs to wake up with notif. Sleep sets the time. The time is up.

Wait does not need to catch exceptions, while sleep needs

Wait (): the current thread enters blocking

At this point, I believe you have a deeper understanding of "the concept and function of lock and synchronized in java". You might as well do it in practice. Here is the website, more related content can enter the relevant channels to inquire, follow us, continue to learn!

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 210

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Internet Technology

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report