Definition of thread safety:
Is a class thread-safe when it is accessed by multiple threads, regardless of how the runtime environment is scheduled or how the processes are executed interchangeably, and does not require any additional synchronization or coordination in the calling code
There are two types of atomic locks:
Synchronized: is a Java keyword that is a type of synchronization lock that depends on the JVM
Lock: Rely on special CPU instructions, code implementation, ReentrantLock
Synchronized
2. Modify method: entire method, applied to the called object. 3. Modify static method: entire static method, applied to all objects
Modify code block
package com.lyy.concurrency.sync;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class SynchronizedExample1 {
// Decorates a code block
public void test1(int j){
synchronized (this) {for (int i = 0; i < 10; i++) {
System.out.println("Test1 j."+j+" — i:"+i); }}}public static void main(String[] args) {
SynchronizedExample1 example1 = new SynchronizedExample1();
SynchronizedExample1 example2 = new SynchronizedExample1();
ExecutorService executorService = Executors.newCachedThreadPool();// Declare a thread pool
// Add the thread pool and we call two threads
// Two threads called the same object
executorService.execute(() ->{
example1.test1(1);
});
executorService.execute(() ->{
example2.test1(2); }); }}Copy the code
Return result:
Test1 j:1- I:0Test1 j:1- I:1Test1 j:1- I:2Test1 j:1- I:3Test1 j:1- I:4Test1 j:1- I:5Test1 j:1- I:6Test1 j:1- I:7Test1 j:1- I:8Test1 j:1- I:9Test1 j:2- I:0Test1 j:2- I:1Test1 j:2- I:2Test1 j:2- I:3Test1 j:2- I:4Test1 j:2- I:5Test1 j:2- I:6Test1 j:2- I:7Test1 j:2- I:8Test1 j:2- I:9
Copy the code
Thread 1 and thread 2 execute in their own order. Thread 1 and thread 2 can follow their own synchronized code, but there is no guarantee that thread 1 will execute until thread 2 executes, depending on which thread can grab the resources first.
Modification methods
package com.lyy.concurrency.sync;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class SynchronizedExample1 {
// Decorates a method
public synchronized void test2(int j){
for (int i = 0; i < 10; i++) {
System.out.println("test2 j:"+j+" — i:"+i); }}public static void main(String[] args) {
SynchronizedExample1 example1 = new SynchronizedExample1();
SynchronizedExample1 example2 = new SynchronizedExample1();
ExecutorService executorService = Executors.newCachedThreadPool();// Declare a thread pool
// Add the thread pool and we call two threads
// Two threads called the same object
executorService.execute(() ->{
example1.test2(1);
});
executorService.execute(() ->{
example2.test2(2); }); }}Copy the code
Return result:
Test2 j:1- I:0Test2 j:1- I:1Test2 j:1- I:2Test2 j:1- I:3Test2 j:1- I:4Test2 j:1- I:5Test2 j:1- I:6Test2 j:1- I:7Test2 j:2- I:0Test2 j:2- I:1Test2 j:2- I:2Test2 j:2- I:3Test2 j:2- I:4Test2 j:2- I:5Test2 j:2- I:6Test2 j:2- I:7Test2 j:2- I:8Test2 j:1- I:8Test2 j:2- I:9Test2 j:1- I:9
Copy the code
We can see that 1 and 2 are running interchangeably, but each is executing in order, because the modifier block only works on the object that’s currently being called, and we’re calling two methods so the two threads are not interfering with each other, each executing its own code, and synchronization is the whole method
Note: If SynchronizedExample1 were a subclass, test2 would not implement synchronized because synchronized is not part of the method declaration, and therefore cannot be inherited. To implement this subclass inheriting synchronized, you need to implement it manually
decorator
package com.lyy.concurrency.sync;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class SynchronizedExample2 {
// Decorates a class
public static void test1(int j){
synchronized (SynchronizedExample2.class){
for (int i = 0; i < 10; i++) {
System.out.println("Test1 j."+j+" — i:"+i); }}}public static void main(String[] args) {
SynchronizedExample2 example1 = new SynchronizedExample2();
SynchronizedExample2 example2 = new SynchronizedExample2();
ExecutorService executorService = Executors.newCachedThreadPool();// Declare a thread pool
// Adding the thread pool equals calling two threads L
// Two threads called the same object
executorService.execute(() ->{
example1.test1(1);
});
executorService.execute(() ->{
example2.test1(2); }); }}Copy the code
Return result:
Test1 j:1- I:0Test1 j:1- I:1Test1 j:1- I:2Test1 j:1- I:3Test1 j:1- I:4Test1 j:1- I:5Test1 j:1- I:6Test1 j:1- I:7Test1 j:1- I:8Test1 j:1- I:9Test1 j:2- I:0Test1 j:2- I:1Test1 j:2- I:2Test1 j:2- I:3Test1 j:2- I:4Test1 j:2- I:5Test1 j:2- I:6Test1 j:2- I:7Test1 j:2- I:8Test1 j:2- I:9
Copy the code
Decorated static methods
package com.lyy.concurrency.sync;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class SynchronizedExample2 {
// Decorate a static method
public static synchronized void test2(int j){
for (int i = 0; i < 10; i++) {
System.out.println("test2 j:"+j+" — i:"+i); }}public static void main(String[] args) {
SynchronizedExample2 example1 = new SynchronizedExample2();
SynchronizedExample2 example2 = new SynchronizedExample2();
ExecutorService executorService = Executors.newCachedThreadPool();// Declare a thread pool
// Adding the thread pool equals calling two threads L
// Two threads called the same object
executorService.execute(() ->{
example1.test2(1);
});
executorService.execute(() ->{
example2.test2(2); }); }}Copy the code
Return result:
Test2 j:1- I:0Test2 j:1- I:1Test2 j:1- I:2Test2 j:1- I:3Test2 j:1- I:4Test2 j:1- I:5Test2 j:1- I:6Test2 j:1- I:7Test2 j:1- I:8Test2 j:1- I:9Test2 j:2- I:0Test2 j:2- I:1Test2 j:2- I:2Test2 j:2- I:3Test2 j:2- I:4Test2 j:2- I:5Test2 j:2- I:6Test2 j:2- I:7Test2 j:2- I:8Test2 j:2- I:9
Copy the code
Case study:
Thread unsafe cases:
package com.lyy.concurrency.example.count;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
public class CountExample1 {
// Total requests
public static int clientTotal = 5000;
// The number of concurrent threads
public static int threadTotal = 200;
//
public static int count = 0;
public static void main(String[] args) throws Exception{
ExecutorService executorService = Executors.newCachedThreadPool();/ / thread pool
final Semaphore semaphore = new Semaphore(threadTotal);// The number of concurrent requests allowed
final CountDownLatch countDownLatch = new CountDownLatch(clientTotal);
for (int i = 0; i < clientTotal; i++) {
executorService.execute(() ->{
try {
semaphore.acquire();// Determine whether the thread is allowed to execute
add();// Acquire () will not be executed until it returns a value
semaphore.release();
} catch (InterruptedException e) {
e.printStackTrace();
}
countDownLatch.countDown();
});
}
countDownLatch.await();
executorService.shutdown();
System.out.println("count:"+count);
}
private static void add(a){ count++; }}Copy the code
Execution Result:
The count:4973
Copy the code
The correct result is 4973, and the correct result is 5000. How can we get the result to 5000 by using synchronized for a thread-safe class
Thread-safe classes:
package com.lyy.concurrency.example.count;
import com.lyy.concurrency.annoatioons.NotThreadSafe;
import com.lyy.concurrency.annoatioons.ThreadSafe;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
@Slf4j
@ThreadSafe // Thread-safe classes
public class CountExample3 {
// Total requests
public static int clientTotal = 5000;
// The number of concurrent threads
public static int threadTotal = 200;
//
public static int count = 0;
public static void main(String[] args) throws Exception{
ExecutorService executorService = Executors.newCachedThreadPool();/ / thread pool
final Semaphore semaphore = new Semaphore(threadTotal);// The number of concurrent requests allowed
final CountDownLatch countDownLatch = new CountDownLatch(clientTotal);
for (int i = 0; i < clientTotal; i++) {
executorService.execute(() ->{
try {
semaphore.acquire();// Determine whether the thread is allowed to execute
add();// Acquire () will not be executed until it returns a value
semaphore.release();
} catch (InterruptedException e) {
e.printStackTrace();
}
countDownLatch.countDown();
});
}
countDownLatch.await();
executorService.shutdown();
System.out.println("count:"+count);
}
private synchronized static void add(a){ count++; }}Copy the code
Here we add synchronized to make our results show the correct 5000
Return result:
The count:5000
Copy the code
Summary: 1, synchronized: is an uninterruptible lock, suitable for the competition is not fierce, readable 2, whether synchronized keyword added to the method or object, if it acts on the object is not static, then it obtains the lock is the object; 3. If synchronized acts on a static method or a class, it acquires the same lock for all objects of that class. 4. Each object has only a lock associated with it, and whoever holds this lock can run the code it controls. 5. Synchronization is expensive and can even cause deadlocks, so avoid unnecessary synchronization control.