Java并发包
1. 同步容器类
1.1 Vector与ArrayList区别
1、ArrayList的中间位置插入或者删除元素时,需要对数组进行复制、移动、代价比较高。因此,它适
合随机查找、修改和遍历,不适合插入和删除。
2、Vector与ArrayList一样,也是通过数组实现的,不同的是它支持线程的同步,线程是安全的。
1.2 HasTable与HasMap区别
1、HashMap不是线程安全的,不能包含重复键,但可以包含重复值.HashMap允许null key和null value,
而Hashtable不允许。
2、HashMap把Hashtable的contains方法去掉了,改成containsvalue和containsKey
1.3 synchronizedMap
Collections.synchronized*(m) 将线程不安全的集合变为线程安全的集合
1.4 ConcurrentHashMap原理
ConcurrentHashMap内部使用段(Segment)来表示这些不同的部分,每个段其实就是一个小的HashTable,它们有自己的锁。只要多个修改操作发生在不同的段上,它们就可以并发进行。把一个整体分成了16个段(Segment.也就是最高支持16个线程的并发修改操作。这也是在重线程场景时减小锁的粒度从而降低锁竞争的一种方案。并且代码中大多共享变量使用volatile关键字声明,目的是第一时间获取修改的内容,性能非常好
2. 并发包三种计数器
2.1 CountDownLatch
CountDownLatch类位于java.util.concurrent包下,利用它可以实现类似计数器的功能。比如有一个任务A,它要等待其他4个任务执行完毕之后才能执行,此时就可以利用CountDownLatch来实现这种功能了。countDownLatch的值一定是大于等于0。
代码01 CountDownLatch实现计数器功能:src/com/mysoft/demo01/CountDownLatchDemo.java
package com.mysoft.demo01;
import java.util.concurrent.CountDownLatch;
public class CountDownLatchDemo {
public static void main(String[] args) throws InterruptedException {
//初始化countDownLatch为2
CountDownLatch countDownLatch=new CountDownLatch(2);
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("子线程,"+Thread.currentThread().getName()+":执行完毕");
countDownLatch.countDown(); //每次countDownLatch的数值-1
}
},"thread-01").start();
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("子线程,"+Thread.currentThread().getName()+":执行完毕");
countDownLatch.countDown(); //每次countDownLatch的数值-1
}
},"thread-02").start();
countDownLatch.await(); //调用当前方法主线程阻塞 countDown结果为0, 阻塞变为运行状态
System.out.println("两个子线程执行完毕.....");
System.out.println("主线程继续执行.....");
}
}
2.2 CyclicBarrier
CyclicBarrier初始化时规定一个数目(int parties)表示调用了CyclicBarrier.await()进入等待的线程数。当线程数达到了这个数目时,所有进入等待状态的线程被唤醒并继续。CyclicBarrier初始时还可带一个Runnable的参数,当给定数量的线程(线程)等待时,它将跳闸,当屏障跳闸时执行给定的屏障动作,由最后一个进入屏障的线程执行。
代码02 CyclicBarrier案例:/thread04/src/com/mysoft/demo01/CyclicBarrierDemo.java
package com.mysoft.demo02;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
class Write extends Thread{
CyclicBarrier cyclicBarrier;
public Write(CyclicBarrier cyclicBarrier){
this.cyclicBarrier=cyclicBarrier;
}
@Override
public void run() {
System.out.println(getName()+",开始执行...");
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(getName()+",执行完毕...");
try {
//等待线程数达到规定值
cyclicBarrier.await();
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
System.out.println("所有线程执行完毕......");
}
}
public class CyclicBarrierDemo {
public static void main(String[] args) {
CyclicBarrier cyclicBarrier=new CyclicBarrier(5);
for (int i = 0; i < 5; i++) {
new Write(cyclicBarrier).start();
}
}
}
2.3 Semaphore信号量
Semaphore是一种基于计数的信号量。它可以设定一个阈值,基于此,多个线程竞争获取许可信号,做自己的申请后归还,超过阈值后,线程申请许可信号将会被阻塞。Semaphore可以用来构建一些对象池,资源池之类的,比如数据库连接池,我们也可以创建计数为1的Semaphore,将其作为一种类似互斥锁的机制,这也叫二元信号量,表示两种互斥状态
semaphore.availablePermits(); //函数用来获取当前可用的资源数量
semaphore.acquire(); //申请资源
semaphore.release(); // 释放资源
代码03 Semaphore信号量案例:/thread04/src/com/mysoft/demo03/SemaphoreDemo.java
package com.mysoft.demo03;
import java.util.Random;
import java.util.concurrent.Semaphore;
class Parent extends Thread{
Semaphore wc;
String name;
public Parent(Semaphore wc,String name) {
this.wc=wc;
this.name=name;
}
@Override
public void run() {
//wc.availablePermits()获取当前资源数
if(wc.availablePermits()>0) {
System.out.println(name+"太好了,有座位了...");
}else {
System.out.println(name+"怎么没有座位了...");
}
try {
//申请资源,如果没有资源就等待其它线程释放资源
wc.acquire();
//申请到资源后,执行
System.out.println(name+"哈哈哈,抢到座位了...");
Thread.sleep(new Random().nextInt(1000));
System.out.println(name+"下车了...");
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
//释放资源
wc.release();
}
}
}
public class SemaphoreDemo {
public static void main(String[] args) {
Semaphore semaphore=new Semaphore(3);
for (int i = 1; i < 11; i++) {
new Parent(semaphore, "第"+i+"个人说: ").start();
}
}
}
3. 并发队列
在并发队列上JDK提供了两套实现,一个是以ConcurrentLinkedQueue为代表的高性能队列,一个是以BlockingQueue接口为代表的阻塞队列,无论哪种都继承自Queue
3.1 ConcurrentLinkedQueue
是一个适用于高并发场景下的队列,通过无锁的方式,实现了高并发状态下的高性能,通常ConcurrentLinkedQueue性能好于BlockingQueue.它是一个基于链接节点的无界线程安全队列。该队列的元素遵循先进先出的原则。头是最先加入的,尾是最近加入的,该队列不允许null元素
3.2 BlockingQueue
3.2.1 ArrayBlockingQueue
ArrayBlockingQueue:是一个有边界的阻塞队列,它的内部实现是一个数组。有边界的意思是它的容量是有限的,我们必须在其初始化的时候指定它的容量大小,容量大小一旦指定就不可改变。ArrayBlockingQueue是以先进先出的方式存储数据,最新插入的对象是尾部,最新移出的对象是头部
3.2.2 LinkedBlockingQueue
LinkedBlockingQueue:阻塞队列大小的配置是可选的,如果我们初始化时指定一个大小,它就是有边界的,如果不指定,它就是无边界的。说是无边界,其实是采用了默认大小为Integer.MAX_VALUE的容量 。它的内部实现是一个链表。
代码04 BlockingQueue模拟生产与消费:src/com/mysoft/demo04/ProducerAndConsumer.java
package com.mysoft.demo04;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
class Producer extends Thread {
private BlockingQueue<String> queue;
private volatile boolean flag = true;
private static AtomicInteger count = new AtomicInteger();
public Producer(BlockingQueue<String> queue) {
this.queue = queue;
}
@Override
public void run() {
try {
System.out.println("生产者线程启动");
while (flag) {
System.out.println("生产者正在生产数据...");
String data = count.incrementAndGet() + "";
// 将数据存入队列
boolean offer = queue.offer(data, 2, TimeUnit.SECONDS);
if (offer) {
System.out.println("生产者,存入" + data + "到队列中,成功!!!");
} else {
System.out.println("生产者,存入" + data + "到队列中,失败...");
}
Thread.sleep(1000);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
System.out.println("生产者退出线程");
}
}
public void stopThread() {
this.flag=false;
}
}
class Consumer extends Thread{
private BlockingQueue<String> queue;
private volatile boolean flag=true;
public Consumer(BlockingQueue<String> queue) {
this.queue=queue;
}
@Override
public void run() {
try {
System.out.println("消费者线程启动");
while(flag) {
System.out.println("消费者正在消费数据...");
String data = queue.poll(2, TimeUnit.SECONDS);
if(data!=null) {
System.out.println("消费者,消费队列中的数据:" + data);
Thread.sleep(1000);
}else {
System.out.println("消费者,超过2秒未获取到数据");
flag=false;
}
}
} catch (Exception e) {
e.printStackTrace();
}finally {
System.out.println("消费者退出线程...");
}
}
}
public class ProducerAndConsumer {
public static void main(String[] args) throws InterruptedException {
BlockingQueue<String> queue = new LinkedBlockingQueue<String>();
Producer p1=new Producer(queue);
//Producer p2=new Producer(queue);
Consumer c1=new Consumer(queue);
p1.start();
//p2.start();
c1.start();
// 执行10s
Thread.sleep(10 * 1000);
p1.stopThread();
}
}