java multithreading Summary - comparison and introduction of synchronous and concurrent containers of container

A brief set of containers

java.util package collection container below mainly two, one is the Collection interface and the following List Set, one is the Map, the following general structure:

  • Collection

    • List
      • LinkedList
      • ArrayList
      • Vector
        • Stack
    • Set
      • HashSet
      • TreeSet
      • LinkedSet
  • Map

    • Hashtable
    • HashMap
    • WeakHashMap

2 synchronous container

Synchronization container, also known as thread-safe container, through syncrhoized key for locking thread unsafe operation to ensure the safety of the thread
in which the synchronous container include:
1.Vector, Stack, the HashTable
synchronous collection 2.Collections tools provided class
collections class is a utility class that corresponds to the class Arrays Array of support, collections class provides a number of methods to set or sort the container, find. It also provides several static methods to create a synchronous container class:

3 concurrent containers

java.util.concurrent offers a variety of thread-safe container, most systems use the underlying technology of the thread-safe, also called concurrent containers, similar to the native. In Java8 use CAS.

4 explain the case

This introduces some common concurrent synchronous container and container, are introduced by comparing the output of my case is broadly divided into three categories Map / Set, List, Queue to explain, but a Map / Set, only describes the Map, because in java design, Set is the Map, it means not only the Key Value of the Map, well, now we get to the point

4.1 Map/Set

Three new code Map, HashTable, ConcurrentHashMap, ConcurrentSkipListMap compare the efficiency of each map, from the thread store 100 to map the random number 10000 and controls the operation state of the latch by a CountDownLatch, output runtime

/**
 * 并发容器 - ConcurrentMap
 */
package com.bernardlowe.concurrent.t06;

import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentSkipListMap;
import java.util.concurrent.CountDownLatch;

public class Test_01_ConcurrentMap {
	
	public static void main(String[] args) {
		final Map<String, String> map = new Hashtable<>();
		// final Map<String, String> map = new ConcurrentHashMap<>();
		// final Map<String, String> map = new ConcurrentSkipListMap<>();
		final Random r = new Random();
		Thread[] array = new Thread[100];
		final CountDownLatch latch = new CountDownLatch(array.length);
		
		long begin = System.currentTimeMillis();
		for(int i = 0; i < array.length; i++){
			array[i] = new Thread(new Runnable() {
				@Override
				public void run() {
					for(int j = 0; j < 10000; j++){
						map.put("key"+r.nextInt(100000000), "value"+r.nextInt(100000));
					}
					latch.countDown();
				}
			});
		}
		for(Thread t : array){
			t.start();
		}
		try {
			latch.await();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		long end = System.currentTimeMillis();
		System.out.println("执行时间为 : " + (end-begin) + "毫秒!");
	}

}

复制代码

Hashtable results:

ConcurrentHashMap results:

ConcurrentSkipListMap results:

ConcurrentHashMap underlying hash is synchronized Map implementation (Set)
internal ConcurrentSkipListMap is SkipList (jump table) structure to achieve non-blocking read / write / delete Map, ordered its value is stored, and its interior is composed of vertical and horizontal list composition, in JDK1.8, ConcurrentHashMap performance and superior storage ConcurrentSkipListMap

In order to compare the results of the test data more intuitive, I've deliberately generated random number transfer is relatively large. It should be note, at the time of the test, if the machine performance is better, there will be errors may result because System.currentTimeMillis (), this method calls a native method, the accuracy of the time taken will depend on the operating system's implementation mechanism, specifically why, you can read this article http://blog.sina.com.cn/s/blog_6b8bd9d80101fe8t.html. But I follow the way of the document will System.currentTimeMillis () instead System.nanoTime (), found no solution to this problem, probably because it did not achieve nanosecond.

4.2 List

The following code looks like 4.1, but also the three new List, ArrayList, Vector, CopyOnWriteArrayList, threads 100 from storage 1000 to map the random number, and by controlling the operation state of the latch CountDownLatch, and the final output list of the running time of length. ArrayList is thread safe since, in the implementation of multi-threading, need try {} catch {}, or because of array bounds and error, because the bottom is a length ArrayList dynamically expanding array

/**
 * 并发容器 - CopyOnWriteList
 */
package com.bernardlowe.concurrent.t06;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.Vector;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CountDownLatch;

public class Test_02_CopyOnWriteList {
	
	public static void main(String[] args) {
		 final List<String> list = new ArrayList<String>(); // 线程不安全
//		 final List<String> list = new Vector<>(); // 线程安全
//		final List<String> list = new CopyOnWriteArrayList<>(); // 线程安全
		final Random r = new Random();
		Thread[] array = new Thread[100];
		final CountDownLatch latch = new CountDownLatch(array.length);
		
		long begin = System.currentTimeMillis();
		for(int i = 0; i < array.length; i++){
			array[i] = new Thread(new Runnable() {
				@Override
				public void run() {
					for(int j = 0; j < 1000; j++){
						try {
							list.add("value" + r.nextInt(100000));
						} catch (Exception e) {

						}
					}
					latch.countDown();
				}
			});
		}
		for(Thread t : array){
			t.start();
		}
		try {
			latch.await();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		long end = System.currentTimeMillis();
		System.out.println("执行时间为 : " + (end-begin) + "毫秒!");
		System.out.println("List.size() : " + list.size());
	}

}

复制代码

ArrayList result: because ArrayList is not thread-safe, so in a multithreaded environment, you may lose data

Vector results:

CopyOnWriteArrayList results:

CopyOnWriteArrayList is separate read and write, copy-on-write a new array, complete insert, modify, or remove will operate the new array assignment to the array, reads the latest arrays when reading, so when a write operation, efficiency is very low (although slower write, delete it in the head and tail arrays or quickly) can be seen from the above three results, CopyOnWriteArrayList though to ensure that the thread-safe, but it writes efficiency is too low, but compared Vector concurrent security and better performance than Vector, Vector is CRUD methods are added synchronized, to ensure synchronization, but when each method should be performed to obtain a lock, the performance will be greatly decreased, while CopyOnWriteArrayList only additions and deletions on the change plus lock, but do not lock read, reading performance in terms of like in Vector, CopyOnWriteArrayList supports reading and writing concurrency less , so there is not a dirty read CopyOnWriteArrayList problem

4.3 Queue

This section describes some common concurrent queue api

4.3.1 ConcurrentLinkedQueue

Basic synchronization queue list

peek () -> View first data queue in the
poll () -> get the first data in the queue

/**
 * 并发容器 - ConcurrentLinkedQueue
 *  队列 - 链表实现的。 
 */
package com.bernardlowe.concurrent.t06;

import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;

public class Test_03_ConcurrentLinkedQueue {
	
	public static void main(String[] args) {
		Queue<String> queue = new ConcurrentLinkedQueue<>();

		//向队列中增加10个数据
		for(int i = 0; i < 10; i++){
			queue.offer("value" + i);
		}
		
		System.out.println(queue);
		System.out.println(queue.size());

		// peek() -> 查看queue中的首数据,
		System.out.println("首数据 " + queue.peek());
		System.out.println("队列长度 "+ queue.size());
		System.out.println("===================");
		// poll() -> 获取queue中的首数据
		System.out.println("首数据 " + queue.peek());
		System.out.println("队列长度 "+ queue.size());
	}

}


复制代码

result:

4.3.2 blocking queue LinkedBlockingQueue

Blocking queue, the queue capacity is insufficient blocked automatically, and automatic blocking queue capacity is 0.

put & take - blocked automatically
put automatically blocked when the queue is full capacity, automatically blocking
the take automatic blocking method, the queue capacity is 0, the automatic blocking

/**
 * 并发容器 - LinkedBlockingQueue
 *  阻塞容器。
 */
package com.bernardlowe.concurrent.t06;

import java.util.Random;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;

public class Test_04_LinkedBlockingQueue {
	
	final BlockingQueue<String> queue = new LinkedBlockingQueue<>();
	final Random r = new Random();
	
	public static void main(String[] args) {
		final Test_04_LinkedBlockingQueue t = new Test_04_LinkedBlockingQueue();
		
		new Thread(new Runnable() {
			@Override
			public void run() {
				while(true){
					try {
						t.queue.put("value"+t.r.nextInt(1000));
						TimeUnit.SECONDS.sleep(1);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}
		}, "producer").start();
		
		for(int i = 0; i < 3; i++){
			new Thread(new Runnable() {
				@Override
				public void run() {
					while(true){
						try {
							System.out.println(Thread.currentThread().getName() + 
									" - " + t.queue.take());
						} catch (InterruptedException e) {
							e.printStackTrace();
						}
					}
				}
			}, "consumer"+i).start();
		}
	}

}

复制代码

result:

The result is a simple producers and consumers

4.3.3 BlockingQueue

Underlying array bounded queue achieved when an insufficient capacity, there is blocking capability, according to different calling API (add / put / offer) , having different characteristics
herein describes three methods api add, put, offer

  • add method when insufficient capacity, an exception is thrown.
  • put method when insufficient capacity, blocking wait.
  • offer methods

    Offer one-parameter method, without blocking. When sufficient capacity, returns false. New data give up the current operation.
    Three-parameter method offer (offer (value, times, timeunit )), when an insufficient capacity when the length of the blocking times (in TimeUnit), if the long, while the blocking capacity of the free, new data returns true. If blocking a long range, no free capacity, new data is abandoned, returns false.

/**
 * 并发容器 - ArrayBlockingQueue
 *  有界容器。
 */
package com.bernardlowe.concurrent.t06;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;

public class Test_05_ArrayBlockingQueue {
	
	final BlockingQueue<String> queue = new ArrayBlockingQueue<>(3);
	
	public static void main(String[] args) {
		final Test_05_ArrayBlockingQueue t = new Test_05_ArrayBlockingQueue();
		
		for(int i = 0; i < 5; i++){

			// 1.add method
			System.out.println("add method : " + t.queue.add("value"+i));

			// 2.put method
//			try {
//				t.queue.put("put"+i);
//			} catch (InterruptedException e) {
//				e.printStackTrace();
//			}
//			System.out.println("put method : " + i);

			// 3.offer method
//			System.out.println("offer method : " + t.queue.offer("value"+i));
//			try {
//				System.out.println("offer method : " +
//							t.queue.offer("value"+i, 1, TimeUnit.SECONDS));
//			} catch (InterruptedException e) {
//				e.printStackTrace();
//			}
		}
		
		System.out.println(t.queue);
	}

}

复制代码

The results add method: when sufficient capacity, an exception is thrown

The results put method: when sufficient capacity, blocking wait

Single / multi-parameter method offer Results:

Single parameters offer: lack of capacity, the result of a direct return, without blocking
multi-parameter offer: insufficient capacity, obstruction

4.3.4 Latency Queuing DelayQueue

Delay queue. According to the comparison mechanism, the custom processing sequence queues. Commonly used in regular tasks. Such as: time off. In particular the following sample code

/**
 * 并发容器 - DelayQueue
 */
package com.bernardlowe.concurrent.t06;

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

public class Test_06_DelayQueue {
	
	static BlockingQueue<MyTask_06> queue = new DelayQueue<>();
	
	public static void main(String[] args) throws InterruptedException {
		long value = System.currentTimeMillis();
		MyTask_06 task1 = new MyTask_06(value + 2000);
		MyTask_06 task2 = new MyTask_06(value + 1000);
		MyTask_06 task3 = new MyTask_06(value + 3000);
		MyTask_06 task4 = new MyTask_06(value + 2500);
		MyTask_06 task5 = new MyTask_06(value + 1500);
		
		queue.put(task1);
		queue.put(task2);
		queue.put(task3);
		queue.put(task4);
		queue.put(task5);
		
		System.out.println(queue);
		System.out.println(value);
		for(int i = 0; i < 5; i++){
			System.out.println(queue.take());
		}
	}

}

class MyTask_06 implements Delayed {
	
	private long compareValue;
	
	public MyTask_06(long compareValue){
		this.compareValue = compareValue;
	}

	/**
	 * 比较大小。自动实现升序
	 * 建议和getDelay方法配合完成。
	 * 如果在DelayQueue是需要按时间完成的计划任务,必须配合getDelay方法完成。
	 */
	@Override
	public int compareTo(Delayed o) {
		return (int)(this.getDelay(TimeUnit.MILLISECONDS) - o.getDelay(TimeUnit.MILLISECONDS));
	}

	/**
	 * 获取计划时长的方法。
	 * 根据参数TimeUnit来决定,如何返回结果值。
	 */
	@Override
	public long getDelay(TimeUnit unit) {
		return unit.convert(compareValue - System.currentTimeMillis(), TimeUnit.MILLISECONDS);
	}
	
	@Override
	public String toString(){
		return "Task compare value is : " + this.compareValue;
	}
	
}
复制代码

result:

4.3.5 Transfer Queue LinkedTransferQueue

Here is the main difference between the two methods, add and transfer

  • add - queue data is saved, not block waiting.
  • transfer - is a unique method of TransferQueue. Consumers must have (take () caller of the method).
/**
 * 并发容器 - LinkedTransferQueue
 *  转移队列
 */
package com.bernardlowe.concurrent.t06;

import java.util.concurrent.LinkedTransferQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TransferQueue;

public class Test_07_TransferQueue {
	
	TransferQueue<String> queue = new LinkedTransferQueue<>();
	
	public static void main(String[] args) {
		final Test_07_TransferQueue t = new Test_07_TransferQueue();
		
		/*new Thread(new Runnable() {
			@Override
			public void run() {
				try {
					System.out.println(Thread.currentThread().getName() + " thread begin " );
					System.out.println(Thread.currentThread().getName() + " - " + t.queue.take());
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}, "output thread").start();

		try {
			TimeUnit.SECONDS.sleep(2);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}

		try {
			t.queue.transfer("test string");
		} catch (InterruptedException e) {
			e.printStackTrace();
		}*/
		
		new Thread(new Runnable() {

			@Override
			public void run() {
				try {
					t.queue.transfer("test string");
					// t.queue.add("test string");
					System.out.println("add ok");
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		}).start();
		
		try {
			TimeUnit.SECONDS.sleep(2);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}

		new Thread(new Runnable() {
			@Override
			public void run() {
				try {
					System.out.println(Thread.currentThread().getName() + " thread begin " );
					System.out.println(Thread.currentThread().getName() + " - " + t.queue.take());
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}, "output thread").start();
		
	}

}

复制代码

Here transfer (), and take () method is blocking, or Take the first data transfer request receiving first transmission data will be blocked waiting.
For example, transfer () is quite the phone call, when to call A to B, B to be received telephone signal before answering a call, or A waits add () is equivalent to A to B to send text messages, text messages already exists to the operator's side, waiting for B to receive, regardless of when texting B is online

4.3.6 SynchronousQueue

The queue is a queue capacity is zero, is a special TransferQueue, and TransferQueue like it, but the job queue must have a consumer thread has two methods the Add, PUT
the Add method, non-blocking. If there is no thread is blocked waiting for data consumption, an exception is thrown. put method, a blockage. If there is no thread is blocked waiting for data consumption is blocked.

/**
 * 并发容器 - SynchronousQueue
 */
package com.bernardlowe.concurrent.t06;

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.TimeUnit;

public class Test_08_SynchronusQueue {
	
	BlockingQueue<String> queue = new SynchronousQueue<>();
	
	public static void main(String[] args) {
		final Test_08_SynchronusQueue t = new Test_08_SynchronusQueue();
		
		new Thread(new Runnable() {
			@Override
			public void run() {
				try {
					System.out.println(Thread.currentThread().getName() + " thread begin " );
					try {
						TimeUnit.SECONDS.sleep(2);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					System.out.println(Thread.currentThread().getName() + " - " + t.queue.take());
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}, "output thread").start();
		
		/*try {
			TimeUnit.SECONDS.sleep(3);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}*/
		// t.queue.add("test add");
		try {
			t.queue.put("test put");
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		
		System.out.println(Thread.currentThread().getName() + " queue size : " + t.queue.size());
	}

}

复制代码

The t.queue.add("test add");comment open, t.queue.put("test put");with comments
add method Abnormal results: Since it is a capacity of the queue 0

Guess you like

Origin juejin.im/post/5d2ed0315188253e215ff30d