Java concurrent programming JUC (2) collection lock

Thread safety of an ArrayList collection

Introducing thread unsafety issues

package new_course.chp3.list_demo;

import java.util.ArrayList;
import java.util.UUID;

/**
 * @author Created by Lin Weihong
 * @date on 2022/6/1 21:33
 * List集合线程不安全
 */
public class ErrorList {
    
    
    public static void main(String[] args) {
    
    
        //创建ArrayList集合
        List<String> list = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
    
    
            new Thread(() -> {
    
    
                //往集合添加内容
                list.add(UUID.randomUUID().toString().substring(0, 8));
                //从集合取出内容
                System.out.println(list);
            }, String.valueOf(i)).start();
        }
    }
}
//结果:
java.util.ConcurrentModificationException
程序会报错,修改异常

Solution Vector Collections collection

package new_course.chp3.list_demo;

import java.util.*;

/**
 * @author Created by Lin Weihong
 * @date on 2022/6/1 21:33
 * List集合线程不安全
 */
public class ErrorList {
    
    
    public static void main(String[] args) {
    
    
        
        //Vector解决
//        List<String> list = new Vector<>();

        //Collections解决
        List<String> list = Collections.synchronizedList(new ArrayList<>());
        

        for (int i = 0; i < 10; i++) {
    
    
            new Thread(() -> {
    
    
                //往集合添加内容
                list.add(UUID.randomUUID().toString().substring(0, 8));
                //从集合取出内容
                System.out.println(list);
            }, String.valueOf(i)).start();
        }
    }
}

Workaround CopyOnWriteArrayList

package new_course.chp3.list_demo;

import java.util.List;
import java.util.UUID;
import java.util.concurrent.CopyOnWriteArrayList;

/**
 * @author Created by Lin Weihong
 * @date on 2022/6/1 22:38
 */
public class Solution2 {
    
    
    public static void main(String[] args) {
    
    
        List<String> list = new CopyOnWriteArrayList<>();
        for (int i = 0; i < 10; i++) {
    
    
            new Thread(() -> {
    
    
                //存入集合
                list.add(UUID.randomUUID().toString().substring(0,8));
                //从集合当中取出
                System.out.println(list);
            },String.valueOf(i)).start();
        }
    }
}

CopyOnWriteArrayList principle

  1. Supports concurrent read operations
  2. When writing, first copy the current container, and then perform the write operation on the new copy
  3. After the end, point the reference of the original container to the new container
    insert image description here
//CopyOnWriteArrayList 的add 源码  

  public boolean add(E e) {
    
    
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
    
    
            Object[] elements = getArray(); //原始数组
            int len = elements.length;
            Object[] newElements = Arrays.copyOf(elements, len + 1);//复制得到的新数组
            newElements[len] = e;
            setArray(newElements);//调用下面的setArray方法
            return true;
        } finally {
    
    
            lock.unlock();
        }
    }


final void setArray(Object[] a) {
    
    
        array = a;//将原本的引用指向新的数组
    }

Thread safety of two HashSet collections

Introducing thread unsafety issues

package new_course.chp3.hashset_demo;

import java.util.HashSet;
import java.util.Set;
import java.util.UUID;

/**
 * @author Created by Lin Weihong
 * @date on 2022/6/2 0:21
 * 演示HashSet线程不安全
 */
public class ErrorHashSet {
    
    
    public static void main(String[] args) {
    
    
        Set<String> set = new HashSet<>();
        for (int i = 0; i < 30; i++) {
    
    
            new Thread(() -> {
    
    
                //放入set集合
                set.add(UUID.randomUUID().toString().substring(0,8));
                //从set集合取出
                System.out.println(set);
            },String.valueOf(i)).start();
        }
    }
}
//程序报错

Workaround CopyOnWriteArraySet

/**
 * @author Created by Lin Weihong
 * @date on 2022/6/2 0:21
 * 演示HashSet线程不安全
 */
public class Solution1{
    
    
    public static void main(String[] args) {
    
    

        Set<String> set = new CopyOnWriteArraySet<>();
        for (int i = 0; i < 30; i++) {
    
    
            new Thread(() -> {
    
    
                //放入set集合
                set.add(UUID.randomUUID().toString().substring(0,8));
                //从set集合取出
                System.out.println(set);
            },String.valueOf(i)).start();
        }
    }
}

Thread safety of three HashMap collections

Introducing thread unsafety issues


package new_course.chp3.hashmap_demo;

import java.util.Map;
import java.util.UUID;
import java.util.HashMap;

/**
 * @author Created by Lin Weihong
 * @date on 2022/6/2 0:27
 * 演示HashMap线程不安全问题
 */
public class ErrorHashMap {
    
    
    public static void main(String[] args) {
    
    
        Map<String, String> map = new HashMap<>();
        for (int i = 0; i < 10; i++) {
    
    
            String key = String.valueOf(i);
            new Thread(() -> {
    
    
                //放入集合
                map.put(String.valueOf(key),UUID.randomUUID().toString().substring(0,8));
                //取出集合
                System.out.println(map);
            },String.valueOf(i)).start();
        }
    }
}
//程序报错

Solution ConcurrentHashMap

package new_course.chp3.hashmap_demo;

import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author Created by Lin Weihong
 * @date on 2022/6/2 0:36
 */
public class Solution1 {
    
    

    public static void main(String[] args) {
    
    
        Map<String, String> map = new ConcurrentHashMap<>();
        for (int i = 0; i < 50; i++) {
    
    
            String key = String.valueOf(i);
            new Thread(() -> {
    
    
                //放入集合
                map.put(String.valueOf(key), UUID.randomUUID().toString().substring(0, 8));
                //取出集合
                System.out.println(map);
            }, String.valueOf(i)).start();
        }
    }

}

Four multithreaded locks

eight-lock phenomenon

package new_course.chp3;

import java.util.concurrent.TimeUnit;

/**
 * @author Created by Lin Weihong
 * @date on 2022/6/2 0:47
 */
class Phone {
    
    

    public synchronized void sendSMS() {
    
    
        //停留4秒
        try {
    
    
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        }
        System.out.println("------sendSMS");
    }

    public static synchronized void sendSMS1() {
    
    
        //停留4秒
        try {
    
    
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        }
        System.out.println("------sendSMS1");
    }

    public synchronized void sendEmail() {
    
    

        System.out.println("------sendEmail");
    }

    public static synchronized void sendEmail1() {
    
    

        System.out.println("------sendEmail1");
    }

    public void getHello() {
    
    

        System.out.println("------getHello");
    }
}


public class Lock8 {
    
    
    public static void main(String[] args) throws InterruptedException {
    
    
        Phone phone1 = new Phone();
        Phone phone2 = new Phone();
        new Thread(() -> {
    
    
//            phone1.sendSMS();
            phone1.sendSMS1();
        }, "线程1").start();
        Thread.sleep(100);
        new Thread(() -> {
    
    
//            phone1.getHello();
            phone2.sendEmail();
//            phone1.sendEmail();
//            phone2.sendEmail1();
        }, "线程2").start();
    }
}


/**
 * @Description 8锁
 * <p>
 * 1、标准访问,先打印短信还是邮件
 * ------sendSMS
 * ------sendEmail
 * <p>
 * 2、停4秒在短信方法内,先打印短信还是邮件
 * ------sendSMS
 * ------sendEmail
 * -------------------------synchronized锁的是当前对象this,所以上面两个都是一样的结果----------------------
 * <p>
 * 3、新增普通的hello方法,先打印短信(4s)还是hello
 * ------getHello
 * ------sendSMS
 * -------------------------sendSMS被锁住了,但是普通方法没有被锁----------------------
 * <p>
 * 4、现在有两部手机,先打印短信(4s)还是邮件
 * ------sendEmail
 * ------sendSMS
 * -------------------------synchronized锁的是当前对象this,两部手机分别为两个不同对象,所以被锁的是phone1,但是phone2没有被锁----------------------
 * 5、两个静态同步方法,1部手机,先打印短信还是邮件
 * ------sendSMS1
 * ------sendEmail1
 *
 * 6、两件静态同步方法,2部手机,先打印短信还是邮件
 * ------sendSMS1
 * ------sendEmail1
 * -------------------------以上两个方法,static+synchronized,锁的是类对象Class--------------
 * 7、1个静态同步方法,1个普通同步方法,1部手机,先打印短信还是邮件
 * ------sendEmail
 * ------sendSMS1
 * <p>
 * 8、1个静态同步方法,1个普通同步方法,2部手机,先打印短信还是邮件
 * ------sendEmail
 * ------sendSMS1
 * 
 */

insert image description here

Fair locks and unfair locks

For the previous ticket selling problem, suppose that there are four threads selling tickets (AA, BB, CC, DD) together at this time. If it is an unfair lock (default), it may happen that one thread sells all the tickets, while other threads do not. The situation of ticket sales. The fair lock means that every resource can have the opportunity to sell tickets together, reducing the occurrence ofDrought, drought, flood, floodscene.

How to declare fair locks and unfair locks

//公平锁
private final ReentrantLock lock = new ReentrantLock(true);
//非公平锁
private final ReentrantLock lock = new ReentrantLock(true);

Unfair lock: high efficiency, thread starvation

Fair lock: low efficiency, everyone has a share

Reentrant lock (recursive lock)

re-entrant lock

synchronized (implicit lock)

package new_course.chp3;

/**
 * @author Created by Lin Weihong
 * @date on 2022/6/2 11:21
 * 可重入锁
 */
public class SyncLockDemo {
    
    
    public synchronized void test() {
    
    
        test(); //Exception in thread "main" java.lang.StackOverflowError,如果不是可重入锁,就不会栈溢出,因为被锁住了
    }
    public static void main(String[] args) {
    
    
        SyncLockDemo syncLockDemo = new SyncLockDemo();
        syncLockDemo.test();
//        Object o = new Object();
//        new Thread(() -> {
    
    
//            synchronized (o) {
    
    
//                System.out.println(Thread.currentThread().getName() + "外层");
//                synchronized (o) {
    
    
//                    System.out.println(Thread.currentThread().getName() + "中层");
//                    synchronized (o) {
    
    
//                        System.out.println(Thread.currentThread().getName() + "内层");
//                        synchronized (o) {
    
    
//                            System.out.println(Thread.currentThread().getName() + "底层");
//                        }
//                    }
//                }
//            }
//        }, "aa").start();
        //结果
        aa外层
        aa中层
        aa内层
        aa底层
    }
}

Lock (display)

package new_course.chp3;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @author Created by Lin Weihong
 * @date on 2022/6/2 11:21
 * 可重入锁
 */
public class SyncLockDemo02 {
    
    

    public static void main(String[] args) {
    
    
        //Lock演示可重入锁
        Lock lock = new ReentrantLock();
        new Thread(() -> {
    
    
            lock.lock();
            try {
    
    
                System.out.println(Thread.currentThread().getName() + "外层");

                new Thread(() -> {
    
    
                    lock.lock();
                    try {
    
    
                        System.out.println(Thread.currentThread().getName() + "外层");

                    } catch (Exception e) {
    
    
                        e.printStackTrace();
                    } finally {
    
    
                        lock.unlock();
                    }
                },"a").start();
            } catch (Exception e) {
    
    
                e.printStackTrace();
            } finally {
    
    
                 lock.unlock();
            }
        },"a").start();
    }
}

deadlock

The concept of deadlock

insert image description here

The cause of the deadlock

  1. Insufficient system resources
  2. The progress sequence of process running is inappropriate
  3. Misallocation of resources

Introducing a deadlock scenario

package new_course.chp4;

import java.util.concurrent.TimeUnit;

/**
 * @author Created by Lin Weihong
 * @date on 2022/6/2 13:02
 * 展示死锁
 */
public class DeadLock {
    
    
    //创建两个对象
    static Object a = new Object();
    static Object b = new Object();

    public static void main(String[] args) {
    
    
        new Thread(() -> {
    
    
            synchronized (a) {
    
    
                System.out.println(Thread.currentThread().getName() + "持有锁a,试图获取锁b");
                try {
    
    
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
    
    
                    e.printStackTrace();
                }

                synchronized (b) {
    
    
                    System.out.println(Thread.currentThread().getName() + "获取锁b");
                }
            }
        },"A").start();

        new Thread(() -> {
    
    
            synchronized (b) {
    
    
                System.out.println(Thread.currentThread().getName() + "持有锁b,试图获取锁a");
                try {
    
    
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
    
    
                    e.printStackTrace();
                }

                synchronized (a) {
    
    
                    System.out.println(Thread.currentThread().getName() + "获取锁a");
                }
            }
        },"B").start();
    }
}

Verify if it is a deadlock

  • jps is similar to linux's ps -ef to display java processes

insert image description here

  • jstack comes with jvm, can directly view stack information, stack trace tool

insert image description here

Guess you like

Origin blog.csdn.net/weixin_48244108/article/details/125342557