Java Basic (ConcurrentHashMap+ReEntryLock+CountDownLock)

121 阅读2分钟

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