zoukankan      html  css  js  c++  java
  • 十一、多线程基础-同步容器类

    1、Vector与ArrayList区别
    1)ArrayList是最常用的List实现类,内部是通过数组实现的,它允许对元素进行快速随机访问。数组的缺点是每个元素之间不能有间隔,当数组大小不满足时需要增加存储能力,就要将已经有数组的数据复制到新的存储空间中。当从ArrayList的中间位置插入或者删除元素时,需要对数组进行复制、移动、代价比较高。因此,它适合随机查找和遍历,不适合插入和删除。
    2)Vector与ArrayList一样,也是通过数组实现的,不同的是它支持线程的同步,即某一时刻只有一个线程能够写Vector,避免多线程同时写而引起的不一致性,但实现同步需要很高的花费,因此,访问它比访问ArrayList慢。Vector线程安全、ArrayList非线程安全

    Vector源码类中的Add方法
        /**
         * Appends the specified element to the end of this Vector.
         *
         * @param e element to be appended to this Vector
         * @return {@code true} (as specified by {@link Collection#add})
         * @since 1.2
         */
        public synchronized boolean add(E e) {
        modCount++;
        ensureCapacityHelper(elementCount + 1);
        elementData[elementCount++] = e;
            return true;
        }
    
    Arraylist源码类中的Add方法
        public boolean add(E e) {
        ensureCapacity(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
        }
    View Code

    2、HasTable与HasMap区别
    1)HastMap
    <1>是一个接口 是map接口的子接口,是将键映射到值的对象,其中键和值都是对象,并且不能包含重复键,但可以包含重复值(HashMap允许null key和null value),而hashtable不允许。
    <2>由于非线程安全,效率上相对来说高于Hashtable。
    <3>HashMap把Hashtable的contains方法去掉了,改成containsvalue和containsKey
    2)HashTable
    <1>是线程安全的一个Collection。HashMap是Hashtable的轻量级实现(非线程安全的实现),他们都完成了Map接口
    <2>Hashtable不允许空值或者空键,线程安全
    备注:1)集合的线程安全如何实现:
    Collections.synchronized*(m) 将线程不安全额集合变为线程安全集合
    2)ConcurrentMap接口下有两个重要的实现 :
    ConcurrentHashMap
    ConcurrentHashMap内部使用段(Segment)来表示这些不同的部分,每个段其实就是一个
    小的HashTable,它们有自己的锁。只要多个修改操作发生在不同的段上,它们就可以并
    发进行。把一个整体分成了16个段(Segment.也就是最高支持16个线程的并发修改操作。
    这也是在重线程场景时减小锁的粒度从而降低锁竞争的一种方案。并且代码中大多共享变
    量使用volatile关键字声明,目的是第一时间获取修改的内容,性能非常好
    ConcurrentskipListMap (支持并发排序功能。弥补ConcurrentHas hMa p)
    3、CountDownLatch
    CountDownLatch类位于java.util.concurrent包下,利用它可以实现类似计数器的功能。
    锁存器的常用方法:
        countDown() 递减锁存器的计数,如果计数到达零,则释放所有等待的线程
        await(long timeout, TimeUnit unit) 使当前线程在锁存器倒计数至零之前一直等待,除非线程被中断或超出了指定的等待时间
        await()     使当前线程在锁存器倒计数至零之前一直等待,除非线程被中断
        getCount()     返回当前计数
    功能描述:有一个任务A,它要等待其他2个任务执行完毕之后才能执行,此时就可以利用CountDownLatch来实现这种功能了

    import java.util.concurrent.CountDownLatch;
    public class CountDownLatchTest {
    
        public static void main(String[] args) throws InterruptedException {
            System.out.println("等待子线程执行完毕...");
            /*
     CountDownLatch类位于java.util.concurrent包下,利用它可以实现类似计数器的功能,
             * */
            final CountDownLatch countDownLatch = new CountDownLatch(2);
            new Thread(new Runnable() {
                public void run() {
                System.out.println("子线程," + Thread.currentThread().getName() + "开始执行...");
                countDownLatch.countDown();// 每次减去1
                System.out.println("子线程," + Thread.currentThread().getName() + "结束执行...");
                }
            }).start();
            new Thread(new Runnable() {
                public void run() {
                System.out.println("子线程," + Thread.currentThread().getName() + "开始执行...");
                countDownLatch.countDown();
                System.out.println("子线程," + Thread.currentThread().getName() + "结束执行...");
                }
            }).start();
    // 调用该方法    主线程将阻塞  直到countDown结果为0, 阻塞才会变为运行状态
            countDownLatch.await();        
    System.out.println("两个子线程执行完毕....");
            System.out.println("继续主线程执行..");
        }
    
    }
    View Code

    运行结果:

    等待子线程执行完毕...
    子线程,Thread-0开始执行...
    子线程,Thread-0结束执行...
    子线程,Thread-1开始执行...
    子线程,Thread-1结束执行...
    两个子线程执行完毕....
    继续主线程执行..
    View Code

    4、CyclicBarrier
      CyclicBarrier初始化时规定一个数目,然后用CyclicBarrier.await()方法计算进入等待的线程数。当线程数达到了这个数目时,所有进入等待状态的线程被唤醒并继续。CyclicBarrier就象它名字的意思一样,可看成是个障碍, 所有的线程必须到齐后才能一起通过这个障碍。 CyclicBarrier初始时还可带一个Runnable的参数, 此Runnable任务在CyclicBarrier的数目达到后,所有其它线程被唤醒前被执行。

    import java.util.concurrent.CyclicBarrier;
    class Writer extends Thread {
        private CyclicBarrier cyclicBarrier;
        public Writer(CyclicBarrier cyclicBarrier){
             this.cyclicBarrier=cyclicBarrier;
        }
        @Override
        public void run() {
            System.out.println("线程" + Thread.currentThread().getName() + ",正在写入数据");
            try {
                Thread.sleep(3000);
            } catch (Exception e) {
                // TODO: handle exception
            }
            System.out.println("线程" + Thread.currentThread().getName() + ",写入数据成功.....");
            try {
                cyclicBarrier.await();
            } catch (Exception e) {
            }
            System.out.println("所有线程执行完毕..........");
        }
    
    }
    
    public class CyclicBarrierTest {
    
        public static void main(String[] args) {
            CyclicBarrier cyclicBarrier=new CyclicBarrier(5);
            for (int i = 0; i < 5; i++) {
                Writer writer = new Writer(cyclicBarrier);
                writer.start();
            }
        }
    
    }
    View Code

    运行结果:

    线程Thread-0,正在写入数据
    线程Thread-1,正在写入数据
    线程Thread-2,正在写入数据
    线程Thread-4,正在写入数据
    线程Thread-3,正在写入数据
    线程Thread-3,写入数据成功.....
    线程Thread-4,写入数据成功.....
    线程Thread-1,写入数据成功.....
    线程Thread-0,写入数据成功.....
    线程Thread-2,写入数据成功.....
    所有线程执行完毕..........
    所有线程执行完毕..........
    所有线程执行完毕..........
    所有线程执行完毕..........
    所有线程执行完毕..........
    View Code

    5、Semaphore(示列2)
      Semaphore就是一个信号量,它的作用是 限制某段代码块的并发数 。Semaphore有一个构造函数,可以传入一个int型整数n,表示某段代码最多只有n个线程可以访问,如果超出了n,那么请等待,等到某个线程执行完毕这段代码块,下一个线程再进入。由此可以看出如果Semaphore构造函数中传入的int型整数n=1,相当于变成了一个synchronized了。
          Semaphore:计数信号量,信号量维护了一个许可集.如有必要,在许可可用前会阻塞每一个 acquire(),然后再获取该许可。每个 release() 添加一个许可,从而可能释放一个正在阻塞的获取者。但是,不使用实际的许可对象,Semaphore 只对可用许可的号码进行计数,并采取相应的行动。
        说明:
            acquire():从此信号量获取一个许可,在提供一个许可前一直将线程阻塞.获取一个许可(如果提供了一个)并立即返回,将可用的许可数减 1  
        如果没有可用的许可,则在发生以下两种情况之一前,禁止将当前线程用于线程安排目的并使其处于休眠状态。
            release():释放一个许可,将其返回给信号量。 释放一个许可,将其返回给信号量,将可用的许可数增加 1。如果任意线程试图获取许可,则选中一个线程并将刚刚释放的许可给予它。然后针对线程安排目的启用(或再启用)该线程

    示例1:(功能描述:如何控制某个方法允许并发访问线程的个数?)

    import java.util.concurrent.Semaphore;
    public class SemaphoreTest {
    
        static Semaphore semaphore = new Semaphore(5, true);//创建具有给定的许可数和给定的公平设置的 Semaphore。
        public static void main(String[] args) {
            for (int i = 0; i < 100; i++) {
                new Thread(new Runnable() {
                    public void run() {
                        test();
                    }
                }).start();
            }
        }
    
        public static void test() {
            try {
                // 申请一个请求
                semaphore.acquire();
            } catch (InterruptedException e1) {
                e1.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "进来了");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "走了-----------------------");
            // 释放一个请求
            semaphore.release();
        }
    }
    View Code

    运行结果:

    Thread-0进来了
    Thread-2进来了
    Thread-1进来了
    Thread-3进来了
    Thread-4进来了
    Thread-0走了-----------------------
    Thread-5进来了
    Thread-1走了-----------------------
    Thread-2走了-----------------------
    Thread-4走了-----------------------
    Thread-3走了-----------------------
    Thread-8进来了
    Thread-7进来了
    Thread-6进来了
    Thread-9进来了
    Thread-5走了-----------------------
    Thread-10进来了
    Thread-9走了-----------------------
    Thread-8走了-----------------------
    Thread-7走了-----------------------
    Thread-11进来了
    Thread-12进来了
    Thread-6走了-----------------------
    Thread-13进来了
    Thread-14进来了
    Thread-10走了-----------------------
    Thread-15进来了
    Thread-11走了-----------------------
    Thread-13走了-----------------------
    Thread-14走了-----------------------
    Thread-12走了-----------------------
    Thread-16进来了
    Thread-18进来了
    Thread-17进来了
    Thread-19进来了
    Thread-15走了-----------------------
    Thread-20进来了
    Thread-16走了-----------------------
    Thread-18走了-----------------------
    Thread-19走了-----------------------
    Thread-21进来了
    Thread-22进来了
    Thread-23进来了
    Thread-17走了-----------------------
    View Code

    示例2:(功能描述:一个厕所只有3个坑位,但是有10个人来上厕所,那怎么办?)
      假设10的人的编号分别为1-10,并且1号先到厕所,10号最后到厕所。那么1-3号来的时候必然有可用坑位,顺利如厕,4号来的时候需要看看前面3人是否有人出来了,如果有人出来,进去,否则等待。同样的道理,4-10号也需要等待正在上厕所的人出来后才能进去,并且谁先进去这得看等待的人是否有素质,是否能遵守先来先上的规则。

    import java.util.Random;
    import java.util.concurrent.Semaphore;
    class Parent implements Runnable {
        private String name;
        private Semaphore wc;
        public Parent(String name,Semaphore wc){
            this.name=name;
            this.wc=wc;
        }
        public void run() {
            try {
                // 剩下的资源(剩下的茅坑)
                int availablePermits = wc.availablePermits();
                if (availablePermits > 0) {
                    System.out.println(name+"天助我也,我有茅坑哈哈");
                } else {
                    System.out.println(name+"怎么没有茅坑呀郁闷");
                }
                //申请茅坑 如果资源达到3次,就等待
                wc.acquire();
                System.out.println(name+"我可以上厕所了..爽啊");
                Thread.sleep(new Random().nextInt(1000)); // 模拟上厕所时间。
                System.out.println(name+"厕所上完了...");
                wc.release();
            } catch (Exception e) {
    
            }
        }
    }
    public class SemaphoreTest2 {
        public static void main(String[] args) {
             Semaphore semaphore = new Semaphore(3);
            for (int i = 1; i <=10; i++) {
                 Parent parent = new Parent("第"+i+"个人,",semaphore);
                 new Thread(parent).start();
            }
        }
    }
    View Code

     运行结果:

    第1个人,天助我也,我有茅坑哈哈
    第3个人,天助我也,我有茅坑哈哈
    第2个人,天助我也,我有茅坑哈哈
    第2个人,我可以上厕所了..爽啊
    第3个人,我可以上厕所了..爽啊
    第1个人,我可以上厕所了..爽啊
    第4个人,天助我也,我有茅坑哈哈
    第7个人,怎么没有茅坑呀郁闷
    第8个人,怎么没有茅坑呀郁闷
    第9个人,怎么没有茅坑呀郁闷
    第5个人,怎么没有茅坑呀郁闷
    第6个人,怎么没有茅坑呀郁闷
    第10个人,怎么没有茅坑呀郁闷
    第1个人,厕所上完了...
    第4个人,我可以上厕所了..爽啊
    第3个人,厕所上完了...
    第7个人,我可以上厕所了..爽啊
    第7个人,厕所上完了...
    第8个人,我可以上厕所了..爽啊
    第2个人,厕所上完了...
    第9个人,我可以上厕所了..爽啊
    第8个人,厕所上完了...
    第5个人,我可以上厕所了..爽啊
    第4个人,厕所上完了...
    第6个人,我可以上厕所了..爽啊
    第5个人,厕所上完了...
    第10个人,我可以上厕所了..爽啊
    第10个人,厕所上完了...
    第9个人,厕所上完了...
    第6个人,厕所上完了...
    View Code
    细水长流,打磨濡染,渐趋极致,才是一个人最好的状态。
  • 相关阅读:
    在未排序的数组中找到第 k 个最大的元素
    区域和检索
    控制台画图程序(可更换笔刷版本)
    循环中的scanf处理了换行符怎么破
    strlen获取字符数组为什么是255
    宽字符输出中文,Devc++解决方法
    区间取最小值最大值-位值和
    模拟鼠标键盘-封装函数
    scanf("%d",a[i]+j)为什么不加取地址符号
    scanf需要多输入一行是什么问题
  • 原文地址:https://www.cnblogs.com/jiarui-zjb/p/9622898.html
Copyright © 2011-2022 走看看