Small knowledge, big challenge! This article is participating in the creation activity of “Essential Tips for Programmers”.
JUC
JUC is short for the Java.util.Concurrent toolkit. This is a toolkit for handling threads.
And the package contains many concurrent programming needs to use the class.
JUC has been around since JDK 1.5.
JUC is designed to better support high-concurrency tasks
Concurrency: Can be understood as multiple threads operating on the same resource
1. Traditional Synchronized locks
Do not add the Synchronize the lock
Take selling tickets:
package com.cheng.lock;
public class LockDemo1 {
public static void main(String[] args) {
Ticket ticket = new Ticket();
// Concurrency: Put the resource class in each thread, multiple threads operate on the same resource lambda expression: (parameter)->{code}
new Thread(()-> {
for (int i = 0; i < 50; i++) { ticket.sale(); }},"Xiao Ming").start();
new Thread(()-> {
for (int i = 0; i < 50; i++) { ticket.sale(); }},"Little red").start();
new Thread(()-> {
for (int i = 0; i < 50; i++) { ticket.sale(); }},"White").start(); }}Resource class Ticket, attribute + method
class Ticket{
private int nums = 50;
public void sale(a){
if (nums > 0){
System.out.println(Thread.currentThread().getName()+"Sold the first."+(nums--)+"One ticket, the rest."+nums+"Ticket"); }}}Copy the code
Run to view the result:
When a resource is operated without a Synchronize lock, data is corrupted when multiple threads operate the resource.
Combined with the Synchronize lock
Add a Synchronize lock to the method
public synchronized void sale(a){
if (nums > 0){
System.out.println(Thread.currentThread().getName()+"Sold the first."+(nums--)+"One ticket, the rest."+nums+"Ticket"); }}Copy the code
After running, the data is in order without disorder.
2, the LOCK LOCK
Lock is an interface that controls multiple threads’ access to a shared resource under the JUC package. Only one thread can hold the Lock at a time.
The thread should acquire the Lock object before starting to access the shared resource.
Lock implementation class
ReentrantLock: ReentrantLock, exclusive lock, lock and unlock process needs to be manually, difficult to operate, but very flexible
ReentrantReadWriteLock. ReadLock: read lock
ReentrantReadWriteLock. WriteLock: write locks
ReentrantLock implements fair and unfair locks
Fair and unfair locks:
A fair lock means that when a lock is available, the thread that has waited the longest on the lock gains access to it, first come, second come.
Non-fair lock is randomly assigned this right to use, can jump the queue.
Already the source code:
According to the above source code analysis:
The default ReentrantLock implementation is unfair locking (because unfair locking performs better)
When creating a ReentrantLock, pass true to create a fair lock, pass false or pass no argument to create an unfair lock
LOCK LOCK example
1、new ReentrantLock();
2, lock lock. Lock ()
3, Unlock lock.unlock()
package com.cheng.lock;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class LockDemo2 {
public static void main(String[] args) {
Ticket ticket = new Ticket();
// Concurrency: Put the resource class in each thread, multiple threads operate on the same resource lambda expression: (parameter)->{code}
new Thread(()-> { for (int i = 0; i < 20; i++) ticket.sale(); },"Xiao Ming").start();
new Thread(()-> { for (int i = 0; i < 20; i++) ticket.sale(); },"Little red").start();
new Thread(()-> { for (int i = 0; i < 20; i++) ticket.sale(); },"White").start(); }}Resource class Ticket, attribute + method
class Ticket1{
private int nums = 20;
// Create a ReentrantLock object
Lock lock = new ReentrantLock();
public void sale(a){
lock.lock(); / / lock
try {
// Business code
if (nums > 0){
System.out.println(Thread.currentThread().getName()+"Sold the first."+(nums--)+"One ticket, the rest."+nums+"Ticket"); }}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock(); / / unlock}}}Copy the code
3. The difference between Synchronized and Lock
1. Synchronized is a built-in Java keyword. On the JVM level, Lock is a Java class.
2. Synchronized cannot determine whether the Lock is obtained. Lock can determine whether the Lock is obtained.
3. Synchronized will automatically release the Lock when an abnormal thread occurs, so no abnormal deadlock will occur. Lock must be manually released in finally;
Synchronized: if thread A blocks, thread B will wait and Lock will not wait. TryLock () is used to try to acquire the Lock. If it cannot obtain the Lock, the thread can stop waiting.
5. Synchronized locks are reentrant, uninterruptible, and non-fair, while synchronized locks are reentrant, judge, and fair.
6.Lock Lock is suitable for a large number of synchronized code synchronization problems, synchronized Lock is suitable for a small number of code synchronization problems.
7. In terms of performance, if the resource competition is not fierce, the performance of the two is similar, but when the resource competition is very fierce (that is, a large number of threads are competing at the same time), the performance of Lock is far better than synchronized.
4. Producer-consumer issues
4.1 Synchronized producer and consumer issues
package com.cheng.PC;
public class PCSynchronize {
public static void main(String[] args) {
Test1 test1 = new Test1();
// Create two threads to manipulate resources
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
test1.increment();
} catch(InterruptedException e) { e.printStackTrace(); }}},"A").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
test1.decrement();
} catch(InterruptedException e) { e.printStackTrace(); }}},"B").start(); }}class Test1{
private int nums = 0;
public synchronized void increment(a) throws InterruptedException {
if(nums ! =0) {this.wait();// The thread waits
}
nums++;
System.out.println(Thread.currentThread().getName()+"-- >"+nums);
this.notifyAll();// after nums++, wake up other threads to num--
}
public synchronized void decrement(a) throws InterruptedException {
if (nums == 0) {this.wait();
}
nums--;
System.out.println(Thread.currentThread().getName()+"-- >"+nums);
this.notifyAll();//nums-- after the notification wakes up other threads to num++}}Copy the code
Start test:
The above code looks fine, but what if we increased the number of threads from two to four?
Add two threads C,D:
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
test1.increment();
} catch(InterruptedException e) { e.printStackTrace(); }}},"C").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
test1.decrement();
} catch(InterruptedException e) { e.printStackTrace(); }}},"D").start();
Copy the code
Start the test again:
Communication between threads is problematic because we use if to determine if we need to wait, which creates a false wake up, and waiting should always occur in the loop.
False wake up: A thread can be woken up without being notified, interrupted, or timed out.
Fix false wake up: change the above if to while:
public synchronized void increment(a) throws InterruptedException {
while(nums ! =0) {this.wait();// The thread waits
}
nums++;
System.out.println(Thread.currentThread().getName()+"-- >"+nums);
this.notifyAll();// after nums++, wake up other threads to num--
}
public synchronized void decrement(a) throws InterruptedException {
while (nums == 0) {this.wait();
}
nums--;
System.out.println(Thread.currentThread().getName()+"-- >"+nums);
this.notifyAll();//nums-- after the notification wakes up other threads to num++
}
Copy the code
Start the test again: OK!
4.2 Lock version producer consumer issues
Contition is introduced
Synchronized is used in conjunction with wait()/notify() to implement wait/notification mode. The Lock object’s newContition() method returns an instance of Condition. The Condition class also implements wait/notification mode.
Condition commonly used methods:
-
Await () : causes the current thread to wait and releases the lock. When another thread calls signal() or signalAll(), the thread regains the lock and continues.
-
Signal () : wakes up a waiting thread. If any thread is waiting for this condition, a thread is selected to wake up and that thread must reacquire the lock before receiving await.
-
SignalAll () : Wakes up all waiting threads. If any thread is waiting for this condition, then they are woken up and each thread must reacquire the lock before receiving from await.
Condition Notifies and waits
Code test:
package com.cheng.PC;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class PCLock {
public static void main(String[] args) {
Test2 test2 = new Test2();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
test2.increment();
} catch(InterruptedException e) { e.printStackTrace(); }}},"A").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
test2.decrement();
} catch(InterruptedException e) { e.printStackTrace(); }}},"B").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
test2.increment();
} catch(InterruptedException e) { e.printStackTrace(); }}},"C").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
test2.decrement();
} catch(InterruptedException e) { e.printStackTrace(); }}},"D").start(); }}class Test2{
private int nums = 0;
Lock lock = new ReentrantLock();// Get the lock object
Condition condition = lock.newCondition();// Get the Condtion object
public void increment(a) throws InterruptedException {
lock.lock();
try {
while(nums ! =0) {// The thread waits
condition.await();
}
nums++;
System.out.println(Thread.currentThread().getName()+"-- >"+nums);
// after nums++, wake up other threads to num--
condition.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
} finally{ lock.unlock(); }}public void decrement(a) throws InterruptedException {
lock.lock();
try {
while (nums == 0){
condition.await();
}
nums--;
System.out.println(Thread.currentThread().getName()+"-- >"+nums);
condition.signalAll();//nums-- after the notification wakes up other threads to num++
} catch (InterruptedException e) {
e.printStackTrace();
} finally{ lock.unlock(); }}}Copy the code
Result: Wake up randomly, execution order is out of order
With notify(), the JVM wakes up a waiting thread at random, and the Condition class allows selective notification.
Condition implements selective notification
Multiple Condition implements partial thread notification, which is more flexible to use,
Create three threads A, B, and C in the sequence A->B->C
package com.cheng.PC;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class PCLockPlus {
public static void main(String[] args) {
Test3 test3 = new Test3();
// Create three threads A, B, and C in the order A->B->C
new Thread(() -> {
for (int i = 0; i < 10; i++) { test3.TestA(); }},"A").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) { test3.TestB(); }},"B").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) { test3.TestC(); }},"C").start(); }}class Test3 {
private Lock lock = new ReentrantLock();
// Create three Condition instances, each monitoring a thread
private Condition condition1 = lock.newCondition();
private Condition condition2 = lock.newCondition();
private Condition condition3 = lock.newCondition();
private int nums = 1; //nums=1, execute thread A; Nums =2, execute thread B; Nums =3, execute thread C
public void TestA(a) {
lock.lock();
try {
while(nums ! =1) {
condition1.await();
}
nums = 2;
System.out.println(Thread.currentThread().getName() + "->AAAAA");
condition2.signal();Condition2 when executed
} catch (Exception e) {
e.printStackTrace();
} finally{ lock.unlock(); }}public void TestB(a) {
lock.lock();
try {
while(nums ! =2) {
condition2.await();
}
nums = 3;
System.out.println(Thread.currentThread().getName() + "->BBBBB");
condition3.signal();// specify condition3
} catch (Exception e) {
e.printStackTrace();
} finally{ lock.unlock(); }}public void TestC(a) {
lock.lock();
try {
while(nums ! =3) {
condition3.await();
}
nums = 1;
System.out.println(Thread.currentThread().getName() + "->CCCCC");
condition1.signal();// specify condition1
} catch (Exception e) {
e.printStackTrace();
} finally{ lock.unlock(); }}}Copy the code