Multithreading and high concurrency

1. Create thread mode

  1. Inherit the Thread class, rewrite the run() method, and call the start method to start the thread

  2. Implement the Runnable interface

  3. Implement the Callable interface ( return value can be obtained )

  4. Executors.newCachedThreadPool() Created by thread pool

    Ali recommends the way to create a thread pool: new ThreadPoolExecutor()

//1.继承Thread类,重写run()方法,调用start方法开启线程
public class ThreadTest extends Thread {
    
    
    @Override
    public void run() {
    
    
        for (int i = 0; i < 2010; i++) {
    
    
            System.out.println("打豆豆");
        }
    }
    public static void main(String[] args) {
    
    
        new ThreadTest().start();
    }    
}
//2.实现Runnable接口
public class RunnableTest implements Runnable {
    
    
    @Override
    public void run() {
    
    
        for (int i = 0; i < 2010; i++) {
    
    
            System.out.println("打豆豆");
        }
    }
    public static void main(String[] args) {
    
    
        new Thread( new RunnableTest()).start();
        //也可用lambda表达式
        new Thread(()->{
    
    System.out.println("打豆豆");}).start();
    }
}
//3.实现Callable接口
public class CallableTest implements Callable<Boolean> {
    
    
    @Override
    public Boolean call() {
    
    
        System.out.println("11111");
        return true;
    }

    public static void main(String[] args) {
    
    
        CallableTest p1 = new CallableTest();       

        //1.创建线程池
        ExecutorService serve = Executors.newFixedThreadPool(3);
        //阿里推荐创建线程池的方式
        threadPool = new ThreadPoolExecutor(8, 16, 60, TimeUnit.SECONDS,
                            new LinkedBlockingQueue<>(32), new ThreadPoolExecutor.CallerRunsPolicy());

        //2.提交执行
        Future<Boolean> r1 = serve.submit(p1);
        //3.获取结果
        Boolean res1 = r1.get();
        //4.关闭服务
        serve.shutdownNow();
    }
}

Note: Create thread call method

1.new T1().run();     仅是执行run()方法,并不开启一个线程
2.new T1().start()	  开启一个线程,执行run()方法

2. Thread method

  1. Thread sleep : Thread.sleep(1000)
    1. Thread.sleep(0) : Trigger a cpu competition to prevent a thread from occupying cpu resources for a long time in the cpu preemptive algorithm
  2. Thread politeness : Thread.yeild() re-enters the waiting queue (give up the first-line cpu, return to the ready state, and also participate in the competition of cpu)
  3. Thread jumping : t1.join() : When t2 (other threads) needs to be executed, t1 jumps in the queue, and t2 (other threads) must wait for t1 to finish executing
  4. Get thread state : t1.getState();

Ready and Running combined into a Runnable state

insert image description here

insert image description here

3.Synchronized keyword (implicit definition, automatic release out of scope)

1. Synchronized code block: (the object of the default lock is Obj) , the lock will be released after the code block is executed

synchronized(Obj){
    
    //Obj称为同步监视器,Obj可以锁任意对象,但是推荐使用共享资源作为同步监视器
    ...
}

2. Synchronization method (the default lock is the object of the method)

public synchronized void method(int args){
    
    
    ...
}   

The synchronized method controls the access to the "object". Each object corresponds to a lock. Each synchronized method must obtain the lock of the object calling the method before it can be executed. Otherwise, the thread will be blocked. Once the method is executed, it will monopolize the lock until the Only when the method returns can the lock be released, and the thread that is blocked by i can acquire the lock and continue to execute.
Defect: If a large method is declared as synchronized, it will affect the efficiency.
The synchronization monitor of the synchronization method is this is the object itself, or the class (reflection)

Notice:

1. In the same class: synchronized(this ){…} and public synchronized void method(){…} lock the same object, and the two expressions are equivalent

class T{
    
    
	//方法1和2表达的效果一样
    public void method1(){
    
    
        synchronized(this){
    
    
            System.out.println("000");
        }
    }
    public synchronized void method2(){
    
    
        System.out.println("000");
    }
    
    //synchronized 静态方法  锁的是class对象
    public synchronized static void method(){
    
    
    	...
    }
}

2. public synchronized static void method(){…} locks T.class

3. **Dirty read: write method is locked, read method is not locked, (read unmodified value) **Solution: lock at the same time, (but low efficiency)

4.synchronized: Guaranteed visibility, consistency, and reentrant locks :

5. Reentrant lock : A reentrant lock in a broad sense refers to a lock that can be called repeatedly and recursively. After the lock is used in the outer layer, the inner layer can still be used without deadlock. A thread has already acquired a lock and can acquire the lock again without deadlock

6. When the program generates an exception, the lock will be released automatically

Synchronized lock upgrade

4 states of the lock: no lock state, biased lock state, lightweight lock state, heavyweight lock state (levels from low to high)

1. Biased lock : MarkWord records the thread ID, and judges whether the threadID of other threads is consistent with the threadID in the Java object header . If they are consistent (thread 1 still acquires the lock object), there is no need to use CAS to lock and unlock; if they are inconsistent (other Threads, such as thread 2 wants to compete for the lock object, but the biased lock will not be actively released (so it is still the stored threadID of thread 1), then you need to check whether the thread 1 recorded in the Java object header is alive, if not, then the lock object will be reset Set it to a lock-free state, and other threads (thread 2) can compete to set it as a biased lock; if it survives, then immediately look up the stack frame information of the thread (thread 1), if you still need to continue to hold the lock object, then pause The current thread 1 revokes the biased lock and upgrades it to a lightweight lock. If thread 1 no longer uses the lock object, set the state of the lock object to the lock-free state and re-bias the new thread. ( Multiple threads compete, upgrade to spin lock , spin consumes cpu resources)
2. Lightweight lock

Upgrade to a heavyweight lock when the number of spins exceeds (10/100 times).

3. Heavyweight lock (apply to the operating system, enter the waiting queue,)

***Note:** In order to avoid useless spins, once a lightweight lock expands to a heavyweight lock, it will not be downgraded to a lightweight lock; a biased lock cannot be downgraded to a biased lock when it is upgraded to a lightweight lock. Lock. In a word, the lock can be upgraded but not downgraded , but the biased lock state can be reset to the lock-free state.

insert image description here

Summarize:

  1. Locking code execution time is short or the number of threads is small, use lightweight locks
  2. The execution time of locking code is long or the number of threads is large, use heavyweight lock

synchronized(Object)

​The object of the lock

4. The volatile keyword

Volatile is a lightweight synchronization mechanism provided by Java. The Java language contains two inherent synchronization mechanisms: synchronization blocks (or methods) and volatile variables. Compared with synchronized (synchronized is usually called a heavyweight lock), volatile is lighter because it does not cause thread context switching and scheduling. But volatile variables are less synchronized (sometimes simpler and less expensive), and their use is more error-prone.

1. Guaranteed visibility (through the MESI cache consistency protocol)

​ When writing a volatile variable, JMM will force the variable in the thread's local memory to be refreshed to the main memory . This write operation will invalidate the volatile variable cache in other threads.

​Invisible between threads : One thread modifies the shared variables that are copied to the working memory of its own thread, and the other thread cannot see the changes of the shared variables

2. Prohibition of instruction reordering (read barrier, write barrier, primitive operation)
volatile can guarantee thread visibility and provide a certain order, but cannot guarantee atomicity . At the bottom of the JVM, volatile is implemented using a " memory barrier ". Observing the assembly code generated when the volatile keyword is added and when the volatile keyword is not added, it is found that when the volatile keyword is added, there will be an extra lock prefix instruction, and the lock prefix instruction is actually equivalent to a memory barrier (also known as a memory fence). The memory barrier will provide 3 functions:
1. It ensures that when the instruction is reordered, it will not arrange the following instructions to the position before the memory barrier, nor will it arrange the previous instructions to the back of the memory barrier; When the instruction of memory barrier is executed, all operations before it have been completed;

2. It will force the modification of the cache to be written to the main memory immediately;

3. If it is a write operation, it will invalidate the corresponding cache line in other CPUs.

3. Atomicity is not guaranteed

//1.双重检测实现单例
public class SingleInstance {
    
    

    //加volatile 禁止指令重排,因为new SingleInstance() 并不是一个原子性的操作(1.分配内存(默认值) -> 2.为成员变量赋值 ->3.将值指向内存)
    //在超高并发的情况下:第一个线程进行分配内存(此时对象已不为空),第二个线程判断该实例不为空,进行取值时取的是初始化的默认值,并不是第二步的真实值
    public static volatile SingleInstance singleInstance;

    private SingleInstance() {
    
    
    }

    public static SingleInstance getInstance(){
    
    
        if(singleInstance==null){
    
    
            synchronized (SingleInstance.class){
    
    
                if(singleInstance==null){
    
    
                    singleInstance=new SingleInstance();
                }
            }
        }
        return singleInstance;
    }

    public static void main(String[] args) {
    
    
        for (int i = 0; i < 20; i++) {
    
    
            new Thread(()->{
    
    
                System.out.println(SingleInstance.getInstance());
            }).start();
        }
    }
}
//2.通过枚举实现单例
class Resource{
    
    

}
public enum SingleInstanceEnum {
    
    

    INSTANCE;
    
    private Resource resource;
    
    SingleInstanceEnum(){
    
    
        resource=new Resource();
    }
    
    public Resource getInstance(){
    
    
        return resource;
    }

    public static void main(String[] args) {
    
    
        for (int i = 0; i < 20; i++) {
    
    
            new Thread(()->{
    
    
                System.out.println(SingleInstanceEnum.INSTANCE.getInstance());
            }).start();
        }
    }
}

5. CAS (compare and set/swap) lock-free optimization, spin

CPU primitive support: not interruptible

CAS (Expected, NewValue) cycle: When the current value is just the expected value (Expected) (that is, no other thread comes in to change the value), the value is changed to the new value (NewValue), if it is not the expected value, it means that a thread comes in to change the value value, you need to re-read the current value and reset the expected value

  • Expected : expected value
  • NewValue : the value to change to

ABA problem : cas will cause ABA problem (a thread modifies the original value A to B value, and then modifies the B value to A value, but the other thread does not know that he has changed)

Primitive Data Types: Does not affect

Citation Type: Influential (Popular interpretation is that your girlfriend reunited with you, and you experienced n men among your girlfriends)

Solution: add a version number vision , timestamp

Unsafe class : direct manipulation of jvm memory ( similar to c++ pointers )

All atomic classes (such as: AtomicInteger) are operated by compareAndSwap in the unsafe class

Highly concurrent operation scheme of the same number:

  1. long type lock
  2. atomicLong class
  3. LongAdder (advantageous when there are many threads, segment lock)

6. lock (manually open and close)

Starting from jdk1.5
, using Lock locks, jvm will spend less time scheduling threads, have better performance , and have good scalability (provide more subclasses)

1. The java.util.concurrent.locks.Lock interface is a tool for controlling multiple threads to access shared resources. The lock provides exclusive access to shared resources. Only one thread can lock the Lock object at a time, and the thread starts A lock should be acquired before accessing a shared resource

Reentrant lock: when the lock object is the same, there is no need to acquire the lock again

2. **ReentrantLock (reentrant lock)** class implements Lock, which has the same concurrency and memory semantics as synchronized. In implementing thread-safe control, ReentrantLock is commonly used, which can be explicitly locked and released.

Use priority: lock>synchronized code block>synchronized method

Advantages of lock:

  1. lock.tryLock(5,TimeUnit.SECONDS) try to acquire the lock within 5s
// lock的使用方式
private final ReentrantLock lock =new ReentrantLock(); //参数可指定公平/非公平锁,默认为非公平锁
try{
    
    
    lock.lock();
    ........
}finally {
    
    
    lock.unlock();
}

JUC helper classes

1. CountDownLatch : The subtraction counter is used to wait for several threads to end, wake up when countDown is 0

import java.util.concurrent.CountDownLatch;

//闭锁,用于等待时间
//1.总数为6的计数器
public class CountDownLatchTest {
    
    
    public static void main(String[] args) {
    
    
        //1.创建一个总数为6的计数器
        CountDownLatch countDownLatch = new CountDownLatch(6);

        for (int i = 0; i < 6; i++) {
    
    
            new Thread(()->{
    
    
                System.out.println(Thread.currentThread().getName()+"走了");
                //2.计数器减一
                countDownLatch.countDown();
            },String.valueOf(i)).start();
        }

        try {
    
    
            //等待计数器归零时,唤醒countDownLatch.await(),继续向下执行
            countDownLatch.await();
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        }
        System.out.println("结束");
    }
}

2.CyclicBarrier addition counter (wait for the number of threads to be full and call the specified action (BarrierAction), if not satisfied, wait)

import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;

//栅栏:用于等待其他线程(全部完成才会向下执行)
//2.加法计数器

public class CyclicBarrierTest {
    
    
    public static void main(String[] args) {
    
    

        //创建总数为7个的计数器
        CyclicBarrier cyclicBarrier = new CyclicBarrier(7,()->{
    
    
            System.out.println("成功!!!");
        });
        for (int i = 0; i < 7; i++) {
    
    
            //
            final int temp=i;
            new Thread(()->{
    
    
                System.out.println(Thread.currentThread().getName()+"现在是"+temp);
                try {
    
    
                    //等待集齐7个线程时才会执行下一步
                    cyclicBarrier.await();

                } catch (InterruptedException e) {
    
    
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
    
    
                    e.printStackTrace();
                }
            }).start();
        }

    }
}
3.Semaphore semaphore current limit (limit the maximum number of multiple threads that can only run)

​ Example: 4 lanes (multiple threads enter), 2 toll exits (only two threads are allowed to run at most)

import java.util.concurrent.Semaphore;

//3.信号量
//semaphore.acquire()  获得,如果已经满了,将会阻塞等待
//semaphore.release()  释放,将当前信号量+1,同时唤醒等待的线程
//作用,多个共享资源互斥的使用,并发限流,控制最大的线程数

public class SemaphoreTest {
    
    
    public static void main(String[] args) {
    
    
        //参数为线程数量,限流
        Semaphore semaphore = new Semaphore(3);

        for (int i = 0; i < 6; i++) {
    
    
            new Thread(()->{
    
    
                try {
    
    
                    //1.获取(Semaphore--),semaphore为0时阻塞
                    semaphore.acquire();
                    System.out.println(Thread.currentThread().getName()+"抢到了车位");
                    //2.释放(Semaphore++)
                    semaphore.release();
                    System.out.println(Thread.currentThread().getName()+"离开了车位");
                } catch (InterruptedException e) {
    
    
                    e.printStackTrace();
                }
            },String.valueOf(i)).start();
        }

    }
}
4.Phaser: stage operation, after reaching a stage, it will continue to execute

Registration mechanism : Unlike other barriers, the "registered synchronizers (parties)" in Phaser will change over time. Phaser can initialize the number of parties through the constructor, or add (register) new ones at any time during Phaser operation. parties, and deregister parties during runtime. You can join and log out of parties at any time during runtime.

package com.example.demo.util;

import java.util.concurrent.Phaser;

public class PhaserTest {
    
    

	public static void main(String[] args) {
    
    
		WorkPhaser workPhaser = new WorkPhaser();
		// 4个工人参与工作
		for (int i = 0; i < 4; i++) {
    
    
			// 注册
			workPhaser.register();
			// 启动线程
			new Thread(new Worker(workPhaser), "work-" + (i + 1)).start();
		}
	}
}

/**
 * 工作阶段器
 */
class WorkPhaser extends Phaser {
    
    

	//所有线程满足条件时,自动调用 onAdvance()方法
	@Override
	protected boolean onAdvance(int phase, int registeredParties) {
    
    
		switch (phase) {
    
    
		case 0:
			System.out.println("第一阶段工作完成,进入第二阶段>>>");
			return false;
		case 1:
			System.out.println("第二阶段工作完成,进入第三阶段>>>");
			return false;
		case 2:
			System.out.println("第三阶段工作完成,退出.");
			return true;
		default:
			return true;
		}
	}
}

/**
 * 工人
 */
class Worker implements Runnable {
    
    

	private WorkPhaser workPhaser;

	public Worker(WorkPhaser workPhaser) {
    
    
		this.workPhaser = workPhaser;
	}

	@Override
	public void run() {
    
    
		String playerName = Thread.currentThread().getName();
		System.out.println(playerName + " 工人完成了第一阶段的工作.");
		// 到达阶段,等待进入下一阶段
		workPhaser.arriveAndAwaitAdvance();

		System.out.println(playerName + " 工人完成了第二阶段的工作.");
		// 到达阶段,等待进入下一阶段
		workPhaser.arriveAndAwaitAdvance();

		System.out.println(playerName + " 工人完成了第三阶段的工作.");
		// 到达阶段,等待进入下一阶段
		workPhaser.arriveAndAwaitAdvance();
	}

}

//   workPhaser.arriveAndDeregister()     //不用等,自己注销

5. ReadWriteLock read-write lock

Mutually exclusive when reading and writing, a shared lock when reading alone (read-only without locking will cause dirty reads), and an exclusive lock when writing alone

package com.it.juc.readWriteLock;

import javax.swing.plaf.IconUIResource;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

public class ReadWriteLockTest {
    
    
    public static void main(String[] args) {
    
    
        MyCacheLock myCacheLock = new MyCacheLock();

        for (int i = 0; i < 5; i++) {
    
    
            final int temp =i;
            new Thread(()->{
    
    
                myCacheLock.put(temp+"",temp);
            },"A").start();
        }
        for (int i = 0; i < 5; i++) {
    
    
            final int temp =i;
            new Thread(()->{
    
    
                myCacheLock.get(temp+"");
            },"B").start();
        }
    }
}
class MyCacheLock{
    
    
    private volatile Map<String,Object> map=new HashMap<>();

    //读写锁,更加细粒度的控制
    private ReadWriteLock readWriteLock=new ReentrantReadWriteLock();

    public void put(String key,Object value){
    
    
        readWriteLock.writeLock().lock();
        try {
    
    
            System.out.println(Thread.currentThread().getName()+"写入"+key);
            map.put(key,value);
            System.out.println(Thread.currentThread().getName()+"写入完成");
        } catch (Exception e) {
    
    
            e.printStackTrace();
        } finally {
    
    
            readWriteLock.writeLock().unlock();
        }
    }
    public void get(String key){
    
    
        readWriteLock.readLock().lock();
        try {
    
    
            System.out.println(Thread.currentThread().getName()+"读取"+key);
            map.get(key);
            System.out.println(Thread.currentThread().getName()+"读取完成");
        } catch (Exception e) {
    
    
            e.printStackTrace();
        } finally {
    
    
            readWriteLock.readLock().unlock();
        }
    }
}
6.exchange

To exchange information between two threads, the first thread needs to wait for the entry of the second thread before exchanging, the waiting process is blocked, and the execution continues after the exchange. (Scene: Exchanging equipment)

package com.securitit.serialize.juc;

import java.util.concurrent.Exchanger;

public class ExchangerTester {
    
    

	// Exchanger实例.
	private static final Exchanger<String> exchanger = new Exchanger<String>();

	public static void main(String[] args) {
    
    
		// 模拟阻塞线程.
		new Thread(() -> {
    
    
			try {
    
    
				String wares = "红烧肉";
				System.out.println(Thread.currentThread().getName() + "商品方正在等待金钱方,使用货物兑换为金钱.");
				Thread.sleep(2000);
				String money = exchanger.exchange(wares);
				System.out.println(Thread.currentThread().getName() + "商品方使用商品兑换了" + money);
			} catch (InterruptedException ex) {
    
    
				ex.printStackTrace();
			}
		}).start();
		// 模拟阻塞线程.
		new Thread(() -> {
    
    
			try {
    
    
				String money = "人民币";
				System.out.println(Thread.currentThread().getName() + "金钱方正在等待商品方,使用金钱购买食物.");
				Thread.sleep(4000);
				String wares = exchanger.exchange(money);
				System.out.println(Thread.currentThread().getName() + "金钱方使用金钱购买了" + wares);
			} catch (InterruptedException ex) {
    
    
				ex.printStackTrace();
			}
		}).start();
	}

}

transient keyword

​transient declares an instance variable whose value does not need to be maintained when the object is stored . In other words, member variables marked with the transient keyword do not participate in the serialization process.

Role:
Java's serialization provides a mechanism for persistent object instances. When persisting objects, there may be a special object data member that we don't want to use the serialization mechanism to save it. To turn off serialization on a field of a particular object, precede the field with the keyword transient. When an object is serialized, values ​​of transient variables are not included in the serialized representation, whereas non-transient variables are.

7. deadlock

Multiple threads each occupy limited resources and wait for each other to run on the resources occupied by other threads, resulting in two or more threads waiting for each other to release resources and stopping execution. A synchronization code block has "more than two "Deadlock" may occur when

4 necessary conditions for deadlock :

1. Mutually exclusive conditions : a resource can only be used by one process at a time

2. Request and hold conditions : When a process is blocked due to requesting resources, it will not let go of the obtained resources

3. Non-deprivation conditions : The resources obtained by the process pair cannot be forcibly deprived before they are used up.

4. Circular waiting condition : A head-to-tail intersecting circular waiting resource relationship is formed between several processes

8. Thread communication:

insert image description here

9. Thread pool:

//线程池
public class ThreadPool {
    
    
    public static void main(String[] args) {
    
    
        //1.创建服务,创建线程池
        ExecutorService service = Executors.newFixedThreadPool(5);
        //2.执行
        service.execute(new MyThread());
        service.execute(new MyThread());
        service.execute(new MyThread());
        service.execute(new MyThread());
        service.execute(new MyThread());
        service.execute(new MyThread());
        //3.关闭连接
        service.shutdownNow();
    }
}
class MyThread implements Runnable{
    
    
    @Override
    public void run() {
    
    
        System.out.println(Thread.currentThread().getName());
    }
}

10. Producer consumer model:

package com.it.MulThread.produceConsumer;

//1.管程法: 利用缓冲区解决

public class PC {
    
    
    public static void main(String[] args) {
    
    
        SynContainer synContainer = new SynContainer();

        new Producer(synContainer).start();
        new Consumer(synContainer).start();
    }
}

class Chicken{
    
    
    int id;

    public Chicken(int id) {
    
    
        this.id = id;
    }
}

class Producer extends Thread{
    
    

    SynContainer synContainer;

    public Producer(SynContainer synContainer){
    
    
        this.synContainer=synContainer;
    }

    //生产
    @Override
    public void run() {
    
    
        for (int i = 0; i < 100; i++) {
    
    
            synContainer.push(new Chicken(i));
        }
    }
}

class Consumer extends Thread{
    
    

    SynContainer synContainer;

    public Consumer(SynContainer synContainer){
    
    
        this.synContainer=synContainer;
    }

    //消费
    @Override
    public void run() {
    
    
        for (int i = 0; i < 100; i++) {
    
    
            synContainer.pop();
        }
    }
}

class SynContainer{
    
    
    //容器
    Chicken[] chickens =new Chicken[10];
    //容器计数器
    int count=0;

    //生产者放入产品
    public synchronized void push(Chicken chicken){
    
    
        //如果日期满了
        if(count==chickens.length){
    
    
            try {
    
    
                //生产者等待
                this.wait();
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            }
        }
        //没有存满,就生产进容器
        chickens[count++]=chicken;
        System.out.println("生产了"+chicken.id+"只级");

        //通知消费者消费
        this.notifyAll();
    }

    //消费者消费产品
    public synchronized Chicken pop(){
    
    
        //判断能否消费
        if(count==0){
    
    
            //等待生产者生产
            try {
    
    
                this.wait();
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            }
        }
        //如果可以消费
        count--;
        Chicken chicken = chickens[count];
        System.out.println("消费了第"+chicken.id+"只级");

        //吃完了,通知生产者生产
        this.notifyAll();
        return chicken;
    }
}

Question: Why is ArrayList thread unsafe?

Let me first talk about what thread insecurity is: thread safety means that when multi-threaded access, a locking mechanism is adopted. When a thread accesses a certain data of this type, it is protected, and other threads cannot access it until the thread has finished reading. Only other threads can use it. There is no data inconsistency or data pollution. Thread insecurity means that it does not provide data access protection. It is possible that multiple threads may change data successively, resulting in dirty data. As shown in the figure, there are two implementations under the List interface, one is ArrayList and the other is vector. From the source code point of view, because the Vector method is preceded by the synchronized keyword, which means synchronization, Sun hopes that Vector is thread-safe and ArrayList is efficient. The disadvantage is another advantage. Let's talk about the principle (Baidu's, easy to understand): an ArrayList, when adding an element, it may have two steps to complete:

  1. Store this element at the position of Items[Size];
  2. Increase the value of Size.

In the case of single-threaded operation, if Size = 0, after adding an element, the element is at position 0, and Size=1;
and if it is in a multi-threaded situation, such as two threads, thread A first stores the element at position 0. But at this time, the CPU schedules thread A to suspend, and thread B gets a chance to run. Thread B also adds elements to this ArrayList, because Size is still equal to 0 at this time (note, we assume that adding an element takes two steps, and thread A only completes step 1), so thread B also adds elements Stored at location 0. Then both thread A and thread B continue to run, increasing the value of Size.
Well, now let's take a look at the situation of ArrayList. There is actually only one element, which is stored at position 0, but Size is equal to 2. This is "thread unsafe".

Thread-safe list : CopyOnWriteArrayList

Guess you like

Origin blog.csdn.net/The_xiaoke/article/details/124235213