Java7线程学习笔记(六)

并发集合

当需要在并发程序中使用数据集合时,必须要谨慎的选择相应的实现方式。大多数集合类不能直接用于并发应用,因为他们没有对本身数据的并发访问进行控制。如果一些并发任务共享了一个不适用于并发任务的数据结构,将会遇到数据不一致的错误,并将影响程序的准确运行。这类数据结构的一个例子是arrayList类。
一般来说,Java提供了两类适用于并发应用的集合

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

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

非阻塞式列表对应的实现类:ConcurrentLinkedDeque类。
阻塞式列表对应的实现类:LinkedblockingDeque类。
用于数据生成或消费的阻塞式列表对应的实习类:LinkedTransferQueue类。
按优先级排序列表元素的阻塞式列表对应的实现类:PriorityBlockingQueue类。
带有延迟列表元素的阻塞式列表对应的实现类:DelayQueue类。
非阻塞式可遍历映射对应的实现类:ConcurrentSkipListMap类。
随机数字对应的实现类:ThreadLocalRandom类。
原子变量对应的实现类:AtomicLong和AtomicIntegerArray类。

使用非阻塞式线程安全列表:ConcurrentLinkedDeque

ConcurrentLinkedDeque<String> list=new ConcurrentLinkedDeque<>();

list.add(name+": Element "+i);

list.pollFirst();移除,如果列表为空,则返回null
list.pollLast();

Size()方法返回的值不一定是真实的,仅当没有任何线程修改列表时才能返回正确的结果。

getFirst()和getLast()。如果列标为空,则抛出NoSuchElementException异常。

Peek()、peekFirst()、peekLast(),如果列表为空,这些方法返回null,不会移除元素。

Remove、removeFirst()、removeLast(),如果列表为空这些方法返回null

使用阻塞式线程安全列表:LinkedBlockingDeque
LinkedBlockingDeque list=new LinkedBlockingDeque<>(3);
list.put(request.toString());如果线程已满,调用这个方法的线程将被阻塞直到列标中有可用的空间。
String request=list.take();如果列标为空,调用这个方法的线程将被阻塞直到列表不为空
getFirst()、getLast()抛出NoSuchElementException异常。
使用按优先级排序的阻塞式线程安全列表:PriorityBlockingQueue
所有添加进PriorityBlockingQueue的元素必须实现Comparable接口。这个接口提供了compareTo()方法。使用compareTo()芳芳来决定插入元素的位置。
PriorityBlockingQueue的另一个重要的特性是:他是阻塞式数据结构。

package com.bh.recipe3.task;

/**
 * This class stores the attributes of an event. Its thread
 * and is priority. Implements the comparable interface to
 * help the priority queue to decide which event has more priority 
 *
 */
public class Event implements Comparable<Event> {

    /**
     * Number of the thread that generates the event
     */
    private int thread;
    /**
     * Priority of the thread
     */
    private int priority;

    /**
     * Constructor of the thread. It initializes its attributes
     * @param thread Number of the thread that generates the event
     * @param priority Priority of the event
     */
    public Event(int thread, int priority){
        this.thread=thread;
        this.priority=priority;
    }

    /**
     * Method that returns the number of the thread that generates the
     * event
     * @return The number of the thread that generates the event
     */
    public int getThread() {
        return thread;
    }

    /**
     * Method that returns the priority of the event
     * @return The priority of the event
     */
    public int getPriority() {
        return priority;
    }

    /**
     * Method that compares two events and decide which has more priority
     */
    @Override
    public int compareTo(Event e) {
        if (this.priority>e.getPriority()) {
            return -1;
        } else if (this.priorityreturn 1; 
        } else {
            return 0;
        }
    }
}
package com.bh.recipe3.task;

import java.util.concurrent.PriorityBlockingQueue;

/**
 * This class implements a generator of events. It generates
 * 1000 events and stores them in a priory queue
 *
 */
public class Task implements Runnable {

    /**
     * Id of the task
     */
    private int id;

    /**
     * Priority queue to store the events
     */
    private PriorityBlockingQueue queue;

    /**
     * Constructor of the class. It initializes its attributes
     * @param id Id of the task 
     * @param queue Priority queue to store the events
     */
    public Task(int id, PriorityBlockingQueue queue) {
        this.id=id;
        this.queue=queue;
    }

    /**
     * Main method of the task. It generates 1000 events and store
     * them in the queue
     */
    @Override
    public void run() {
        for (int i=0; i<1000; i++){
            Event event=new Event(id,i);
            queue.add(event);
        }
    }
}
package com.bh.recipe3.core;

import java.util.concurrent.PriorityBlockingQueue;

import com.bh.recipe3.task.Event;
import com.bh.recipe3.task.Task;

/**
 * Main class of the example. Executes five threads that
 * store their events in a common priority queue and writes
 * them in the console to verify the correct operation of the
 * PriorityBlockingQueue class
 *
 */
public class Main {

    /**
     * @param args
     */
    public static void main(String[] args) {
        /*
         * Priority queue to store the events
         */
        PriorityBlockingQueue queue=new PriorityBlockingQueue<>();

        /*
         * An array to store the five Thread objects
         */
        Thread taskThreads[]=new Thread[5];

        /*
         * Create the five threads to execute five tasks
         */
        for (int i=0; inew Task(i,queue);
            taskThreads[i]=new Thread(task);
        }

        /*
         * Start the five threads
         */
        for (int i=0; i/*
         * Wait for the finalization of the five threads
         */
        for (int i=0; itry {
                taskThreads[i].join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        /*
         * Write the events in the console 
         */
        System.out.printf("Main: Queue Size: %d\n",queue.size());
        for (int i=0; i1000; i++){
            Event event=queue.poll();
            System.out.printf("Thread %s: Priority %d\n",event.getThread(),event.getPriority());
        }
        System.out.printf("Main: Queue Size: %d\n",queue.size());
        System.out.printf("Main: End of the program\n");
    }
}

使用带有延迟元素的线程安全列表:delayQueue类
delayQueue类可以存放带有激活日期的元素,当代用方法从队列中返回或提取元素时,未来的元素日期将被忽略,这些元素对于这些方法是不可见的。
为了具有调用行为,存放到DelayQueue类中的元素必须继承Delayed接口。Delayed接口使对象成为延迟对象,它使存放在DelayQueue类中的对象具有了激活日期,即到激活日期的时间。该接口强制执行下列两个方法:
compareTo(Delayed o):Delayed接口继承了Comparable接口。
getDelay(TimeUtil util):这个方法返回到激活日期的剩余时间,单位由参数指定。
使用线程安全可遍历映射:ConcurrentNavigableMap接口及其实现类。实现这个接口的类以如下两部分存放元素:
一个键值(key),它是元素的标识并且是唯一的。
元素其他部分数据。
javaAPI也提供了一个实现ConcurrentSkipListMap接口的类,ConcurrentSkipListMap接口实现了与ConcurrentNavigableMap接口有相同的行为的一个非阻塞式列表。
生成并发随机数:ThreadLocalRandom类。它是线程本地变量,每个生成随机数的线程都有一个不同的生成器,但是都在同一个类中被管理,相比对于Random对象为所有线程生成随机数,这种机制具有更好的性能。
使用ThreadLocalRandom的current()方法,该方法是一个静态方法。
ThreadLocalRandom.current().nextInt(10) 改方法返回一个介于1-10之间的随机数。
使用原子数组:AtomicIntegetArray
更优的性能共享对象,Java引入了比较和交换操作,这个操作使用以下三步修改变量的值:
1.取得变量值,即变量的旧值。
2.在一个临时变量中修改变量值,即变量的新值。
3.如果上面获得的变量旧值与当前变量值相等,就用新值替换旧值。如果已有其他线程修改了这个变量的值,上面获得的变量的旧值就可能与当前变量值不同。
采用比较和交换机制不需要使用同步机制,不仅可以避免死锁而且性能更好。

你可能感兴趣的:(java)