锁的现象

102 阅读3分钟

「这是我参与11月更文挑战的第17天,活动详情查看:2021最后一次更文挑战

静态同步方法锁的是当前类

非静态同步方法锁的是当前对象

8锁的本质是要判断锁的对象是什么

synchronized锁的不只是一个方法,而是锁的是这个方法所在的对象,同一时间只有一个线程进入

image-20210417153012902.png

image-20210417153030589.png

image-20210417152827497.png

package com.lemon;
​
import java.util.concurrent.TimeUnit;
​
public class Lesson4_8Lock {
    public static void main(String[] args) throws InterruptedException {
        //后面的判断都是建立了在 Thread.sleep(100);的情况下此时有足够的时间让A线程先启动
        //1.两个标准synchronized方法   先sendEmail还是sendSMS? 先sendEmail
        //2.邮件方法暂停2秒,先邮件还是先短信  ? 邮件 因为sleep不会释放锁,同一时间只能有一个线程调用一个对象里面的同步方法
        //3.新增一个普通方法?x先打印邮件还是hello ?hello  普通方法和同步锁无关
        //4.两部手机先邮件还是先短信?先短信  这个时候两个对象对应的是两把锁,虽然A先启动但是sendEmail方法睡眠了2秒,所以B线程的sendSMS先打印
        //5.两个静态同步方法,一个手机,先打印邮件还是短信?  先打印邮件,锁的是同一个类
        //6.两个静态同步方法,两个手机,先打印邮件还是短信?   先打印邮件,锁的是同一个类
        //7.  1个普通同步方法,一个静态同步方法,一个手机,先打印的是邮件还是短信? 不是一个锁,一个类锁一个对象锁 先打印短信
        //8.  1个普通同步方法,一个静态同步方法,两个手机,先打印的是邮件还是短信?不是一个锁,一个类锁一个对象锁 短信
        Phone phone = new Phone();
        Phone phone2=new Phone();
        new Thread(()->{
            try {
                phone.sendEmail();
            }catch (Exception e){
               e.printStackTrace();
            }
        },"A").start();
        //让主线程sleep100毫秒
        Thread.sleep(100); //此时保证了A线程先启动
        new Thread(()->{
            try{
                //phone.hello();
               // phone2.sendSMS();
                 phone2.sendSMS();
            }catch (Exception e){
              e.printStackTrace();
            }
        },"B").start();
    }
}
class Phone{  //资源类
    public  synchronized  void sendEmail(){
        try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); }
        System.out.println("send  email~~~~~~~");
    }
    public static synchronized  void sendSMS(){
        System.out.println("send  SMS~~~~~~~~");
    }
    public void hello(){
        System.out.println("hello ~~~~~~~~~");
    }
}
​

list不安全

public class Lesson5_ListNotSafe {
    public static void main(String[] args) {
        //Exception in thread "19" Exception in thread "24" Exception in thread "14" java.util.ConcurrentModificationException
        //线程不安全
      //  List<String> list=new Vector<>(); //1.Vector线程安全但是性能不好  查看源码可以看到add方法加锁
        List<String> list=new CopyOnWriteArrayList<>(); //3.CopyOnWriteArrayList
        //2.List<String> list1 = Collections.synchronizedList(list); //juc中Collections工具类提供了方法
        for(int i=1;i<=30;i++){
            new Thread(()->{
                list .add(UUID.randomUUID().toString().substring(0,8));
                System.out.println(list);
                },String.valueOf(i)).start();
        }
    }
}

Vector 1读1写 性能低

ArrayList 读写提升,数据一致性下降

CopyOnWirteArrayList 写时复制读写分离

先clone一份,写完再合并

CopyOnWirteArrayList 的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); //将原来的索引指向新的元素
            return true;  //这样做的好处是可以对CopyOnWrite容器进行并发的读而不需要加锁
        } finally {
            lock.unlock();
        }

image-20210417185739082.png

HashSet到底层是HashMap

  public HashSet() {
        map = new HashMap<>();
    }

HashSet丢进去的元素就是Key,Value是写死的Object的常量

HashMap线程不安全

HashMap底层是数组链表加红黑树

hashMap里面存的是Node

默认为0初始化为16负载因子为0.75

new HashMap(16,0.75)

hashMap优化:

直接设置一个比较大的初始值,比,避免了频繁扩容

new HashMap<>(1000)直接设置初始值

new HashMap();