GVKun编程网logo

JAVA多线程并发容器(java多线程并发容器是什么)

22

在这里,我们将给大家分享关于JAVA多线程并发容器的知识,让您更了解java多线程并发容器是什么的本质,同时也会涉及到如何更有效地Java多线程概念[同步容器,并发容器](二)、JAVA多线程的并发控

在这里,我们将给大家分享关于JAVA多线程并发容器的知识,让您更了解java多线程并发容器是什么的本质,同时也会涉及到如何更有效地Java 多线程概念 [同步容器,并发容器](二)、JAVA 多线程的并发控制 | java 多线程并发实例、Java 多线程,同步容器与并发容器、Java多线程-并发容器的内容。

本文目录一览:

JAVA多线程并发容器(java多线程并发容器是什么)

JAVA多线程并发容器(java多线程并发容器是什么)

1、ArrayList线程不安全;copyOnWriteArrayList线程安全
package concurrent;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.copyOnWriteArrayList;

/**
 * Auth: zhouhongliang
 * Date:2019/8/1
 * copyOnWriteArrayList 替代 ArrayList
 */
public class copyOnWriteArrayListDemo {
    public static void main(String[] args) {
        List<Integer> list = new copyOnWriteArrayList<>();
        //List<Integer> list = new copyOnWriteArrayList<>();
        for (int i=0;i<1000;i++){
            list.add(i);
        }
        Iterator iterator = list.iterator();
        while(iterator.hasNext()){
            list.remove(iterator.next());
        }
        System.out.println(list);
    }
}

2、HashMap线程不安全;HashTable synchronize线程安全、```
ConcurrentHashMap采用”分段锁”线程安全;
package concurrent;

import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**

  • Auth: zhouhongliang
  • Date:2019/8/1*/public class ConcurrentHashMapDemo {public static void main(String[] args) throws InterruptedException {Map map = new HashMap();//线程不安全//Map map = new Hashtable();//线程安全//Map map = new ConcurrentHashMap();//线程安全ExecutorService executorService = Executors.newCachedThreadPool();CountDownLatch countDownLatch = new CountDownLatch(10000);for (int i=0;i<10000;i++){final Integer index = i;executorService.execute(()->{map.put(index,index);countDownLatch.countDown();});}countDownLatch.await();System.out.println(map.size());}}

Java 多线程概念 [同步容器,并发容器](二)

Java 多线程概念 [同步容器,并发容器](二)

同步容器

同步容器通过 synchronized 关键字修饰容器,保证同一时刻只有一个线程使用容器,从而使容器线程安全. synchronized 的意思的同步.

1.Vector 和 ArrayList 都实现了 List 接口,Vector 对数组的操作和 ArrayList 都一样,区别在于 Vector 在可能出现线程安全的方法上都加了 synchronized 关键字修饰.

2.Stack 是 Vector 的子类,Stack 实现的是先进后出,在出栈入栈都进行了 synchronized 修饰.

3.HashTable: 它实现了 Map 接口,操作和 HashMap 一样 (区别:HashTable 不能存 null,HashMap 键值都可以为 null),HashTable 的所有操作都加了 synchronized 修饰.

4.Collections 提供了线程同步集合类

List list=Collections.synchronizedList(new ArrayList());
Set set=Collections.synchronizedSet(new HashSet());
Map map=Collections.synchronizedMap(new HashMap());

并发容器

并发容器是指允许多线程访问的容器,并保证线程安全。为了尽可能提高并发,Java 并发工具包中采用多种优化方式来提高并发容器的执行效率,核心就是锁,CAS (无锁),COW (读写分离), 分段锁.

1.CopyOnWriteArrayList

CopyOnWriteArrayList 相当于实现了线程安全的 ArrayList, 在对容器写入时,Copy 出一份副本数组,完成操作后把副本数组的引用赋值给容器,底层是通过 ReentrantLock 来保证同步。但它通过牺牲容器的一致性来换取容器的并发 (在 Copy 期间读取的还是旧数据), 所以不能在强一致的场景下使用.

2.CopyOnWriteArraySet

CopyOnWriteArraySet 和 CopyOnWriteArrayList 的原理一样,它是实现了 CopyOnWrite 机制的 Set 集合.

3.ConcurrentHashMap

ConcurrentHashMap 相当于实现了线程安全的 HashMap,Key 是无序的,并且 key 和 value 都不能为 null, 在 JDK8 之前,采用分段锁的机制来提高并发,只有在操作同一段键值对是才需要加锁.JDK8 以后才用 CAS 算法提高容器的并发.

4.ConcurrentSkipListMap

ConcurrentSkipListMap 相当于实现了线程安全的 TreeMap,key 是有序的,key 和 value 不允许为 null, 它采用跳跃表的来替代红黑树,原因是红黑树在插入或者删除节点时需要做旋转调整,导致要控制的粒度太大。而跳跃表使用的是链表,利用 CAS 算法实现高并发线程安全.

5.ConcurrentSkipListSet

ConcurrentSkipListSet 和 ConcurrentListMap 的原理一样,它是实现了线程安全的 TreeSet

强一致性

系统中某个数据更新后,后续任何对该数据的读取都将获取到最新的值,在任意时刻,所有节点中的数据是一样的。对于关系型数据库,要求更新过的数据能被后续的访问都能看到,这是强一致性。

弱一致性

系统中某个数据被修改后,后续对该数据的读取有可能获得更新之后的值,可能获得更新前的数据,但经过不一致的窗口这段时间,后续对该数据的读取将获得更改之后的值.

最终一致性

是弱一致性的特殊形式,存储系统在保证没有更新的情况下,最总所有对该数据的访问都会得到更新后的数据。不保证在任意时刻任意节点上的同一份数据都是相同的,但是随着时间的迁移,不同节点上的同一份数据总是在向趋同的方向变化。

JAVA 多线程的并发控制 | java 多线程并发实例

JAVA 多线程的并发控制 | java 多线程并发实例

高春辉、王春生、朱峰:关于开源创业的 15 件小事

java 的多线程实现主要有两种,一种是继承 Thread,一种是实现 Runnable 接口,这个是 java 最基本的多线程知识。这里要补充一下,runnable 接口中的 run 方法是不返回任何内容的,如果想返回一个对象可以试试用 concurrent 包中的 Callable 接口来替换 runable 接口的实现 Executor.submit (Callable instance) 将返回一个 Futrue<?> 实例.

这里举个简单的例子

线程类要实现

public class ReadInforWork implements Callable<HashMap>

其中执行方法

    @Override
    public HashMap call() throws Exception {
        try {
            long time1=new java.util.Date().getTime();
            XXXX
            long time2=new Date().getTime();
            System.out.println(time2+" "+time1+" "+(time2-time1)+" "+reuslt.size());
            return reuslt;
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return null;
    }

主线程使用如下方法调用,其中 get 就是拿到 Future 对象

 

for(int i=0;i<read_info_thread_num;i++){
            ReadInforWork readInforWork=new ReadInforWork(read_info_thread_num,i,read_info_total_num);
            FutureTask<HashMap> readWorkThread = new FutureTask<HashMap>(readInforWork);
            new Thread(readWorkThread).start();
            HashMap result =readWorkThread.get();
            System.out.println(result.size());
        }

另外,还可以使用 ExecutorServcie 来实现。这里也是简单的举个例子

主线程调用方法为

     

  ExecutorService executorService= Executors.newCachedThreadPool();
        CompletionService<HashMap> completionService=new ExecutorCompletionService<HashMap>(executorService);
        for(int i=0;i<read_info_thread_num;i++){
            ReadInforWork readInforWork=new ReadInforWork(read_info_thread_num,i,read_info_total_num);
            completionService.submit(readInforWork);
        }
        for(int i=0;i<read_info_thread_num;i++){
           HashMap result=completionService.take().get();
           System.out.println(result.size());
        }

使用 ExecutorService 后就不需要在 main 函数中控制线程的结束时间了,如果不使用这个类,可以使用 concurrent 中的 countdownLunch


这里是执行线程的写法,在执行完动作后操作下对应的 countdownlunch 即可

 

@Override
    public void run() {
        try {
            long time1=new java.util.Date().getTime();
            XXXX
            long time2=new Date().getTime();
            System.out.println(time2+" "+time1+" "+(time2-time1)+" "+reuslt.size());
            countDownLatch.countDown();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

主函数写法如下

 CountDownLatch read_info_countdownlatch=new CountDownLatch(read_info_thread_num);
 read_info_countdownlatch.await();

另外还有一种写法是利用 cyclickBarrier  参考文档 http://blog.csdn.net/xiao__gui/article/details/9213413


Java 多线程,同步容器与并发容器

Java 多线程,同步容器与并发容器

一、为什么这种方式不能实现线程安全性?
分析一段代码:

package com.guor.util;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class ListHelper<E> {

public List<E> list = Collections.synchronizedList(new ArrayList<E>());

public synchronized boolean putIfAbsent(E x){
    boolean absent = !list.contains(x);
    if(absent){
        list.add(x);
    }
    return absent;
}

}
毕竟putIfAbsent已经声明了synchronized 类型的变量,对不对?问题在于在错误的锁上进行了同步。无论list使用哪一个锁来保护它的状态,可以确定的是,这个锁并不是ListHelper上的锁。ListHelper只是带来了同步的假象,尽管所有的链表操作都被声明为synchronized,但却使用了不同的锁,这意味着putIfAbsent相对于List的其它操作来说并不是原子的,因此就无法确保当putIfAbsent执行时另一个线程不会修改链表。
要想使这个方法能正确执行,必须使List在实现客户端加锁或外部加锁时使用同一个锁。客户端加锁是指,对于使用某个对象X的客户端代码,使用X本身用于保护器状态的锁来保护这段客户端代码。要使用客户端加锁,你必须知道对象X使用的是哪一个锁。
在Vector和同步封装器的文档中指出,它们通过使用Vector或封装器的内置锁来支持客户端加锁,下面代码可以实现线程安全的list操作。

public boolean putIfAbent(E x){

synchronized (list){
    boolean absent = !list.contains(x);
    if(absent){
        list.add(x);
    }
    return absent;
}

}

二、组合
当为现有的类添加一个原子操作时,有一种更好的方法:组合。
下面代码中ImprovedList通过将List对象的操作委托给底层的List实例来实现List的操作,同时还添加了一个原子的putIfAbsent方法。(与Collections.synchronizedList和其它容器封装器一样,ImprovedList假设把某个链表对象传给构造函数以后,客户代码不会再直接使用这个对象,而只能通过ImprovedList来访问它。)

package com.guor.util;

import java.util.List;

public class ImprovedList<T> implements List<T> {

private final List<T> list;

public ImprovedList(List<T> list){
    this.list = list;
}

public synchronized boolean putIfAbsent(T x){
    boolean absent = !list.contains(x);
    if(absent){
        list.add(x);
    }
    return absent;
}

public synchronized void clear(){
    list.clear();
}

...

}

ImprovedList通过自身的内置锁增加了一层额外的加锁,它并不关心底层的list是否是线程安全的,即使List不是线程安全的或者修改了它的加锁实现,ImprovedList也会提供一致的加锁机制来实现线程安全性。虽然额外的同步层可能导致轻微的性能损失,但与模拟另一个对象的加锁策略相比,ImprovedList更为健壮。事实上,我们使用了Java监视器模式来封装现有的List,并且只要在类中拥有指向底层List的唯一外部引用,就能确保线程安全性。

三、同步容器类
同步容器类包括Vector和Hashtable,这些实现线程安全的方式是:将它们的状态封装起来,并对每个公有方法都进行同步,使得每次只有一个线程能访问容器的状态。
同步容器类都是线程安全的,但在某些情况下可能需要额外的客户端加锁来保护符合操作。容器上常见的复合操作包括:迭代、跳转以及条件运算,例如“若没有则添加”。在同步容器类中,这些符合操作在没有客户端加锁的情况下仍然是线程安全的,但当其他线程并发地修改容器时,它们可能会表现出意料之外的行为。

四、隐藏迭代器
虽然加锁可以防止迭代器抛出ConcurrentModificationException,但你必须要记住所有对共享容器进行迭代的地方都需要加锁。实际情况要更加复杂,因为在某些情况下,迭代器会隐藏起来。比如log.info("set content is :"+set),编译器将字符串的连接操作转换为调用StringBuilder.append(Object),而这个方法又会调用容器的toString方法,标准容器的toString方法将迭代容器,并在每个元素上调用toString来生成容器内容的格式化表示。
容器的hashCode和equals等方法也会间接地执行迭代操作,当容器作为另一个容器的元素和键值时,就会出现这种情况。

五、并发容器
jdk1.5提供了多种并发容器来改进同步容器的性能。同步容器将所有对容器状态的访问都串行化,以实现它们的线程安全性。这种方法的代价是严重降低并发性,当多个线程竞争容器的锁时,吞吐量将严重降低。
另一方面,并发容器是针对多个线程并发访问设计的。在jdk1.5中增加了ConcurrentHashMap,用来替代同步且基于散列的Map以及CopyOnWriteArrayList,用于在遍历操作为主要操作的情况下代替同步的List。在新的ConcurrentMap接口中增加了对一些常见复合操作的支持,例如“若没有则添加”、替换以及有条件删除等。
通过并发容器来代替同步容器,可以极大地提高伸缩性并降低风险。
jdk1.5增加了两种新的容器类型:Queue和BlockingQueue。Queue用来临时保存一组等待处理的元素。它提供了几种实现,包括ConcurrentLinkedQueue,这是一个传统的先进先出队列,以及PriorityQueue,这是一个非并发的优先队列。Queue上的操作不会阻塞,如果队列为空,那么获取元素的操作将返回空值。虽然可以用List来模拟Queue的行为,事实上,正是通过LinkedList来实现Queue的,但还需要一个Queue的类,因为它能去掉List的随机访问需求,从而实现更高效的并发。
BlockingQueue扩展了Queue,增加了可阻塞的插入和获取等操作,如果队列为空,那么获取元素的操作将一直阻塞,直到队列中出现一个可用的元素。如果队列已满,那么插入元素的操作将一直阻塞,直到队列中出现可用的空间。
同步容器在执行每个操作期间都持有一个锁。

六、ConcurrentHashMap
与HashMap一样,ConcurrentHashMap也是一个基于散列的Map,但它使用了一种完全不同的加锁策略来提供更高的并发性和伸缩性。ConcurrentHashMap并不是将每个方法都在同一个锁上同步并使得每次只能有一个线程访问容器,而是使用一种更细的加锁机制来实现更大程度的共享,这种机制成为分段锁。在这种机制中,任意数量的读取线程可以并发地访问Map,执行读取操作的线程和执行写入操作的线程可以并发地访问Map,并且一定数量的写入线程可以并发地修改Map。ConcurrentHashMap带来的结果是,在并发访问环境下将实现更高的吞吐量,而在单线程环境中只损失非常小的性能。
ConcurrentHashMap返回的迭代器具有弱一致性,而并非“及时失败”。弱一致性的迭代器可以容忍并发的修改,当创建迭代器时会遍历已有的元素,并可以在迭代器被构造后将修改操作反映给容器。

Java多线程-并发容器

Java多线程-并发容器

Java多线程-并发容器

         在Java1.5之后,通过几个并发容器类来改进同步容器类,同步容器类是通过将容器的状态串行访问,从而实现它们的线程安全的,这样做会消弱了并发性,当多个线程并发的竞争容器锁的时候,吞吐量会下降。那并发容器是为多线程并发而设计的。那么java1.5中添加了哪些并发容器?

      1、ConCurrentHashMap  来替代同步的HashMap 实现     

      2、CopyOnWriteArrayList  是List的同步实现
      3、Queue 和 BlockQueue  接口     

     4、ConCurrentLinkedQueue  一个并发优先级的队列注意: java6中加入ConCurrentSkipListMap 和 ConCurrentSkipListSet 用来作为同步的SortedMap 和SortedSet ..


二、ConCurrentHashMap     

 

我们都知道同步容器类每当执行一个操作的时候都会持有一个锁,如果同步容器中非常大,比如遍历查找是否存某个对象,消耗时间非常长,但是这段时间不允许其他线程访问这个容器,这是一种糟糕是事情。  下载地址      ConCurrentHashMap 和HashMap 一样都是一个hash表,但是ConCurrentHashMap与HashMap完全使用不同的锁策略。它可以提供更好的并发性和伸缩性。在ConCurrentHashMap之前,是使用共用一个锁,进行同步每一个方法。并且严格的限制只有一个线程能同时访问容器;而ConcurrentHashMap 使用了更加细粒度的锁,-分离锁( 详情:ReentrantLock)这个锁机制允许更深层的共享访问,任何多线程可以并发的读操作访问此容器,当且仅当只用一个线程可以进行写容器操作。这样读线程和写线程都可以并发的访问容器大大提高了吞吐量,也没有怎么损失单线程的性能。但是,在应用的需求是线程独占访问枷锁的时候,ConCurrentMap是无法胜任的。

 

三、CopyOnWriteArrayList

 

CopyOnWriteArrayList 是 List容器的并发容器的替代品,通常它提供了更好的并发性,避免在容器迭代时候进行加锁和复制。下面是一段源代码

 

Java代码 复制代码 收藏代码

  1. public E set(int index, E element) {  

  2.        final ReentrantLock lock = this.lock;  

  3.        lock.lock();  

  4.        try {  

  5.            Object[] elements = getArray();  

  6.            E oldValue = get(elements, index);  

  7.   

  8.            if (oldValue != element) {  

  9.                int len = elements.length;  

  10.                Object[] newElements = Arrays.copyOf(elements, len);  

  11.                newElements[index] = element;  

  12.                setArray(newElements);  

  13.            } else {  

  14.                // Not quite a no-op; ensures volatile write semantics  

  15.                setArray(elements);  

  16.            }  

  17.            return oldValue;  

  18.        } finally {  

  19.            lock.unlock();  

  20.        }  

  21.    }  

 public E set(int index, E element) {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            Object[] elements = getArray();
            E oldValue = get(elements, index);

            if (oldValue != element) {
                int len = elements.length;
                Object[] newElements = Arrays.copyOf(elements, len);
                newElements[index] = element;
                setArray(newElements);
            } else {
                // Not quite a no-op; ensures volatile write semantics
                setArray(elements);
            }
            return oldValue;
        } finally {
            lock.unlock();
        }
    }

     在操作之前:lock.lock(); 然后  lock.unlock(); 释放

  

Java代码 复制代码 收藏代码

  1. /** 

  2.    * {@inheritDoc} 

  3.    * 

  4.    * @throws IndexOutOfBoundsException {@inheritDoc} 

  5.    */  

  6.   public E get(int index) {  

  7.       return get(getArray(), index);  

  8.   }  

  /**
     * {@inheritDoc}
     *
     * @throws IndexOutOfBoundsException {@inheritDoc}
     */
    public E get(int index) {
        return get(getArray(), index);
    }

 

   读取不会加锁和释放锁操作。

关于JAVA多线程并发容器java多线程并发容器是什么的介绍现已完结,谢谢您的耐心阅读,如果想了解更多关于Java 多线程概念 [同步容器,并发容器](二)、JAVA 多线程的并发控制 | java 多线程并发实例、Java 多线程,同步容器与并发容器、Java多线程-并发容器的相关知识,请在本站寻找。

本文标签: