ConcurrentHashMap
HashMap不安全, ConcurrentHashMap和HashTable线程安全。 与HashTable不同,ConcurrentHashMap不依赖于synchronization去保证线程操作的安全。
所以ConcurrentHashMap可以保证线程安全(多个线程操作同一个Segment,则某个线程给此Segment加锁,另一个线程只能阻塞)并且在一定程度上提交线程并发执行效率。 数组+链表+红黑树 +乐观锁 + synchronized
Synchronized就是悲观锁 ConcurrentHashmap和HashMap区别: 1.HashMap的key 和value均可以为null;而HashTable和Concur rentHashMap的key和value均不可以为null 2.HashTable 和ConcurrentHashMap的区别:保证线程安全的方式不同: 2.1.HashTable是通过给整张散列表加锁的方式来保证线程安全,这种方式保证了线程安全,但是并发执行效率低下。 2.2.ConcurrentHashMap在JDK1.8之前,采用分段锁机制来保证线程安全的,这种方式可以在保证线程安全的同时,一定程度上提高并发执行效率(当多线程并发访问不同的segment时,多线程就是完全并发的,并发执行效率会提高) 2.3.从JDK1.8开始, ConcurrentHashMap数据结构与1.8中的HashMap保持一致,均为数组+链表+红黑树,是通过乐观锁+Synchronized来保证线程安全的.当多线程并发向同一个散列桶添加元素时。若散列桶为空,此时触发乐观锁机制,线程会获取到桶中的版本号,在添加节点之前,判断线程中获取的版本号与桶中实际存在的版本号是否一致,若一致,则添加成功,若不一致,则让线程自旋。
ReEntryLock
可重入是某个县城已经获得锁,在该线程未解锁前有在此获取到此锁而不出现死锁。 可重入锁, 重复获取而不出现死锁现象。 Sychronized
public class ReentryLock1 {
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
int i = 1;
synchronized (this) {
System.out.println("第" + i + "次获取锁,这个锁是:" + this);
for (i = 2; i < 10; i++) {
synchronized (this) {
System.out.println("第" + i + "次获取锁,这个锁是:" + this);
}
}
}
}
}).start();
}
}
public class ReentryLock2 {
public static void main(String[] args) {
ReentrantLock lock = new ReentrantLock();
new Thread(new Runnable() {
@Override
public void run() {
try {
int i = 1;
lock.lock();
System.out.println("第" + i + "次获取锁,这个锁是:" + lock);
for (i = 2; i < 10; i++) {
try {
lock.lock();
System.out.println("第" + i + "次获取锁,这个锁是:" + lock);
} finally {
lock.unlock();
}
}
} finally {
lock.unlock();
}
}
}).start();
}
}
CountDownLock
程计数器 用于线程执行任务,计数 等待线程结束
CountDownLatch(int count):构造方法,创建一个值为count 的计数器。
· await():阻塞当前线程,将当前线程加入阻塞队列。
· await(long timeout, TimeUnit unit):在timeout的时间之内阻塞当前线程,时间一过则当前线程可以执行,
· countDown():对计数器进行递减1操作,当计数器递减至0时,当前线程会去唤醒阻塞队列里的所有线程。
使用情况
当为统计指标多
当子线程都执行完之后再对每个线程计算。
例子
public class CountDownLatchTest2 {
public static void main(String[] args) {
//模拟线程数
int times = 1000;
//用于模拟线程安全
final AtomicInteger atomicInteger = new AtomicInteger(0);
//用于模拟线程非安全
final Member member = new Member();
//相当于计数器,当所有线程都准备好了,再一起执行,模仿多并发,保证并发量
final CountDownLatch countDownLatch = new CountDownLatch(times);
//保证所有线程执行完了再打印atomicInteger、用户年龄的值
final CountDownLatch countDownLatch2 = new CountDownLatch(times);
ExecutorService executorService = Executors.newFixedThreadPool(10);
try {
for (int i = 0; i < times; i++) {
executorService.submit(() -> {
try {
//一直阻塞当前线程,直到计时器的值为0,保证同时并发
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("当前线程执行时间:" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS").format(new Date()));
//数量自增1:线程安全(执行n次,每次都是1000)
atomicInteger.incrementAndGet();
//用户年龄自增1:线程不安全(执行n次,每次结果可能不一样)
member.setAge(member.getAge() + 1);
//当前线程执行完,计数器减一
countDownLatch2.countDown();
});
countDownLatch.countDown();
}
//保证所有线程执行完
countDownLatch2.await();
//打印结果
System.out.println("atomicInteger = " + atomicInteger);
System.out.println("age = " + member.getAge());
executorService.shutdown();
} catch (Exception e) {
e.printStackTrace();
}
}
}
public class Member {
private int age;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
atomicInteger = 1000
age = 995