【狂神说java juc 20200219 02篇】8锁现象,TimeUnit,线程安全的使用集合 ConcurrentHashMap,Callable,CyclicBarrierr

8锁现象

  • 锁的是谁
    • 对象
    • Class

TimeUnit

        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
public enum TimeUnit {
    SECONDS {
        public long toNanos(long d)   { return x(d, C3/C0, MAX/(C3/C0)); }
        public long toMicros(long d)  { return x(d, C3/C1, MAX/(C3/C1)); }
        public long toMillis(long d)  { return x(d, C3/C2, MAX/(C3/C2)); }
        public long toSeconds(long d) { return d; }
        public long toMinutes(long d) { return d/(C4/C3); }
        public long toHours(long d)   { return d/(C5/C3); }
        public long toDays(long d)    { return d/(C6/C3); }
        public long convert(long d, TimeUnit u) { return u.toSeconds(d); }
        int excessNanos(long d, long m) { return 0; }
    },
}

谁先拿到谁执行 1 2

/**
 * 8锁,就是关于锁的8个问题
 * 1、标准情况下,两个线程先打印 发短信还是 打电话? 1/发短信  2/打电话
 * 1、sendSms延迟4秒,两个线程先打印 发短信还是 打电话? 1/发短信  2/打电话
 */
public class Test1 {
    public static void main(String[] args) {
        Phone phone = new Phone();

        //锁的存在。 A必然先执行,并且会锁上。
        new Thread(()->{
            phone.sendSms();
        },"A").start();

        // 捕获
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        new Thread(()->{
            phone.call();
        },"B").start();
    }
}

class Phone{

    // synchronized 锁的对象是方法的调用者!、
    // 两个方法用的是同一个锁,谁先拿到谁执行!
    public synchronized void sendSms(){
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("发短信");
    }

    public synchronized void call(){
        System.out.println("打电话");
    }

}

非同步方法 和 不同锁 一起执行 3 4

/**
 * 3、 增加了一个普通方法后!先执行发短信还是Hello? 普通方法
 * 4、 两个锁 对象,两个同步方法, 发短信还是 打电话? // 打电话
 */
public class Test2  {
    public static void main(String[] args) {
        // 两个对象,两个调用者,两把锁!
        Phone2 phone1 = new Phone2();
        Phone2 phone2 = new Phone2();

        //锁的存在
        new Thread(()->{
            phone1.sendSms();
        },"A").start();

        // 捕获
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        new Thread(()->{
            phone2.call();
        },"B").start();
    }
}

class Phone2{

    // synchronized 锁的对象是方法的调用者!
    public synchronized void sendSms(){
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("发短信");
    }

    public synchronized void call(){
        System.out.println("打电话");
    }

    // 这里没有锁!不是同步方法,不受锁的影响
    public void hello(){
        System.out.println("hello");
    }

}

static锁Class 5 6

  • 依然是 谁先拿到谁先执行,如发短信
// Phone3唯一的一个 Class 对象
class Phone3{

    // synchronized 锁的对象是方法的调用者!
    // static 静态方法
    // 类一加载就有了!锁的是Class
    public static synchronized void sendSms(){
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("发短信");
    }

    public static synchronized void call(){
        System.out.println("打电话");
    }
}
Class<Phone3> phone3Class = Phone3.class;
/**
 * 5、增加两个静态的同步方法,只有一个对象,先打印 发短信?打电话? 发短信
 * 6、两个对象!增加两个静态的同步方法, 先打印 发短信?打电话? 发短信
 */

锁不一样 都会执行 7 8

  • 锁 对象的先执行,锁 Class的后执行(因为 有睡眠)
// Phone3唯一的一个 Class 对象
class Phone4{

    // 静态的同步方法 锁的是 Class 类模板
    public static synchronized void sendSms(){
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("发短信");
    }

    // 普通的同步方法  锁的调用者
    public synchronized void call(){
        System.out.println("打电话");
    }

}
/**
 * 1、1个静态的同步方法,1个普通的同步方法 ,一个对象,先打印 发短信?打电话? //打电话。 一个锁class 一个锁 new 对象,不相关
 * 2、1个静态的同步方法,1个普通的同步方法 ,两个对象,先打印 发短信?打电话? //打电话。就算是 2个 对象锁,还是 打电话。
 */

new this 具体的一个手机

static Class 唯一的一个模板

集合不安全

        List<String> list1 = Arrays.asList("1", "2", "3");
        list1.forEach(System.out::println);
  • 异常
java.lang.OutOfMemoryError
Java.lang.StackOverFlow
// java.util.ConcurrentModificationException 并发修改异常!

包结构

  • util.concurrent

    • Concurrent HashMap

    • Concurrent HashMap.KeySetView

    • Concurrent Linked Deque 双端队列

    • Concurrent Linked Queue

    • Concurrent Skip ListMap

    • Concurrent Skip ListSet

    • CopyOnWrite ArrayList

    • CopyOnWrite ArraySet

    • CountDownLatch

    • Cyclic Barrier

    • Semaphore

    • Delay Queue

解决list不安全方式

        // 并发下 ArrayList 不安全的吗,Synchronized;
        /**
         * 解决方案;
         * 1、List<String> list = new Vector<>();
         * 2、List<String> list = Collections.synchronizedList(new ArrayList<>());
         * 3、List<String> list = new CopyOnWriteArrayList<>();
         */
        // CopyOnWrite 写入时复制  COW  计算机程序设计领域的一种优化策略;
        // 多个线程调用的时候,list,读取的时候,固定的,写入(覆盖)
        // 在写入的时候避免覆盖,造成数据问题!
        // 读写分离
        // CopyOnWriteArrayList  比 Vector Nb 在哪里?

        List<String> list = new CopyOnWriteArrayList<>();

        for (int i = 1; i <= 10; i++) {
            new Thread(()->{
                list.add(UUID.randomUUID().toString().substring(0,5));
                System.out.println(list);
            },String.valueOf(i)).start();
        }

CopyOnWriteArrayList

new CopyOnWriteArrayList<>()
    
	//底层,使用了 Lock锁
    private transient volatile Object[] array;

    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);
            //最后在 set进去
            newElements[len] = e;
            setArray(newElements);
            return true;
        } finally {
            lock.unlock();
        }
    }

集合结构

  • Collection 实现了 Iterable

    • Blocking Queue 继承了 Queue,Queue继承了 Collection
      • Blocking Deque 接口,还继承了 Deque,Deque 继承了 Queue
    • Set
    • List
  • Collection

    • Queue
      • Blocking Queue 阻塞队列
      • Deque
        • Blocking Deque,继承上面两个

Set安全

public static void main(String[] args) {
        Set<String> set = new HashSet<>();
        // hashmap
        // Set<String> set = Collections.synchronizedSet(new HashSet<>());
        // Set<String> set = new CopyOnWriteArraySet<>();

        for (int i = 1; i <=30 ; i++) {
           new Thread(()->{
               set.add(UUID.randomUUID().toString().substring(0,5));
               System.out.println(set);
           },String.valueOf(i)).start();
        }

    }
  • HashSet底层,只用了 key

        public HashSet() {
            map = new HashMap<>();
        }
    
        public boolean add(E e) {
            return map.put(e, PRESENT)==null;
        }
    
        private static final Object PRESENT = new Object();
    

HashMap

		// map 是这样用的吗? 不是,工作中不用 HashMap
        // 默认等价于什么?  new HashMap<>(16,0.75);
        // Map<String, String> map = new HashMap<>();
        // 研究ConcurrentHashMap的原理

        Map<String, String> map = new ConcurrentHashMap<>();
		//Collections.synchronizedMap(new HashMap<>());

        for (int i = 1; i <=30; i++) {
            new Thread(()->{
                map.put(Thread.currentThread().getName(),UUID.randomUUID().toString().substring(0,5));
                System.out.println(map);
            },String.valueOf(i)).start();
        }
    static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16 。默认的长度

    static final int MAXIMUM_CAPACITY = 1 << 30;

    static final float DEFAULT_LOAD_FACTOR = 0.75f; //默认的加载因子
capacity
n.
能力,才能;容积,容纳能力;职位,职责;功率,容积;生产量,生产能力

factor
n.
因素,要素;等级,系数;因数,因子;遗传因子,基因;(血液中的)凝血因子;代理公司,代理商;<苏格兰>地产管理人,管家;测量水平
v.
把……作为因素计入,把……包括在内(factor in);把……作为因素排除,不把……包括在内(factor out);将……分解为因子;代理经营,(代管)产业;做代理商

Callable

  • Callable接口类似于Runnable ,因为它们都是为其实例可能由另一个线程执行的类设计的。 然而,A Runnable不返回结果,也不能抛出被检查的异常。

    Executors类包含的实用方法,从其他普通形式转换为Callable类。

1、可以有返回值

2、可以抛出异常

3、方法不同,run()/ call()

  • Runnable
    • RunnableFuture
    • Runnable Scheduled Future
    • ForkJoinWorkerThread
    • FutureTask
        //4. 创建执行服务
        ExecutorService ser = Executors.newFixedThreadPool(3);
        //5. 提交执行
        Future<Boolean> r = ser.submit(new MyCallable());

        //6. 获取结果
        Boolean b = r.get();

        //7. 关闭服务
        ser.shutdown();
        FutureTask<Boolean> f = new FutureTask(new MyCallable());
        new Thread(f).start();

        Boolean o = f.get();
        // new Thread(new Runnable()).start();
        // new Thread(new FutureTask<V>()).start();
		//适配器类 为 FutureTask
        // new Thread(new FutureTask<V>( Callable )).start();
        new Thread().start(); // 怎么启动Callable

		//自己的 Callable实现类,
        MyThread thread = new MyThread();
		//放入适配器 类
        FutureTask futureTask = new FutureTask(thread); // 适配类

        new Thread(futureTask,"A").start();
        new Thread(futureTask,"B").start(); // 结果会被缓存,效率高

        Integer o = (Integer) futureTask.get(); //这个get 方法可能会产生阻塞!把他放到最后
        // 或者使用异步通信来处理!
        System.out.println(o);

class MyThread implements Callable<Integer> {
    @Override
    public Integer call() {
        System.out.println("call()"); // 会打印几个call
        // 耗时的操作
        return 1024;
    }
}

常用的辅助类

  • CountDownLatch

  • Cyclic Barrier

  • Semaphore

  • Executors

  • ForkJoin Pool

  • Linked Blocking Deque 阻塞队列

  • Linked Blocking Queue

  • Linked Transfer Queue

  • Recur sive Action 递归的任务

  • Recursive Task

  • Thread Pool Executor. XXX方法

Cyclic
英
/ˈsaɪklɪk/
adj.
环的;循环的;周期的

barrier
英
/ˈbæriə(r)
n.
障碍,壁垒;障碍物,关卡;分界线,屏障;大关,界限

semaphore
英
/ˈseməfɔː(r)/
n.
信号标,旗语;臂板信号装置
v.
打旗语,发信号

CountDownLatch *

  • 子线程先执行

允许一个或多个线程等待直到在其他线程中执行的一组操作完成的同步辅助。
A CountDownLatch用给定的计数初始化。 await方法阻塞,直到由于countDown()方法的调用而导致当前计数达到零,之后所有等待线程被释放,并且任何后续的await 调用立即返回。 这是一个一次性的现象 - 计数无法重置。 如果您需要重置计数的版本,请考虑使用CyclicBarrier 。

        // 总数是6,必须要执行任务的时候,再使用!
        CountDownLatch countDownLatch = new CountDownLatch(6);

        for (int i = 1; i <=6 ; i++) {
            new Thread(()->{
                System.out.println(Thread.currentThread().getName()+" Go out");
                countDownLatch.countDown(); // 数量-1
            },String.valueOf(i)).start();
        }

        countDownLatch.await(); // 等待计数器归零,然后再向下执行

        System.out.println("Close Door");

其他实现的方式:

  • Callable接口 *
  • t.join(); 线程插队,t线程会执行完毕,才能走其他的

.countDown(); // 数量-1

.await(); // 等待计数器归零,然后再向下执行

每次有线程调用 countDown() 数量-1,假设计数器变为0,countDownLatch.await() 就会被唤醒,继续

执行

CyclicBarrier *

  • 允许一组线程全部等待彼此达到共同屏障点的同步辅助。循环阻塞在涉及固定大小的线程方的程序中很有用,这些线程必须偶尔等待彼此。屏障被称为循环 ,因为它可以在等待的线程被释放之后重新使用。

    A CyclicBarrier支持一个可选的Runnable命令,每个屏障点运行一次,在派对中的最后一个线程到达之后,但在任何线程释放之前。 在任何一方继续进行之前,此屏障操作对更新共享状态很有用。

        // 召唤龙珠的线程
        CyclicBarrier cyclicBarrier = new CyclicBarrier(7,()->{
            System.out.println("召唤神龙成功!");
        });

        for (int i = 1; i <=7 ; i++) {
            final int temp = i;
            // lambda能操作到 i 吗
            new Thread(()->{
                System.out.println(Thread.currentThread().getName()+"收集"+temp+"个龙珠");
                try {
                    cyclicBarrier.await(); // 等待
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }
            }).start();
        }

Semaphore

  • 一个计数信号量。 在概念上,信号量维持一组许可证。 如果有必要,每个acquire()都会阻塞,直到许可证可用,然后才能使用它。 每个release()添加许可证,潜在地释放阻塞获取方。 但是,没有使用实际的许可证对象; Semaphore只保留可用数量的计数,并相应地执行。
		// 线程数量:停车位! 限流!
        Semaphore semaphore = new Semaphore(3);

        for (int i = 1; i <=6 ; i++) {
            new Thread(()->{
                // acquire() 得到
                try {
                    semaphore.acquire();
                    System.out.println(Thread.currentThread().getName()+"抢到车位");
                    TimeUnit.SECONDS.sleep(2);
                    System.out.println(Thread.currentThread().getName()+"离开车位");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    semaphore.release(); // release() 释放
                }

            },String.valueOf(i)).start();
        }
acquire
英
/əˈkwaɪə(r)/
v.
获得,得到;学到,习得;患上(疾病);逐渐具有,开始学会

原理:

semaphore.acquire() 获得,假设如果已经满了,等待,等待被释放为止!

semaphore.release(); 释放,会将当前的信号量释放 + 1,然后唤醒等待的线程!

作用:

多个共享资源互斥的使用!并发限流,控制最大的线程数!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值