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; }
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("继续主线程执行.."); } }
运行结果:
等待子线程执行完毕... 子线程,Thread-0开始执行... 子线程,Thread-0结束执行... 子线程,Thread-1开始执行... 子线程,Thread-1结束执行... 两个子线程执行完毕.... 继续主线程执行..
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(); } } }
运行结果:
线程Thread-0,正在写入数据 线程Thread-1,正在写入数据 线程Thread-2,正在写入数据 线程Thread-4,正在写入数据 线程Thread-3,正在写入数据 线程Thread-3,写入数据成功..... 线程Thread-4,写入数据成功..... 线程Thread-1,写入数据成功..... 线程Thread-0,写入数据成功..... 线程Thread-2,写入数据成功..... 所有线程执行完毕.......... 所有线程执行完毕.......... 所有线程执行完毕.......... 所有线程执行完毕.......... 所有线程执行完毕..........
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(); } }
运行结果:
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走了-----------------------
示例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(); } } }
运行结果:
第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个人,厕所上完了...