Multithreaded programming
catalogue
work1
package cn.tedu.work; public class ThreadWork1 { public static void main(String[] args) { RunnableThread1 target_a=new RunnableThread1(); Thread thread_a = new Thread(target_a, "a"); Thread thread_b = new Thread(target_a, "b"); Thread thread_c = new Thread(target_a, "c"); try { thread_a.start(); thread_a.join(); thread_b.start(); thread_b.join(); thread_c.start(); thread_c.join(); } catch (InterruptedException e) { e.printStackTrace(); } } } class RunnableThread1 implements Runnable { public void run() { Thread thread=Thread.currentThread(); System.out.println("Current thread is"+thread.getName()); System.out.println("thread "+thread.getName()+"Sleep 2000 ms"); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("thread "+thread.getName()+"end of execution\n"); } }
work2
package cn.tedu.work; public class ThreadWork2 { /** * use * */ public static void main(String[] args) { RunnableThread2 target1 = new RunnableThread2(); Thread threada = new Thread(target1, "a"); Thread threadb = new Thread(target1, "b"); threada.start(); threadb.start(); } } class RunnableThread2 implements Runnable { public static int nub = 0; public static long th_id = 0; Object ob = new Object(); public void run() { while (true) { synchronized (ob) { long cunnt_id = Thread.currentThread().getId(); if (cunnt_id != th_id && nub <= 100) { System.out.println(Thread.currentThread().getName() + ": " + (nub)); th_id = cunnt_id; nub++; } else if (nub > 100) { break; } } } } }
work3
package cn.tedu.work; import java.security.Principal; public class ThreadWork3 { /** * Write two threads. One thread prints 1 ~ 52 and the other thread prints A~Z. the printing order is 12a34b 5152Z * */ public static void main(String[] args) { Thread thread1=new Thread(new PrintNub(),"nub"); Thread thread2 = new Thread(new PrintChar(), "char"); thread1.start(); thread2.start(); } } class PrintNub implements Runnable{ public void run() { int nub=1; while (true){ synchronized (ThreadWork3.class){ if(nub<52){ System.out.println(nub+" "+(nub+1)); nub+=2; }else if(nub>=52){ ThreadWork3.class.notifyAll(); break; } ThreadWork3.class.notifyAll(); try { ThreadWork3.class.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } } } class PrintChar implements Runnable{ public static Object ob=new Object(); public void run() { char a='A'; while (true){ synchronized (ThreadWork3.class){ if(a<='Z'){ System.out.println(a); a+=1; }else if(a>'Z'){ ThreadWork3.class.notify(); break; } ThreadWork3.class.notifyAll(); try { ThreadWork3.class.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } } }
work4
package cn.tedu.work; public class ThreadWork4 { /** * Write A program to start three threads whose IDs are A, B and C;, Each thread prints its ID value on the screen 5 times in the order of ABCABC * */ public static void main(String[] args) { ThreadA target=new ThreadA(); Thread thread1 = new Thread(target, "A"); Thread thread2 = new Thread(target, "C"); Thread thread3 = new Thread(target, "B"); thread1.start(); thread2.start(); thread3.start(); } } class ThreadA implements Runnable { public volatile char t_char='A'; public void run() { for (int i = 0; i < 5; ) { Thread thread = Thread.currentThread(); char ch = thread.getName().charAt(0); if (ch==t_char) { System.out.printf(Thread.currentThread().getName()); i++; t_char= (char) ('A'+(t_char-'A'+1)%3); } } } }
work5
package cn.tedu.work; public class ThreadWork5 { /** * Write 10 threads. The first thread increases from 1 to 10, * The second thread increases from 11 to 20... The tenth thread increases from 91 to 100, * Finally, add the results of 10 threads. * */ public static void main(String[] args) { for(int i=0;i<10;i++){ ThreadAdd target = new ThreadAdd(i); new Thread(target).start(); } } } class ThreadAdd implements Runnable{ // WriteLock lock = new WriteLock(); public static int sum; public int time; public ThreadAdd(int time) { this.time = time; } public void run() { int t_sum=0; for(int i=1;i<=10;i++) { t_sum += (time * 10)+i; } // System.out.println(t_sum); sum+=t_sum; if(time==9){ System.out.println(sum); } } }
work6
package cn.tedu.work; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantReadWriteLock; public class ThreadWork6 { /** * Three windows sell tickets at the same time */ public static void main(String[] args) { TicketThread target = new TicketThread(); Thread thread1 = new Thread(target, "1"); Thread thread2 = new Thread(target, "2"); Thread thread3 = new Thread(target, "3"); thread1.start(); thread2.start(); thread3.start(); } } class TicketThread implements Runnable { public int ticket_nub = 100; static ReentrantReadWriteLock rwl = new ReentrantReadWriteLock(); static Lock lock = rwl.writeLock(); public void run() { while (true) { lock.lock(); try { if (ticket_nub > 0) { System.out.println("Your ticket number is:" + ticket_nub + ""); System.out.println("Window: " + Thread.currentThread().getName()); Thread.sleep(100); ticket_nub--; } else if (ticket_nub <= 0) { break; } } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } } System.out.println("End thread:" + Thread.currentThread().getName()); } }
work7
package cn.tedu.work; import java.awt.*; import java.util.HashMap; import java.util.LinkedList; import java.util.Map; import java.util.Random; import java.util.concurrent.BlockingDeque; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingDeque; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class ThreadWork7 { /** * 7, bounded-buffer problem * 7.1 synchronized mode * 7.2 ReentrantLock Mode (order can be guaranteed) * 7.3 BlockingQueue mode */ public static void main(String[] args) { // new BufferShelf().synchronizedMode(); // new BufferShelf().ReentrantLockMode(); new BufferShelf().BlockingQueueMode(); } } enum Goodstype { barbecue, eggplant, Hot Pot, Deep-Fried Dough Sticks } class BufferShelf { // private static final Object L = ; public static LinkedList<Map<String, String>> goodsBuffer = new LinkedList<>(); static int bufferNub = 2; static long starttinme; public void add() { Random rand = new Random(); if (goodsBuffer.size() < bufferNub) { HashMap<String, String> map = new HashMap<String, String>(); int goods_ind = rand.nextInt(Goodstype.values().length); map.put("name", Goodstype.values()[goods_ind].toString()); goodsBuffer.push(map); System.out.println(Thread.currentThread().getName() + "production :" + goodsBuffer); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } } public void pop() { if (goodsBuffer.size() > 0) { Map<String, String> pop = goodsBuffer.pop(); // System.out.println("consumption:" + pop); System.out.println(Thread.currentThread().getName() + "Consumption:" + goodsBuffer); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } } public void push_s(){ synchronized (BufferShelf.class){ add(); } } public void pop_s(){ synchronized (BufferShelf.class){ pop(); } } public void push_l(){ lock.lock(); try { add(); } finally { lock.unlock(); } } public void pop_l(){ lock.lock(); try { pop(); } finally { lock.unlock(); } } public void run(Runnable producer_t, Runnable consumer_t) { Thread producer = new Thread(producer_t, "P1"); Thread producer2 = new Thread(producer_t, "P2"); Thread producer3 = new Thread(producer_t, "P3"); Thread consumer = new Thread(consumer_t, "C1"); Thread consumer2 = new Thread(consumer_t, "C2"); Thread consumer3 = new Thread(consumer_t, "C3"); producer.start(); producer2.start(); producer3.start(); consumer.start(); consumer2.start(); consumer3.start(); } public void synchronizedMode() { starttinme = System.currentTimeMillis(); Runnable producer_t = new Runnable() { @Override public void run() { while (true) { push_s(); if (System.currentTimeMillis() - starttinme > 10000) { break; } } } }; Runnable consumer_t = new Runnable() { @Override public void run() { while (true) { pop_s(); if (System.currentTimeMillis() - starttinme > 10000) { // System.out.println(goodsBuffer); break; } } } }; run(producer_t, consumer_t); } static Lock lock = new ReentrantLock(); public void ReentrantLockMode() { starttinme = System.currentTimeMillis(); Runnable producer_t = new Runnable() { @Override public void run() { while (true) { push_l(); if (System.currentTimeMillis() - starttinme > 2000) { // System.out.println(goodsBuffer); break; } } } }; Runnable consumer_t = new Runnable() { @Override public void run() { while (true) { pop_l(); if (System.currentTimeMillis() - starttinme > 10000) { break; } } } }; run(producer_t, consumer_t); } // static BlockingQueue<Map<String, String>> buff = new LinkedBlockingDeque<>(bufferNub); static BlockingQueue< String> buff = new LinkedBlockingDeque<>(bufferNub); /** * buff BlockingQueueMode The size of the buffer under is bufferNub * * * */ public void BlockingQueueMode() { starttinme = System.currentTimeMillis(); Runnable producer_t = new Runnable() { @Override public void run() { while (true) { Random rand = new Random(); HashMap<String, String> map = new HashMap<String, String>(); int goods_ind = rand.nextInt(Goodstype.values().length); map.put("name", Goodstype.values()[goods_ind].toString()); try { // buff.put(map); String aput=Goodstype.values()[goods_ind].toString(); buff.put(aput); boolean bl = true; // bl = buff.offer(map, 10, TimeUnit.MILLISECONDS); if (bl) { // System.out.println(Thread.currentThread().getName() + "production:" + ApUT + "buff =" + buff); System.out.println(Thread.currentThread().getName() + "production:" + aput); } Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } if (System.currentTimeMillis() - starttinme > 2000) { break; } } } }; Runnable consumer_t = new Runnable() { @Override public void run() { while (true) { try { // Map<String, String> pop = buff.take(); // Map<String, String> pop = buff.poll(10, TimeUnit.MILLISECONDS); String pop = buff.poll(10, TimeUnit.MILLISECONDS); // System.out.println(Thread.currentThread().getName() + "consumption" + pop + "buff =" + buff); System.out.println(Thread.currentThread().getName() + "consumption " + pop); Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } if (System.currentTimeMillis() - starttinme > 2000) { // System.out.println(goodsBuffer); break; } } } }; run(producer_t, consumer_t); } }
work8
package cn.tedu.work; import java.util.concurrent.locks.LockSupport; public class ThreadWork8 { static Thread thread1; static Thread thread2; /** * Print two arrays alternately * */ public static void main(String[] args) { int[] nubs=new int[10]; char[] chars=new char[10]; for(int i=0;i<10;i++){ nubs[i]=i; chars[i]= (char) (i+'a'); } thread1=new Thread( ()->{ for(char ach:chars){ System.out.printf(ach+" "); LockSupport.unpark(thread2); LockSupport.park(); } }); thread2=new Thread(()->{ for(int nub:nubs){ System.out.printf(nub+" "); LockSupport.unpark(thread1); LockSupport.park(); } }); thread1.start(); thread2.start(); } }
Wake up blocking
-
LockSupport
LockSupport.unpark(a); LockSupport.park();
-
wait()/notify Object
-
Reentrantlock Condition await/single
When calling condition The await() method will cause the thread currently obtaining the lock to enter the waiting queue. If the thread can return from the await() method, it must have obtained the lock associated with the condition.
Call the signal or signalAll method of condition to move the node with the longest waiting time in the waiting queue to the synchronization queue.
static ReentrantLock lock = new ReentrantLock(); static Condition condition = lock.newCondition(); condition.signal(); //Wake up the first to enter condition.await(); //Join the waiting queue
Implementation of lock
-
synchronized
The lock object is this object or passed in parameter object
-
ReentrantLock
The Lock object is Lock
ReentrantLock lock = new ReentrantLock(); lock.lock(); lock.unlock;
-
wirterLock /readerLock
-
Semaphore
It is usually used to limit the number of threads accessing resources at the same time. For example, if a piece of code only allows up to three threads to enter and execute, you can initialize Semaphore to 3, use acquire to subtract one each time you enter, and use release plus 1 when you exit the code to achieve the purpose of control.
semaphore refers to the number of times that a resource can enter. If the number of times that a resource can enter is 1, it can be compared with the implementation of lock. I have thought of another variant of the implementation of reentrantlock's condition, two conditions of a lock, similar to two semaphores of a resource. The code is written almost the same, so it's no longer posted here.
// s1.acquire(); Acquiring semaphores is equivalent to locking // s1.release(); Release lock private static Semaphore s1 = new Semaphore(1); private static Semaphore s2 = new Semaphore(0); // s1.acquire() / / acquire lock // s2.acquire(); // Get blocked waiting in G // s1.acquire() -> s1.run->s2.release()->s1. static class G extends Thread{ @Override public void run() { while (true){ try { s1.acquire(); //Only one thread can execute the following code System.out.println("G"); s2.release(); //You must open s1. Please wait until the G thread releases s2 Can be executed } catch (InterruptedException e) { e.printStackTrace(); } } } } static class H extends Thread{ @Override public void run() { while (true){ try { s2.acquire(); System.out.println("H"); s1.release(); } catch (InterruptedException e) { e.printStackTrace(); } } } }
-
Implementation of lock
-
Blocking
-
Inter thread synchronization
-
volatile keyword
static volatile boolean a = true;
volatile ensures visibility and order.
Visibility, the modification of variables modified by volatile can be synchronized to main memory immediately. Due to the processor cache consistency mechanism, the data in the cache of other threads will be invalidated, so as to ensure that the thread reads the latest data.
Orderliness prevents instruction reordering. Instruction reordering has no problem in a single thread, because it will not affect the final result, but the processing order is different. However, in the case of multithreading, once the instructions are reordered, some unexpected results will occur. For example, in singleton mode, the reference is exposed in advance before the singleton object is instantiated. After the variable is declared volatile, a happens before relationship will be established between reading and writing to prohibit instruction reordering by adding a memory barrier.
-
Use static variables
Theoretical lock model
- Optimistic lock / pessimistic lock
Optimistic lock: as the name suggests, it is very optimistic. Every time you go to get the data, you think others will not modify it, so you won't lock it. However, when updating, you will judge whether others have updated the data during this period. You can use mechanisms such as version number.
//How to judge the need to update data,
Pessimistic lock: always assume the worst case. Every time you go to get the data, you think others will modify it, so you lock it every time you get the data, so others will block the data until they get the lock. For example, the implementation of the synchronized keyword of the synchronization primitive in Java is pessimistic lock.
-
Exclusive lock / shared lock
An exclusive lock means that the lock can only be held by one thread at a time.
Only one
A shared lock means that the lock can be held by multiple threads.
For Java ReentrantLock, it is an exclusive Lock. However, for another implementation class of Lock, ReadWriteLock, its read Lock is a shared Lock and its write Lock is an exclusive Lock.
The shared lock of read lock can ensure that concurrent reading is very efficient. The processes of reading, writing, reading and writing are mutually exclusive.
Exclusive locks and shared locks are also realized through AQS. Exclusive locks or shared locks can be realized through different methods.
For Synchronized, of course, it is an exclusive lock.
-
Mutex / read / write lock
The exclusive lock / shared lock mentioned above is a broad term, and the mutex lock / read-write lock is a specific implementation.
The specific implementation of mutex in Java is ReentrantLock.
The specific implementation of read-write lock in Java is ReadWriteLock.
-
-
Reentrant lock
Reentrant lock, also known as recursive lock, means that when the same thread obtains a lock in the outer method, it will automatically obtain the lock when entering the inner method. It's a bit abstract. Here's a code example.
For Java reentrantlock, it can be seen from its name that it is a re-entry lock, and its name is re entry lock.
For Synchronized, it is also a reentrant lock. One advantage of reentrant locks is that deadlocks can be avoided to some extent.
1 synchronized void setA() throws Exception{ 2 Thread.sleep(1000); 3 setB(); 4 } 5 6 synchronized void setB() throws Exception{ 7 Thread.sleep(1000); 8 }
The above code is a feature of a reentrant lock. If it is not a reentrant lock, setB may not be executed by the current thread and may cause deadlock.
- Fair lock / unfair lock
Fair lock means that multiple threads acquire locks in the order in which they apply for locks.
An unfair lock means that multiple threads acquire locks in a different order from the order in which they apply for locks. It is possible that the thread that applies later acquires locks first than the thread that applies first. It may cause priority reversal or starvation.
For Java ReetrantLock, specify whether the lock is a fair lock through the constructor. The default is a non fair lock. The advantage of non fair lock is that the throughput is greater than that of fair lock.
For Synchronized, it is also an unfair lock. Unlike ReentrantLock, which implements thread scheduling through AQS, there is no way to turn it into a fair lock.
- Sectional lock
Segmented lock is actually a lock design, not a specific lock. For ConcurrentHashMap, its concurrent implementation is to realize efficient concurrent operation in the form of segmented lock.
When inserting an array, judge the inserted segment and lock that segment to realize parallel insertion
Let's use ConcurrentHashMap to explain the meaning and design idea of Segment lock. The Segment lock in ConcurrentHashMap is called Segment, which is similar to the structure of HashMap (the implementation of HashMap in JDK7 and JDK8), that is, it has an Entry array, and each element in the array is a linked list; at the same time, it is also a ReentrantLock (Segment inherits ReentrantLock).
When the put element is needed, it is not to lock the entire hashmap, but to know which segment it is to be placed in through the hashcode, and then lock this segment. Therefore, when multi-threaded put, as long as it is not placed in one segment, true parallel insertion is realized.
However, when calculating size, you need to obtain all segment locks to obtain hashmap global information.
The design purpose of segmented lock is to refine the granularity of lock. When the operation does not need to update the whole array, only one item in the array is locked.
- Bias lock / lightweight lock / heavyweight lock
These three locks refer to the state of the lock and are for Synchronized. In Java 5, the mechanism of lock upgrade is introduced to realize efficient Synchronized. The status of these three locks is indicated by the fields in the object header of the object monitor.
Biased lock means that a piece of synchronous code has been accessed by a thread, and the thread will automatically obtain the lock. Reduce the cost of obtaining locks.
Lightweight lock means that when a lock is a biased lock and is accessed by another thread, the biased lock will be upgraded to a lightweight lock. Other threads will try to obtain the lock in the form of spin without blocking and improving performance.
Heavyweight lock means that when the lock is a lightweight lock, although another thread spins, the spin will not continue all the time. When it spins a certain number of times, it will enter blocking before it has obtained the lock, and the lock will expand into a heavyweight lock. The heavyweight lock will block the thread he applies for and reduce the performance.
Java lock underlying implementation
CAS (Compare and Swap): when multiple threads try to update the same variable at the same time using CAS, only one thread can update the value of the variable, while other threads fail. The failed thread will not be suspended, but will be told that it has failed in the competition and can try again.