014.多线程-并发包

版权声明:本文为博主原创文章,允许转载,请标明出处。 https://blog.csdn.net/qwdafedv/article/details/84199933

在日常开发中,我们最常用的集合和字典莫过于ArrayList和HashMap了。

	ArrayList,内部是通过数组实现的,它允许对元素进行快速随机访问。
	但是,当进行增加存储功能,以及插入、删除元素时,需要对数组进行复制以及移动,代价比较高。
	它适合随机查找和遍历,不适合插入和删除。

	HashMap,由数组+链表组成的,数组是HashMap的主体,链表则是主要为了解决哈希冲突而存在的。
	如果定位到的数组位置不含链表,那么对于查找,添加等操作很快,仅需一次寻址即可;
	如果定位到的数组包含链表,对于添加操作,其时间复杂度为O(n),首先遍历链表,存在即覆盖,否则新增;
	对于查找操作来讲,仍需遍历链表,然后通过key对象的equals方法逐一比对查找。
	所以,性能考虑,HashMap中的链表出现越少,性能才会越好。

但是,ArrayList和HashMap都是线程不安全的。
与此同时,相对应的 Vector和HashTable却是线程安全的,但是,效率太低了。

	Vector与ArrayList一样,也是通过数组实现的,不同的是它支持线程的同步。
	Vector的add和get均被synchronized修饰,
	即:同一时刻,只有一个线程可以读写Vector。

	HashTable类似于HashMap,只不过HashMap允许null key 和 null value,HashTable不允许。
	不过它的put和get方法均被synchronized修饰,
	即:同一时刻,只有一个线程可以读写HashTable。


因此为提升效率的并发包应运而生。

Collections.synchronizedList


        List<String> list1 = new CopyOnWriteArrayList<String>();
        List<String> list2 = Collections.synchronizedList(new ArrayList<String>());

CopyOnWriteArrayList与Collections.synchronizedList性能比较

  • 读操作时,CopyOnWriteArrayList性能相对较好些。
  • 写操作时,Collections.synchronizedList性能相对较好些。

ConcurrentHashMap

ConcurrentHashMap内部使用段(Segment)来表示不同的部分,
每个部分都相当于一个HashTable,都拥有自己的锁。
只要多个操作不同时发生在一个段上,就可以并发进行。
其中Segment数组长度为16,不可以扩容。
ConcurrentHashMap的初始化容量会平均分配给每个Segment。
其中的负载因子是针对每个Segment的,
当实际容量大于阈值(负载因子乘以Segment容量)时,会扩容当前Segment。

    /**
     * The default load factor for this table, used when not
     * otherwise specified in a constructor.
     */
    static final float DEFAULT_LOAD_FACTOR = 0.75f;

    /**
     * The default concurrency level for this table, used when not
     * otherwise specified in a constructor.
     */
    static final int DEFAULT_CONCURRENCY_LEVEL = 16;

CountDownLatch

利用它可以实现类似计数器的功能。
比如有一个任务A,它要等待其他4个任务执行完毕之后才能执行,
此时就可以利用CountDownLatch来实现这种功能了。

package cn.qbz.thread;

import java.util.concurrent.CountDownLatch;

public class Test111901 {
    public static void main(String[] args) throws InterruptedException {
        final CountDownLatch countDownLatch = new CountDownLatch(3);
        for (int i = 0; i < 3; i++) {
            new Thread(new Runnable() {
                public void run() {
                    System.out.println("子线程" + Thread.currentThread().getName() + "开始运行...");
                    for (int i = 0; i < 10; i++) {
                        try {
                            Thread.sleep(100);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    System.out.println("子线程" + Thread.currentThread().getName() + "结束运行...");
                    countDownLatch.countDown();
                }
            }).start();
        }

        System.out.println("主线程等待子线程执行结束....");
        countDownLatch.await();
        System.out.println("主线程执行结束......");
    }
}

如果 final CountDownLatch countDownLatch = new CountDownLatch(4);
主线程将会一直阻塞在 countDownLatch.await();


CyclicBarrier

CyclicBarrier初始化时规定一个数目,
然后计算调用了CyclicBarrier.await()进入等待的线程数。
当线程数达到了这个数目时,所有进入等待状态的线程被唤醒并继续。

package cn.qbz.thread;

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

public class Test111902 {
    public static void main(String[] args) {
        CyclicBarrier cyclicBarrier = new CyclicBarrier(4);
        for (int i = 0; i < 4; i++) {
            new ThreadTest111902(cyclicBarrier).start();
        }
        System.out.println("主线程执行结束......");
    }
}

class ThreadTest111902 extends Thread {
    private CyclicBarrier cyclicBarrier;

    public ThreadTest111902(CyclicBarrier cyclicBarrier) {
        this.cyclicBarrier = cyclicBarrier;
    }

    @Override
    public void run() {
        System.out.println("子线程" + getName() + ":开始运行");
        try {
            Thread.sleep(3000);

            System.out.println("子线程" + getName() + ":结束运行");
            cyclicBarrier.await();

            System.out.println("子线程" + getName() + ":所有线程执行结束....");
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (BrokenBarrierException e) {
            e.printStackTrace();
        }
    }
}


Semaphore

Semaphore是一种基于计数的信号量。
它可以设定一个阈值,多个线程竞争获取许可信号,
超过阈值后,线程申请许可信号将会被阻塞。
Semaphore可以用来构建一些对象池,资源池之类的,比如数据库连接池。
我们也可以创建计数为1的Semaphore,将其作为一种类似互斥锁的机制,这也叫二元信号量,表示两种互斥状态。
它的用法如下:
availablePermits函数用来获取当前可用的资源数量
wc.acquire(); //申请资源
wc.release();// 释放资源

	// 创建一个计数阈值为5的信号量对象  
   	// 只能5个线程同时访问  
   	Semaphore semp = new Semaphore(5);  
   	  
   	try {  
   	    // 申请许可  
   	    semp.acquire();  
   	    try {  
   	        // 业务逻辑  
   	    } catch (Exception e) {  
   	  
   	    } finally {  
   	        // 释放许可  
   	        semp.release();  
   	    }  
   	} catch (InterruptedException e) {  
   	  
   	}  

code of semaphore demo:

有4个业务办理窗口,等候区共有30人,模拟。

package cn.qbz.thread;

import java.util.concurrent.Semaphore;

public class Test111903 {
    public static void main(String[] args) {
        Semaphore semaphore = new Semaphore(4);
        for (int i = 0; i < 30; i++) {
            new ThreadTest111903(semaphore, "第" + i + "个").start();
        }
        System.out.println("主线程执行结束......");
    }
}

class ThreadTest111903 extends Thread {
    private Semaphore semaphore;
    private String name;

    public ThreadTest111903(Semaphore semaphore, String name) {
        this.semaphore = semaphore;
        this.name = name;
    }

    @Override
    public void run() {
        //判断是否可以申请许可
        int availablePermits = semaphore.availablePermits();
        if (availablePermits > 0) {
            System.out.println(name + "...抢到了窗口");
        } else {
            System.out.println(name + "...没抢到了窗口");
        }

        try {
            //申请资源,如果没有可用的资源,则阻塞在这里
            //有了资源后,再从这里继续执行
            semaphore.acquire();
            System.out.println(name + "。。。开始办理业务");
            Thread.sleep(Math.round(1000));
            System.out.println(name + "。。。结束办理业务");
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            //释放资源
            semaphore.release();
        }
    }
}

猜你喜欢

转载自blog.csdn.net/qwdafedv/article/details/84199933