java中并发集合

阻塞式集合:这类集合包括添加和移除的数据方法。当集合已满或为空时,被调用的添加或者移除方法就不能立即被执行,那么调用这个方法的线程将被阻塞,一直到该方法可以被成功执行。

非阻塞式集合:这类集合也包括添加和移除的方法,如果方法不能立即被执行,则返回null或抛出异常,但是调用这个方法的线程不会被阻塞。

非阻塞集合:

        ConcurrentLinkedQueue:基于链接节点的无限制线程安全队列,此队列命令元素FIFO(先进先出)。这个队列在add(),remove(),poll()都用了cas来保证安全。在iterator()时,如果集合被改变,那么数据可能会不一致。

例子:

 ConcurrentLinkedQueue c = new ConcurrentLinkedQueue<>();
        c.add("1");
        c.add("2");
        System.out.println(c.poll());
        System.out.println(c.poll());

输出结果:
1
2

import java.util.Iterator;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.TimeUnit;

public class SCollection {
    public static void main(String[] args) {
        final ConcurrentLinkedQueue c = new ConcurrentLinkedQueue<>();
        new Thread(new Runnable() {

            @Override
            public void run() {
                for (int i = 0; i < 10000; i++) {
                    for (int j = 0; j < 10000; j++) {
                        try {
                            TimeUnit.MILLISECONDS.sleep(1);
                        } catch (InterruptedException e) {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                        }
                        c.add(i * 10000 + j + "");
                    }
                }

            }
        }).start();
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        for (int i = 0; i < 3; i++) {
            System.out.println("第"+i+"次:");
            //只返回当前集合的值,如果这期间集合改变,那么数据会不准确
            Iterator iterator = c.iterator();
            while (iterator.hasNext()) {
               
                System.out.println(iterator.next());
            }
        }
    }
}

 

源码:
 

 public boolean offer(E e) {
        checkNotNull(e);
        final Node newNode = new Node(e);

        for (Node t = tail, p = t;;) {
            Node q = p.next;
            if (q == null) {
                // p is last node
                if (p.casNext(null, newNode)) {
                    // Successful CAS is the linearization point
                    // for e to become an element of this queue,
                    // and for newNode to become "live".
                    if (p != t) // hop two nodes at a time
                        casTail(t, newNode);  // Failure is OK.
                    return true;
                }
                // Lost CAS race to another thread; re-read next
            }
            else if (p == q)
                // We have fallen off list.  If tail is unchanged, it
                // will also be off-list, in which case we need to
                // jump to head, from which all live nodes are always
                // reachable.  Else the new tail is a better bet.
                p = (t != (t = tail)) ? t : head;
            else
                // Check for tail updates after two hops.
                p = (p != t && t != (t = tail)) ? t : q;
        }
    }



   public E poll() {
        restartFromHead:
        for (;;) {
            for (Node h = head, p = h, q;;) {
                E item = p.item;

                if (item != null && p.casItem(item, null)) {
                    // Successful CAS is the linearization point
                    // for item to be removed from this queue.
                    if (p != h) // hop two nodes at a time
                        updateHead(h, ((q = p.next) != null) ? q : p);
                    return item;
                }
                else if ((q = p.next) == null) {
                    updateHead(h, p);
                    return null;
                }
                else if (p == q)
                    continue restartFromHead;
                else
                    p = q;
            }
        }
    }

 public boolean remove(Object o) {
        if (o == null) return false;
        Node pred = null;
        for (Node p = first(); p != null; p = succ(p)) {
            E item = p.item;
            if (item != null &&
                o.equals(item) &&
                p.casItem(item, null)) {
                Node next = succ(p);
                if (pred != null && next != null)
                    pred.casNext(p, next);
                return true;
            }
            pred = p;
        }
        return false;
    }

ConcurrentLinkedDeque:基于链接节点的无界并发的双端队列。并发插入,删除和访问操作安全执行。因为这些deques的异步性质,确定当前的数量,元素需要遍历元素,因此可以报告,如果在遍历期间修改此集合,则结果不准确。也是使用cas来保证线程安全,这个类不仅可以操控头部,也可以操控尾部。

阻塞集合:

     ArrayBlockingQueue:有界的阻塞队列基于数组,此队列命令元素FIFO(先进先出)。一开就要确定容器,确定后无法修改。线程安全基于ReentrantLock。

   LinkedBlockingDeque:一个可选的有界阻塞双端队列基于链接节点。此队列命令元素FIFO(先进先出)

    LinkedBlockingQueue:一个可选的有界阻塞队列基于链接节点。此队列命令元素FIFO(先进先出)。链接队列通常比基于阵列的队列具有更高的吞吐量,但是大多数并发应用程序的性能预测性较差。

take()和push方法分析:

   
    public void put(E e) throws InterruptedException {
        if (e == null) throw new NullPointerException();
        // Note: convention in all put/take/etc is to preset local var
        // holding count negative to indicate failure unless set.
        int c = -1;
        Node node = new Node(e);
        final ReentrantLock putLock = this.putLock;
        final AtomicInteger count = this.count;
        putLock.lockInterruptibly();
        try {
            /*
             * Note that count is used in wait guard even though it is
             * not protected by lock. This works because count can
             * only decrease at this point (all other puts are shut
             * out by lock), and we (or some other waiting put) are
             * signalled if it ever changes from capacity. Similarly
             * for all other uses of count in other wait guards.
             */
            while (count.get() == capacity) {
                //如果容量满了 就将线程阻塞
                notFull.await();
            }
            enqueue(node);
            //得到之前的count的值 
            c = count.getAndIncrement();
            if (c + 1 < capacity)
                notFull.signal();
        } finally {
            putLock.unlock();
        }
        if (c == 0)
            //如果之前的容量为0 就唤醒线程
            signalNotEmpty();
    }
  public E take() throws InterruptedException {
        E x;
        int c = -1;
        final AtomicInteger count = this.count;
        final ReentrantLock takeLock = this.takeLock;
        takeLock.lockInterruptibly();
        try {
            //如果为0 就调用await的方法 当前方法阻塞
            while (count.get() == 0) {
                notEmpty.await();
            }
            x = dequeue();
            c = count.getAndDecrement();
            if (c > 1)
                notEmpty.signal();
        } finally {
            takeLock.unlock();
        }
        if (c == capacity)
            //如果之前容量满了,就唤醒notfull
            signalNotFull();
        return x;
    }



   private void signalNotFull() {
        final ReentrantLock putLock = this.putLock;
        putLock.lock();
        try {
            //唤醒
            notFull.signal();
        } finally {
            putLock.unlock();
        }
    }

  private void signalNotEmpty() {
        final ReentrantLock takeLock = this.takeLock;
        takeLock.lock();
        try {
            notEmpty.signal();
        } finally {
            takeLock.unlock();
     

PriorityBlockingQueue: 使用的无界阻塞队列基于heap实现。虽然这个队列是逻辑上的无限制,尝试添加可能会因资源耗尽而失败。所有添加进PriorityBlockingQueue的元素必须实现Comparable接口。

  PriorityBlockingQueue pbq = new PriorityBlockingQueue<>();
        pbq.add(5);
        pbq.add(3);
        System.out.println(pbq.peek());

输出结果:

 3。

import java.util.concurrent.PriorityBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class SPriorityBlockingQueue {
    public static void main(String[] args) {
        ThreadPoolExecutor tpe = new ThreadPoolExecutor(1, 1,60, TimeUnit.SECONDS, new PriorityBlockingQueue<>());
        for(int i=6;i>1;i--){
            tpe.execute(new SStudent(i));   
        }
    }
}

class SStudent implements Comparable,Runnable{

    private Integer age;
    

    public SStudent(Integer age) {
        super();
        this.age = age;
    }


    @Override
    public int compareTo(SStudent o) {
        if(age > o.getAge()){
            return 1;
        }
        else if(age < o.getAge()){
            return -1;
        }
        return 0;
    }


    public Integer getAge() {
        return age;
    }


    public void setAge(Integer age) {
        this.age = age;
    }


    @Override
    public void run() {
        System.out.println("age:"+age);
        
    }
     
}

输出结果:
age:6
age:2
age:3
age:4
age:5

第一次需要创建线程所以直接执行了,下面进行加入队列,只要实现了Comparable接口,PriorityBlockingQueue会自动排序。

DelayQueue: 无限制的阻塞队列,其中只能使用元素延迟期满时。如果没有延迟到期,则没有头将返回。当元素出现时到期
方法返回的值较少大于或等于零。

存放到DelayQueue类中的元素必须继承Delayed接口。

例子:

import java.util.Date;
import java.util.concurrent.DelayQueue;
import java.util.concurrent.Delayed;
import java.util.concurrent.TimeUnit;

public class SCollection2 {
    public static void main(String[] args) throws Exception{
       DelayQueue dq = new DelayQueue<>();
       Date now = new Date();
       now.setSeconds(now.getSeconds()+5);
   
       long date1l = now.getTime();
       now.setSeconds(now.getSeconds()+5);
       
       long date2l = now.getTime();
       dq.add(new DTime(new Date(date2l),"date2"));
       dq.add(new DTime(new Date(date1l),"date1"));
       System.out.println("date1l: " +date1l);
       System.out.println("date2l: "+date2l);
       System.out.println("first:"+dq.take().getName());
       
    }
}

class DTime implements Delayed{

    private Date myTime;
    
    private String name;
    
    
    public DTime(Date myTime,String name) {
        super();
        this.myTime = myTime;
        this.name = name;
    }

    @Override
    public int compareTo(Delayed o) {
        TimeUnit unit = TimeUnit.NANOSECONDS;
        if(this.getDelay(unit) > o.getDelay(unit)){
            return 1;
        }else if(this.getDelay(unit) < o.getDelay(unit)){
            return -1;
        }
        return 0;
    }
    
    //一定要返回NANOSECONDS
    @Override 
    public long getDelay(TimeUnit unit) {
       long diff = myTime.getTime() - new Date().getTime();
       return unit.convert(diff, TimeUnit.MILLISECONDS);
      
    }

    public Date getMyTime() {
        return myTime;
    }

    public void setMyTime(Date myTime) {
        this.myTime = myTime;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
    
}

源码分析:
 

public class DelayQueue extends AbstractQueue
    implements BlockingQueue {

    private final transient ReentrantLock lock = new ReentrantLock();
    //里面有PriorityQueue 这个优先级队列
    private final PriorityQueue q = new PriorityQueue();
    .....

}

 public boolean offer(E e) {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
           //把添加的元素 加到这个优先级队列里 让优先级队列进行排序
            q.offer(e);
            if (q.peek() == e) {
                leader = null;
                available.signal();
            }
            return true;
        } finally {
            lock.unlock();
        }


    public E take() throws InterruptedException {
        final ReentrantLock lock = this.lock;
        lock.lockInterruptibly();
        try {
            for (;;) {
                //获取第一个元素
                E first = q.peek(); 
                if (first == null)
                    available.await();
                else {
                   //得到第一个的延时时间
                    long delay = first.getDelay(NANOSECONDS);
                    //如果小于0 就返回
                    if (delay <= 0)
                        return q.poll();
                    first = null; // don't retain ref while waiting
                    if (leader != null)
                        available.await();
                    else {
                        Thread thisThread = Thread.currentThread();
                        leader = thisThread;
                        try {
                           //线程挂起,这边等待延时时间 这边是Nanos
                            available.awaitNanos(delay);
                        } finally {
                            if (leader == thisThread)
                                leader = null;
                        }
                    }
                }
            }
        } finally {
            if (leader == null && q.peek() != null)
                available.signal();
            lock.unlock();
        }
    }

 SynchronousQueue :每个插入的操作必须等待另一个操作的相应删除操作线程,反之亦然。同步队列没有任何内部容量,甚至不是一个容量。你不能{@code peek}在同步队列中,因为元素只是当你试图删除它时出现;你不能插入一个元素(使用任何方法),除非另一个线程试图删除它;你不能迭代,因为没有什么可以迭代。这个用来管理线程的。

当使用put()时,如果数据没被取走,那么该线程会一直阻塞。直到被take()才会被释放。offer()只有插入的时候正好被调用才会插入成功,一般会有一个线程在调用take()或poll()方法来等待,不然offer一般都会失败返回null。不要用add()方法 ,一般都会抛出异常。

 

例子。

public class SynchronousQueueTest {
    public static void main(String[] args) {
                //true保证生产或消费者线程以FIFO的顺序访问。
        SynchronousQueue queue = new SynchronousQueue(true);
      
            new Customer(queue).start();
        
        for (int i = 0; i < 3; ++i) {
            new Product(queue).start();
        }
    }
    static class Product extends Thread {
        SynchronousQueue queue;
        public Product(SynchronousQueue queue) {
            this.queue = queue;
        }
        @Override
        public void run() {
            while (true) {
                int rand = new Random().nextInt(1000);
                System.out.println("Thread Id:" + getId() + "  生产了一个产品:" + rand);
            
             
                /*
                 * offer()往queue里放一个element后立即返回,如果碰巧这个element被另一个thread取走了,
                 * offer方法返回true,认为offer成功;否则返回false。
                 * 也就是说offer不一定真正的插入的队列中,肯定没成功丢失了
                 */
                
                // queue.offer(rand);  //注意offer与put方法的区别
                try {
                    /* 
                     * put()往queue放进去一个element以后就一直wait直到有其他thread进来把这个element取走。
                     */
                    TimeUnit.SECONDS.sleep(2);
                    queue.put(rand);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    static class Customer extends Thread {
        SynchronousQueue queue;
        public Customer(SynchronousQueue queue) {
            this.queue = queue;
        }
        @Override
        public void run() {
            while (true) {
                try {
                    // 线程运行到queue.take()阻塞,直到Product生产一个产品queue.offer。
                    System.out.println("Thread Id:" + getId() + " 消费了一个产品:" + queue.take());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("------------------------------------------");
            }
        }
    }
}

newCachedThreadPool的分析:

    //使用了SynchronousQueue队列来管理线程
    public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue());
    }

   public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();
        /*
         * Proceed in 3 steps:
         *
         * 1. If fewer than corePoolSize threads are running, try to
         * start a new thread with the given command as its first
         * task.  The call to addWorker atomically checks runState and
         * workerCount, and so prevents false alarms that would add
         * threads when it shouldn't, by returning false.
         *
         * 2. If a task can be successfully queued, then we still need
         * to double-check whether we should have added a thread
         * (because existing ones died since last checking) or that
         * the pool shut down since entry into this method. So we
         * recheck state and if necessary roll back the enqueuing if
         * stopped, or start a new thread if there are none.
         *
         * 3. If we cannot queue task, then we try to add a new
         * thread.  If it fails, we know we are shut down or saturated
         * and so reject the task.
         */
        int c = ctl.get();
        if (workerCountOf(c) < corePoolSize) {
            if (addWorker(command, true))
                return;
            c = ctl.get();
        }
        //每次都workQueue.offer 判断是否有空闲的线程
        //因为空闲线程会调用take()或poll(keepalivetime.unit)方法等待任务的到来
        //如果添加失败说明没有线程空闲就会添加新的线程
        if (isRunning(c) && workQueue.offer(command)) {
            int recheck = ctl.get();
            if (! isRunning(recheck) && remove(command))
                reject(command);
            else if (workerCountOf(recheck) == 0)
                addWorker(null, false);
        }
        else if (!addWorker(command, false))
            reject(command);
    }

LinkedTransferQueue:一个无边界的阻塞队列,基于链表实现。这个主要有transfer方法,如果加入的数据没有被消费掉,当前线程会一直阻塞。 tryTransfer(e, timeout, unit) 在一段时间内如果没有被获取就返回false, transfer加入的数据都是在链表尾部,如果队列之前有数据,task会先取之前的数据。

列子:

import java.util.concurrent.LinkedTransferQueue;

public class SLinkedTransferQueue {
    public static void main(String[] args) {
        LinkedTransferQueue ltq = new LinkedTransferQueue<>();     
        new Thread(){
            public void run() {
                try {
                    System.out.println("开始transfer");
                    ltq.transfer("1");
                    System.out.println("结束transfer");
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            };
        }.start();
         
        new Thread(){
            public void run() {
                try {
                    Thread.sleep(3000);
                    System.out.println("开始take");
                    System.out.println("value:"+ltq.take());
                    System.out.println("结束take");
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            };
        }.start();
    }
}

 

你可能感兴趣的:(java,多线程)