Java 并发集合 详解

Java 提供两类适用于并发应用的集合 :
1> 阻塞式集合 : 当集合已满或为空时,被调用的添加或移除方法不能立即执行,此时这个线程阻塞,一直到该方法可以被成功执行
2> 非阻塞式集合 : 如果方法不能被立即执行,则返回 null 或抛出异常,但是调用这个方法的线程不会被阻塞


ConcurrentLinkedDeque 类
非阻塞式线程安全列表,线程安全的双向链表
public int size() : 返回链表中的元素量,但可能数据不是真实的

public E getFirst()、public E getLast() : 返回元素中的第一个、最后一个数据,且数据不会移除,如果列表为空将抛出 NoSuchElementException

public E peek()、public E peekFirst()、public E peekLast() : 返回元素中的第一个、第一个、最后一个数据,没有数据时返回 null

public E poll()、public E pollFirst()、public E pollLast() : 返回并移除第一个、第一个、最后一个数据,没有数据时返回 null

public E remove()、public E removeFirst()、public E removeLast() : 返回并移除第一个、第一个、最后一个数据,如果列表为空将抛出 NoSuchElementException

public class AddTask implements Runnable {
    private ConcurrentLinkedDeque list ;
    public AddTask(ConcurrentLinkedDeque list) {
        this . list = list;
    }
    @Override
    public void run() {
        String name = Thread. currentThread ().getName();
        for ( int i = 0 ; i < 10000 ; i++) {
            list .add(name + ": Element " + i);
        }
    }
}

public class PollTask implements Runnable {
    private ConcurrentLinkedDeque list ;
    public PollTask(ConcurrentLinkedDeque list) {
        this . list = list;
    }
    @Override
    public void run() {
        for ( int i = 0 ; i < 5000 ; i++) {
            list .pollFirst();
            list .pollLast();
        }
    }
    public static void main(String[] args) {
        ConcurrentLinkedDeque list = new ConcurrentLinkedDeque<>();
        Thread[] threads = new Thread[ 100 ];
        for ( int i = 0 ; i < threads. length ; i++) {
            AddTask addTask = new AddTask(list);
            threads[i] = new Thread(addTask);
            threads[i].start();
        }
        System. out .printf( "Main: %d AddTask threads have been launched \n " , threads. length );
        try {
            for ( int i = 0 ; i < threads. length ; i++) {
                threads[i].join();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System. out .printf( "Main: Size of the List: %d \n " , list.size());
        for ( int i = 0 ; i < threads. length ; i++) {
            PollTask pollTask = new PollTask(list);
            threads[i] = new Thread(pollTask);
            threads[i].start();
        }
        System. out .printf( "Main: %d PollTask threads have been launched \n " , threads. length );
        for ( int i = 0 ; i < threads. length ; i++) {
            try {
                threads[i].join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System. out .printf( "Main: Size of the list: %d \n " , list.size());
    }
}


LinkedBlockingDeque 类
阻塞式线程安全列表,队列已满或为空操作不会立即执行

public LinkedBlockingDeque(int capacity) : 创建固定容量的 LinkedBlockingDeque 对象

public void put(E e) throws InterruptedException、public void putFirst(E e) throws InterruptedException、public void putLast(E e) throws InterruptedException : 向队列中 结尾、开头、结尾 插入元素,如果队列已满,则阻塞线程等待直到列表中有可用空间

public boolean add(E e)、public void addFirst(E e)、public void addLast(E e) : 向队列中 结尾、开头、结尾 插入元素,如果队列已满则抛出 IllegalStateException

public E take() throws InterruptedException、public E takeFirst() throws InterruptedException、public E takeLast() throws InterruptedException : 从列表中移除并返回第一个、第一个、最后一个元素,如果列表为空则线程将会被阻塞等待一直到列表中有元素

public E poll()、public E pollFirst()、public E pollLast() : 从列表中移除并返回第一个、第一个、最后一个元素,如果列表为空则返回 null

public E getFirst()、public E getLast() : 从列表中返回但不移除第一个、最后一个元素,如果列表为空则抛出 NoSuchElementException

public E peek()、public E peekFirst()、public E peekLast() : 取出但不移除队列中第一个、第一个、最后一个元素,如果列表为空则返回 null

public class Client implements Runnable {
    private LinkedBlockingDeque requestList ;
    public Client(LinkedBlockingDeque requestList) {
        this . requestList = requestList;
    }
    @Override
    public void run() {
        for ( int i = 0 ; i < 3 ; i++) {
            for ( int j = 0 ; j < 5 ; j++) {
                StringBuilder request = new StringBuilder();
                request.append(i);
                request.append( ":" );
                request.append(j);
                try {
                    requestList .put(request.toString());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System. out .printf( "Client: %s at %s. \n " , request, new Date());
            }
            try {
                TimeUnit. SECONDS .sleep( 2 );
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System. out .printf( "Clients: End. \n " );
    }
    public static void main(String[] args) throws InterruptedException {
        LinkedBlockingDeque list = new LinkedBlockingDeque<>( 3 );
        Client client = new Client(list);
        Thread thread = new Thread(client);
        thread.start();
        for ( int i = 0 ; i < 5 ; i++) {
            for ( int j = 0 ; j < 3 ; j++) {
                String request = list.take();
                System. out .printf( "Main: Request: %s at %s. Size: %d \n " , request, new Date(), list.size());
            }
            TimeUnit. MILLISECONDS .sleep( 300 );
        }
        System. out .printf( "Main: End of the program. \n " );
    }
}


PriorityBlockingQueue 类
阻塞式数据结构,所有添加的元素必须实现 Comparable 接口用于排序

public PriorityBlockingQueue() : 创建一个默认数量为 11 的队列,队列中的元素根据 Comparable 进行排序

public boolean add(E e) : 向队列中插入一个优先级队列数据

public void put(E e) : 向队列中插入一个优先级队列数据。因为队列是无界的,所以这个方法永远不会阻塞

public int size() : 返回队列中元素的个数,如果队列中元素个数超过 Integer.MAX_VALUE,则返回 Integer.MAX_VALUE

public E poll() : 移除并返回队列中第一个元素,如果队列为空则返回 null

public E take() throws InterruptedException : 移除并返回队列中第一个元素,如果队列为空则阻塞线程直到有元素取出

public E peek() : 返回但不移除队列中的数据,如果队列中的数据为空则返回 null

public void clear() : 移除队列中所有元素

public class Event implements Comparable {
    private int thread ;
    private int priority ;
    public Event( int thread, int priority) {
        this . thread = thread;
        this . priority = priority;
    }
    public int getThread() {
        return thread ;
    }
    public int getPriority() {
        return priority ;
    }
    @Override
    public int compareTo(Event event) {
        if ( this . priority > event.getPriority()) {
            return - 1 ;
        } else if ( this . priority < event.getPriority()) {
            return 1 ;
        }
        return 0 ;
    }
}

public class Task implements Runnable {
    private int id ;
    private PriorityBlockingQueue queue ;
    public Task( int id, PriorityBlockingQueue queue) {
        this . id = id;
        this . queue = queue;
    }
    @Override
    public void run() {
        for ( int i = 0 ; i < 1000 ; i++) {
            Event event = new Event( id , i);
            queue .add(event);
        }
    }
    public static void main(String[] args) throws InterruptedException {
        PriorityBlockingQueue queue = new PriorityBlockingQueue<>();
        Thread[] taskThreads = new Thread[ 5 ];
        for ( int i = 0 ; i < taskThreads. length ; i++) {
            Task task = new Task(i, queue);
            taskThreads[i] = new Thread(task);
            taskThreads[i].start();
        }
        for ( int i = 0 ; i < taskThreads. length ; i++) {
            taskThreads[i].join();
        }
        System. out .printf( "Main: Queue Size: %d \n " , queue.size());
        for ( int i = 0 ; i < taskThreads. length * 1000 ; 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 类 中的元素必须继承 Delayed接口,Delayed接口 继承于 Comparable接口因此有 compareTo方法,这样存放的对象具有激活时间,该接口两个方法 :
1> int compareTo(T o) : 如果当前对象延迟值小于参数对象值,将返回一个小于 0 的值;延迟值大于参数对象,返回一个大于0的值;如果两者延迟值相等则返回0
2> long getDelay(TimeUnit unit) : 返回激活日期剩余时间单位

public E poll() : 返回并移除队列头数据,或者队列中没有一个过期延迟对象返回 null

public E take() throws InterruptedException : 返回并移除第一个元素,如果延迟队列中没有元素则一直等待

public int size() : 返回队列中元素的个数,如果队列中元素个数超过 Integer.MAX_VALUE,则返回 Integer.MAX_VALUE。该方法返回元素的总个数,包括活动和非活动元素

public void clear() : 移除队列中所有元素

public boolean offer(E e) : 插入特定元素到延迟队列中

public E peek() : 返回但不移除第一个元素,如果在过期队列中没有元素将返回即将过期的数据,如果依旧没有则返回 null

public class Event implements Delayed {
    private Date startDate ;
    public Event(Date startDate) {
        this . startDate = startDate;
    }
    @Override
    public long getDelay(TimeUnit unit) {
        Date now = new Date();
        long diff = startDate .getTime() - now.getTime();
        return unit.convert(diff, TimeUnit. MILLISECONDS );
    }
    @Override
    public int compareTo(Delayed o) {
        long result = this .getDelay(TimeUnit. NANOSECONDS ) - o.getDelay(TimeUnit. NANOSECONDS );
        if (result < 0 ) { // 当前对象的延迟值小于参数对象值
            return - 1 ;
        } else if (result > 0 ) { // 当前对象的延迟值大于参数对象值
            return 1 ;
        }
        return 0 ; // 两者的延迟值相等
    }
}

public class Task implements Runnable {
    private int id ;
    private DelayQueue queue ;
    public Task( int id, DelayQueue queue) {
        this . id = id;
        this . queue = queue;
    }
    @Override
    public void run() {
        Date now = new Date();
        Date delay = new Date();
        delay.setTime(now.getTime() + ( id * 1000 ));
        System. out .printf( "Thread %s: %s \n " , id , delay);
        for ( int i = 0 ; i < 100 ; i++) {
            Event event = new Event(delay);
            queue .add(event);
        }
    }
    public static void main(String[] args) throws InterruptedException {
        DelayQueue queue = new DelayQueue<>();
        Thread[] threads = new Thread[ 5 ];
        for ( int i = 0 ; i < threads. length ; i++) {
            Task task = new Task(i + 1 , queue);
            threads[i] = new Thread(task);
            threads[i].start();
        }
        for ( int i = 0 ; i < threads. length ; i++) {
            threads[i].join();
        }
        do {
            int counter = 0 ;
            Event event;
            do {
                event = queue.poll();
                if (event != null ) {
                    counter++;
                }
            } while (event != null );
            System. out .printf( "At %s you have read %d events \n " , new Date(), counter);
        } while (queue.size() > 0 );
    }
}

线程安全可遍历
ConcurrentNavigableMap接口
该接口 public interface ConcurrentNavigableMap extends ConcurrentMap, NavigableMap 有两个存放元素,一个唯一标示元素另一个是其它部分数据

ConcurrentSkipListMap类
ConcurrentSkipListMap类 实现 ConcurrentNavigableMap接口,public class ConcurrentSkipListMap extends AbstractMap implements ConcurrentNavigableMap, Cloneable, Serializable
ConcurrentSkipListMap类 内部实现是通过一个 Skip List存放数据,Skip List基于并发列表的数据结构
ConcurrentSkipListMap类 插入的元素会使用键值来排序所有元素

public V put(K key, V value) : 在 map 中使用特定的 key 来关联特定的 value,如果 map 之前已经包含这个 key值,则旧值将会被取代

public Map.Entry firstEntry() : 返回map中第一个元素,如果map为空返回 null,返回实体不支持 Entry.setValue 方法

public class Contact {
    private String name ;
    private String phone ;
    public Contact(String name, String phone) {
        this . name = name;
        this . phone = phone;
    }
    public String getName() {
        return name ;
    }
    public String getPhone() {
        return phone ;
    }
}

public class Task implements Runnable {
    private ConcurrentSkipListMap map ;
    private String id ;
    public Task(ConcurrentSkipListMap map, String id) {
        this . map = map;
        this . id = id;
    }
    @Override
    public void run() {
        for ( int i = 0 ; i < 1000 ; i++) {
            Contact contact = new Contact( id , String. valueOf (i + 1000 ));
            map .put( id + contact.getPhone(), contact);
        }
    }
    public static void main(String[] args) {
        ConcurrentSkipListMap map = new ConcurrentSkipListMap<>();
        Thread[] threads = new Thread[ 25 ];
        int counter = 0 ;
        for ( char i = 'A' ; i < 'Z' ; i++) {
            Task task = new Task(map, String. valueOf (i));
            threads[counter] = new Thread(task);
            threads[counter].start();
            counter++;
        }
        try {
            for ( int i = 0 ; i < 25 ; i++) {
                threads[i].join();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System. out .printf( "Main: Size of the map: %d \n " , map.size());
        Map.Entry element = map.firstEntry();
        Contact contact = element.getValue();
        System. out .printf( "Main: First Entry: %s: %s \n " , contact.getName(), contact.getPhone());
        element = map.lastEntry();
        contact = element.getValue();
        System. out .printf( "Main: Last Entry: %s: %s \n " , contact.getName(), contact.getPhone());

        System. out .printf( "Main: Submap from A1996 to B1002: \n " );
        ConcurrentNavigableMap submap = map.subMap( "A1996" , "B1002" );
        do {
            element = submap.pollFirstEntry();
            if (element != null ) {
                contact = element.getValue();
                System. out .printf( "%s: %s \n " , contact.getName(), contact.getPhone());
            }
        } while (element != null );
    }
}


ThreadLocalRandom 类
ThreadLocalRandom 类线程本地变量,相比使用共享 Random对象其性能更好

public static ThreadLocalRandom current() : 返回当前线程的 ThreadLocalRandom 对象

public int nextInt(int bound) : 返回介于指定范围的数据,类似方法还有其它

public class TaskLocalRandom implements Runnable {
    public TaskLocalRandom() {
        ThreadLocalRandom. current ();
    }
    @Override
    public void run() {
        String name = Thread. currentThread ().getName();
        for ( int i = 0 ; i < 10 ; i++) {
            System. out .printf( "%s: %d \n " , name, ThreadLocalRandom. current ().nextInt( 10 ));
        }
    }
    public static void main(String[] args) {
        Thread[] threads = new Thread[ 3 ];
        for ( int i = 0 ; i < 3 ; i++) {
            TaskLocalRandom task = new TaskLocalRandom();
            threads[i] = new Thread(task);
            threads[i].start();
        }
    }
}


原子变量
原子变量不使用锁或其他同步机制来保护对其值的并发访问,所有操作都基于 CAS原子操作,其保证多线程在同一时间操作一个原子变量而不会反升。当一个线程在对原子变量操作时,如果其它线程试图对同一原子变量执行操作,原子变量的实现类提供一套机制来检查操作是否在一步内完成。一般来说,这个操作先获取变量值,然后在本地改变变量的值,然后试图用这个改变的值去替换之前的值。如果之前的值没有被其它线程改变,就可以执行这个替换操作。否则,方法将再执行这个操作。这个操作成为 CAS原子操作(Compare and Set)

AtomicLong 类
public final void set(long newValue) : 设置给定值

public final long getAndAdd(long delta) : 自动给定指定值到当前值,给定值可以为负数

类似的类还有 AtomicBoolean AtomicInteger 等

public class Bank implements Runnable {
    private Account account ;
    public Bank(Account account) {
        this . account = account;
    }
    @Override
    public void run() {
        for ( int i = 0 ; i < 10 ; i++) {
            account .subtractAmount( 1000 );
        }
    }
}

public class Company implements Runnable {
    private Account account ;
    public Company(Account account) {
        this . account = account;
    }
    @Override
    public void run() {
        for ( int i = 0 ; i < 10 ; i++) {
            account .addAmount( 1000 );
        }
    }
}

public class Account {
    private AtomicLong balance ;
    public Account() {
        balance = new AtomicLong();
    }
    public long getBalance() {
        return balance .get();
    }
    public void setBalance( long balance) {
        this . balance .set(balance);
    }
    public void addAmount( long amount) {
        this . balance .getAndAdd(amount);
    }
    public void subtractAmount( long amount) {
        this . balance .getAndAdd(-amount);
    }
    public static void main(String[] args) {
        Account account = new Account();
        account.setBalance( 1000 );
        Company company = new Company(account);
        Thread companyThread = new Thread(company);
        Bank bank = new Bank(account);
        Thread bankThread = new Thread(bank);
        System. out .printf( "Account : Initial Balance: %d \n " , account.getBalance());
        companyThread.start();
        bankThread.start();
        try {
            companyThread.join();
            bankThread.join();
            System. out .printf( "Account : Final Balance: %d \n " , account.getBalance());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

原子数组
当实现一个并发应用时,将不可避免地会有多线程共享一个或多个对象的现象,为避免数据不一致问题需要使用同步机制(锁或synchronized关键字) 来保护这些共享属性的访问,但是同步机制存在问题 :
1> 死锁 : 一个线程被阻塞,并且试图获得锁正在被其它线程使用,但其它线程永远不会释放这个锁。这种情况使得应用不会继续执行,永远不会结束
2> 即是只有一个线程访问共享对象,它仍然需要执行必须的代码来获取和释放锁
未解决这种问题,Java引入比较和交换操作(Compare-and-Swap Operation),该操作使用以下三个变量值 :
1> 取得变量值,即变量的旧值
2> 在一个临时变量中修改变量值,即变量的新值
3> 如果上面获得的变量值与当前变量值相等,就用新值替换旧值。如果已有其它线程修改这个变量值,上面获得的变量的旧值就可能与当前变量值不同
采用比较和交换机制不需要使用同步机制,不仅可以避免死锁而且性能更好

Java在原子变量中实现这种机制

AtomicIntegerArray 类
public final int getAndDecrement(int i) : 在指定索引处的元素自动减一

public final int getAndIncrement(int i) : 在指定索引处的元素自动加一

public class Incrementer implements Runnable {
    private AtomicIntegerArray vector ;
    public Incrementer(AtomicIntegerArray vector) {
        this . vector = vector;
    }
    @Override
    public void run() {
        for ( int i = 0 ; i < vector .length(); i++) {
            vector .getAndIncrement(i);
        }
    }
}

public class Decrementer implements Runnable {
    private AtomicIntegerArray vector ;
    public Decrementer(AtomicIntegerArray vector) {
        this . vector = vector;
    }
    @Override
    public void run() {
        for ( int i = 0 ; i < vector .length(); i++) {
            vector .getAndDecrement(i);
        }
    }
    public static void main(String[] args) {
        final int THREADS = 100 ;
        AtomicIntegerArray vector = new AtomicIntegerArray( 1000 );
        Incrementer incrementer = new Incrementer(vector);
        Decrementer decrementer = new Decrementer(vector);
        Thread[] threadIncrementer = new Thread[THREADS];
        Thread[] threadDecrementer = new Thread[THREADS];
        for ( int i = 0 ; i < THREADS; i++) {
            threadIncrementer[i] = new Thread(incrementer);
            threadDecrementer[i] = new Thread(decrementer);
            threadIncrementer[i].start();
            threadDecrementer[i].start();
        }
        try {
            for ( int i = 0 ; i < THREADS; i++) {
                threadIncrementer[i].join();
                threadDecrementer[i].join();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        for ( int i = 0 , len = vector.length(); i < len; i++) {
            if (vector.get(i) != 0 ) {
                System. out .println( "Vector[" + i + "] : " + vector.get(i));
            }
        }
        System. out .println( "Main: End of the example" );
    }
}

你可能感兴趣的:(Java)