文章目录
- 6.并发工具类
- 6.1 并发工具类-Hashtable
- 6.2 并发工具类-ConcurrentHashMap基本使用
- 6.3 并发工具类-ConcurrentHashMap1.7原理
- 6.4 并发工具类-ConcurrentHashMap1.8原理
- 6.5 并发工具类-CountDownLatch
- 6.6并发工具类-Semaphore
- 总结
6.并发工具类
6.1 并发工具类-Hashtable
- Hashtable出现的原因:在集合类中HashMap是比较常用的集合对象,但是HashMap是线程不安全的(多线程环境下可能会存在问题)。为了保证数据的安全性我们可以使用Hashtable,但是Hashtable的效率低下。
- 代码实现:
package com.hcx.mymap;
import java.util.HashMap;
import java.util.Hashtable;
public class MyHashtableDemo {
public static void main(String[] args) throws InterruptedException {
Hashtable<String,String> hm = new Hashtable<>();
Thread t1 = new Thread(()->{
for (int i = 0; i < 25; i++) {
hm.put(i+" ",i+" ");
}
});
Thread t2 = new Thread(()->{
for (int i = 25; i < 51; i++) {
hm.put(i+" ",i+" ");
}
});
t1.start();
t2.start();
System.out.println("=============");
//让 main 线程睡一秒中,为了让 t1、t2能把数据全部添加完毕
Thread.sleep(1000);
for (int i = 0; i < 51; i++) {
System.out.println(hm.get(i+" "));
}//0 1 2 3 4 ... 50
}
}
- 运行结果:
6.2 并发工具类-ConcurrentHashMap基本使用
-
ConcurrentHashMap出现的原因:在集合中HashMap是比较常用的集合对象,但是HashMap是线程不安全的(多线程环境下可能会存在问题)。为了保证数据的安全性我们可以使用Hashtable,但是Hashtable的效率低下。基于以上两个原因我们可以使用JDK1.5以后所提供的ConcurrentHashMap。
-
体系结构:
-
总结:
- 1.HashMap是线程不安全的 ,多线程环境下会有数据安全问题。
- 2.Hashtable是线程安全的,但是会将整张表锁起来,效率低下。
- 3.ConcurrentHashMap也是线程安全的,效率较高,在JDK7和JDK8中,底层原理不一样。
-
代码实现:
package com.hcx.mymap;
import java.util.Hashtable;
import java.util.concurrent.ConcurrentHashMap;
public class MyConcurrentHashMapDemo {
public static void main(String[] args) throws InterruptedException {
ConcurrentHashMap<String,String> hm = new ConcurrentHashMap<>();
Thread t1 = new Thread(()->{
for (int i = 0; i < 25; i++) {
hm.put(i+" ",i+" ");
}
});
Thread t2 = new Thread(()->{
for (int i = 25; i < 51; i++) {
hm.put(i+" ",i+" ");
}
});
t1.start();
t2.start();
System.out.println("=============");
//让 main 线程睡一秒中,为了让 t1、t2能把数据全部添加完毕
Thread.sleep(1000);
for (int i = 0; i < 51; i++) {
System.out.println(hm.get(i+" "));
}//0 1 2 3 4 ... 50
}
}
- 运行结果:
6.3 并发工具类-ConcurrentHashMap1.7原理
- 创建对象:
- 1.默认创建一个长度16,加载因子为0.75的大数组。
- 2.还会创建一个长度为2的小数组,把地址值赋值给0索引处,其他索引位置的元素都是null
- 添加数据:
- 为 null:
- 则按照模板创建小数组,创建完毕,会二次哈希,计算出在小数组中应存入的位置,直接存入。
- 如果不为null:
- 就会根据记录的地址值找到小数组 ,二次哈希,计算出在小数组中应存入的位置。如果要扩容,则将小数组扩容两倍,如果不需要扩容,则就会判断小数组这个位置有没有元素,如果没有元素,则直接存,如果有元素,就会调用equals方法,比较属性值,如果equals为true,则不存,如果equals为false,形成哈希桶结构。
- 为 null:
6.4 并发工具类-ConcurrentHashMap1.8原理
- 总结:
- 1,如果使用空参构造创建ConcurrentHashMap对象,则什么事情都不做。 在第一次添加元素的时候创建哈希表。
- 2,计算当前元素应存入的索引。
- 3,如果该索引位置为null,则利用cas算法,将本结点添加到数组中。
- 4,如果该索引位置不为null,则利用volatile关键字获得当前位置最新的结点地址,挂在他下面,变成链表。
- 5,当链表的长度大于等于8时,自动转换成红黑树6,以链表或者红黑树头结点为锁对象,配合悲观锁保证多线程
操作集合时数据的安全性。
6.5 并发工具类-CountDownLatch
-
CountDownLatch类:
-
使用场景:让某一条线程等待其他线程执行完毕之后再执行。
-
代码实现:
package com.hcx.mycountdownlatch;
import java.util.concurrent.CountDownLatch;
public class ChildThread1 extends Thread {
private CountDownLatch countDownLatch;
public ChildThread1(CountDownLatch countDownLatch) {
this.countDownLatch=countDownLatch;
}
@Override
public void run() {
//1.吃饺子
for (int i = 0; i < 10; i++) {
System.out.println(getName()+"在吃第"+ i+ "个饺子");
}
//吃完说一声
//每一次countDown方法的时候,就让计数器减1
countDownLatch.countDown();
}
}
package com.hcx.mycountdownlatch;
import java.util.concurrent.CountDownLatch;
public class ChildThread2 extends Thread {
private CountDownLatch countDownLatch;
public ChildThread2(CountDownLatch countDownLatch) {
this.countDownLatch=countDownLatch;
}
@Override
public void run() {
for (int i = 0; i < 15; i++) {
System.out.println(getName()+"在吃第"+ i+ "个饺子");
}
//吃完说一声
countDownLatch.countDown();
}
}
package com.hcx.mycountdownlatch;
import java.util.concurrent.CountDownLatch;
public class ChildThread3 extends Thread {
private CountDownLatch countDownLatch;
public ChildThread3(CountDownLatch countDownLatch) {
this.countDownLatch=countDownLatch;
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println(getName()+"在吃第"+ i+ "个饺子");
}
//吃完说一声
countDownLatch.countDown();
}
}
package com.hcx.mycountdownlatch;
import java.util.concurrent.CountDownLatch;
public class MotherThread extends Thread {
private CountDownLatch countDownLatch;
public MotherThread(CountDownLatch countDownLatch) {
this.countDownLatch=countDownLatch;
}
@Override
public void run() {
//1.等待
try {
//当计数器变成0的时候,会自动唤醒这里等待的线程。
countDownLatch.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
//2.收拾碗筷
System.out.println("妈妈在收拾碗筷");
}
}
package com.hcx.mycountdownlatch;
import java.util.concurrent.CountDownLatch;
public class MyCountDownLatchDemo {
public static void main(String[] args) {
//1.创建 CountDownLatch的对象,需要传递给四个线程
//在底层就定义了一个计数器,此时计数器的值就是3
CountDownLatch countDownLatch = new CountDownLatch(3);//参数传递的是等待线程的数量
//2.创建四个线程线程对象并开启他们
MotherThread motherThread = new MotherThread(countDownLatch);
motherThread.start();
ChildThread1 t1 = new ChildThread1(countDownLatch);
t1.setName("小明");
ChildThread2 t2 = new ChildThread2(countDownLatch);
t2.setName("小红");
ChildThread3 t3 = new ChildThread3(countDownLatch);
t3.setName("小刚");
t1.start();
t2.start();
t3.start();
}
}
- 总结:
-
- CountDownLatch(int count):参数写等待线程的数量。并定义了一个计数器。
-
- await():让线程等待,当计数器为0时,会唤醒等待的线程
-
- countDown(): 线程执行完毕时调用,会将计数器-1。
-
6.6并发工具类-Semaphore
-
使用场景:
- 可以控制访问特定资源的线程数量。
-
实现步骤:
- 1,需要有人管理这个通道
- 2,当有车进来了,发通行许可证
- 3,当车出去了,收回通行许可证
- 4,如果通行许可证发完了,那么其他车辆只能等着
-
代码实现:
package com.hcx.mysemaphore;
import java.util.concurrent.Semaphore;
public class MyRunnable implements Runnable {
//1.获得管理员对象,
private Semaphore semaphore = new Semaphore(2);//参数为最多允许几个线程同时进行
@Override
public void run() {
//2.获得通行证
try {
semaphore.acquire();//如果线程没有获得通行证,则会一直在这里等着,直到获得通行证为止
//3.开始行驶
System.out.println("获得了通行证开始行驶");
Thread.sleep(2000);
System.out.println("归还通行证");
semaphore.release();
} catch (InterruptedException e) {
e.printStackTrace();
}
//4.归还通行证
}
}
package com.hcx.mysemaphore;
public class MySemaphoreDemo {
public static void main(String[] args) {
MyRunnable myRunnable = new MyRunnable();
for (int i = 0; i < 100; i++) {
Thread thread = new Thread(myRunnable);
thread.start();
}
}
}
- 运行结果:
总结
本篇文章介绍了并发工具类 Hashtable、ConcurrentHashMap、 CountDownLatch、Semaphore的基本使用,并讲解了ConcurrentHashMap1.7版本以及1.8版本的底层原理。希望大家多多支持!