Computer users want to do more than one thing at a time. A single application does more than one things at a time called concurrent software.
The Java platform is designed to support concurrent programming. Since Java SE 5, the Java platform has also included high-level concurrency APIs.
Processes and Threads
In concurrent programming, there are two basic units of execution: process and threads. In the Java programming language, concurrent programming is mostly concerned with threads.
A computer system has many active processes and threads. If in computer systems that only have a single execution core, there is one thread actually executing at any given moment. Processing time for a single core is shared among processes and threads through an OS feature called time slicing.
It’s becoming more and more common for computer systems to have multiple processors or processors with multiple execution cores.
Processes
A process has a self-contained execution environment. A process generally has a complete, private set of basic run-time resources, for example, memory space.
Processes are often seen as synonymous with programs or applications. Most implementations of Java Virtual machines run as a single process. A Java application can create additional processes using ProcessBuilder object.
Threads
Threads are sometimes called lightweight processes. Both processes and threads provide an execution environment, but creating a new thread requires fewer resources than creating a new process.
Threads exist within a process, and every process has at least one thread. Threads share the process’s resources, including memory and open files. This makes for efficient, but potentially problematic, communication.
Difference between processes and threads
Threads exist within a process, and every process has at least one thread. Both processes and threads are units of execution, and they have an execution environment.
A process is a minimal resource assignment unit, but a thread is a minimal execution unit.
A process has a self-contained execution environment. No resources are shared between processes. But threads have a private resource from a process, and multiple threads in the same process also can share the resource of their process.
The Advantages and Disadvantages of Threads
Advantages
Improve the performance of applications.
Asynchronous workflows.
Exploiting multiple processors.
More responsive user interfaces.
Disadvantages
Safety.
Liveness.
Performance. Thread introduces additional performance costs, for example, context switches, and synchronization costs.
Properly using multi-thread is more beneficial than disadvantages.
Thread Objects
Each thread is associated with an instance of the class Thread. There are two basic strategies for using Thread objects to create a concurrent application.
To directly control thread creation and management, simply instantiate Thread each time.
To abstract thread management from the rest of your application, pass the application’s task to an executor.
Creating an instance of Thread must provide the code that will run in that thread. There are two ways to create a Thread instance:
Provide a runnable object.
Subclass Thread.
Using a class of implemented Runnable interface to create an instance of Thread is more general because of the Runnable object can subclass a class other than Thread.
An interrupt is an indication to a thread that it should stop what it is doing and do something else. Thread.interrupt() sets the interrupted status/flag of the target thread. The code running in that target thread MAY poll the interrupted status and handle it appropriately. Some methods that block such as Object.wait() may consume the interrupted status immediately and throw an appropriate exception (usually InterruptedException). Thread interruption is a gentle way to stop a thread. It is used to give threads a chance to exit cleanly, as opposed by deprecated Thread.stop() that force to stop the thread.
Interruption in Java is not preemptive. Threads have to cooperate in order to process the interrupt properly. If the target thread does not poll the interrupted status the interrupt is effectively ignored. Polling occurs via the Thread.interrupted() method which returns the current thread’s interrupted status and clears that interrupt flag. Usually, the thread might then do something such as throw InterruptedException. If a thread goes a long time without invoking a method that throws InterruptedException Then it must periodically invoke Thread.interrupted(), which returns if an interrupt has been received. For example:
while (...){ doSometing(); if (Thread.interrupted()){ // We've been interrupted System.out.println("Thread interrupted!"); return; // or break loop } }
if (Thread.interrupted()){ thrownewInterrupteException(); }
Some API methods have built-in interrupt handling
Object.wait()
Thread.sleep(), Thread.join()
Most java.util.concurrent structures.
Java NIO (it does not use InterruptedException, instead using ClosedByInterruptException).
Joins
The join method allows one thread to wait for the completion of another. Example of current thread wait for thread t to be complete:
t.join()
join is similar to sleep doesn’t release the locks it holds, it just suspends the current thread.
Difference Between Wait and Sleep
wait is for concurrent programming but sleep not. Sleeping does not release the locks it holds while wait releases the lock on wait() is called.
sleep just suspends a thread execution for a fixed time, but wait suspends a thread execution until notify is called.
wait must happen in a block synchronized on the monitor object (otherwise occurs IllegalMonitorStateException ) whereas sleep does not.
You can wait on object itself whereas you call sleep on Thread.
Synchronization
Threads communicate primarily by sharing access to fields and the objects reference fields refer to. This form of communication is extremely efficient, but makes two kinds of errors possible: thread interference and memory consistency errors. The tool needed to prevent these errors is synchronization.
However, synchronization can introduce threads deadlock and thread contention (starvation and livelock).
Thread Interference: Errors are introduced when multiple threads access shared data.
Memory Consistency Errors: Errors that result form inconsistent views of shared memory.
Synchronized Methods: It can effectively prevent thread interference and memory consistency errors.
Implicit Locks: synchronization is based on implicit locks.
Atomic Access: operations that can’t be interfered with by other threads.
The increment() and decrement() are not atomic operations. Each method has three steps operations: retrieve variable, update value, store result in variable. When multiple threads invoke these methods, they interfere with each other, because the three steps of each thread are interleaving executed. So the result of each thread is unpredictable.
Memory Consistency Errors
Memory consistency errors occur when different threads have inconsistent views of what should be the same data. When other thread update value of a variable, your current thread still read old value of the variable.
The key to avoiding memory consistency errors is understanding the happens-before relationship. This relationship is simply a guarantee that memory writes by one specific statement is visible to another specific statement.
There are several actions that create happens-before relationships:
Single thread rule: Each action in a single thread happens-before every action in that thread that comes later in the program order.
Monitor lock rule (synchronization): An unlock on a monitor lock (exiting synchronized method/block) happens-before every subsequent acquiring on the same monitor lock.
Volatile variable rule: A write to a volatile field happens-before every subsequent read of that same field.
Thread start rule: A call to Thread.start() on a thread happens-before every action in the started thread.
Thread join rule: All actions in a thread happen-before any other thread successfully returns from a join on that thread.
Transitivity: If A happens-before B, and B happens-before C, then A happens-before C.
Synchronized Methods
The Java programming language provides two basic synchronization idioms: synchronized methods and synchronized statements. synchronized make non-atomic operations become atomic operations and establish happens-before relationships between threads that access the same variables.
Synchronized methods enable a simple strategy for preventing thread interference and memory errors. First, it is not possible for two invocations of synchronized methods on the same object to interleave. Second, when a synchronized method exists, it automatically establishes a happens-before relationship with any subsequent invocation of a synchronized method for the same object.
Intrinsic Locks and Synchronization
Synchronization is built around an internal entity known as the intrinsic lock or monitor lock. (The API specification often refers to this entity simply as a “monitor.”) Intrinsic locks play a role in both aspects of synchronization: enforcing exclusive access to an object’s state and establishing happens-before relationships.
Every object has an intrinsic lock associated with it.
Locks in Synchronized Methods
When a thread invokes a synchronized method, it automatically acquires the intrinsic lock for that method’s object and release it when the method returns. The lock release occurs even if the return was caused by an uncaught exception.
A static synchronized method is associated with a class. A thread acquires the intrinsic lock for the Class object associated with the class.
Invoking other objects’ methods from synchronized code can create liveness problems.
Synchronized statements are also useful for improving concurrency with fine-grained synchronization.
Reentrant Synchronization
Allowing a thread to acquire the same lock more than once enable reentrant synchronization.
Atomic Access
Common Atomic Actions
Reads and writes are atomic for reference variables and for most of primitive variables (except long and double).
Reads and writes are atomic for all variable declared volatile (including long and double variables)
Atomic actions cannot be interleaved, so they can be used without fear of thread interference. However, this does not eliminate all need to synchronize atomic actions, because memory consistency errors are still possible. Using volatile variables reduce the risk of memory consistency errors, because any write to a volatile variable establishes a happens-before relationship with subsequent reads of the same variable.
Using simple atomic variable access is more efficient than accessing these variables through synchronized code, but requires more care by the programmer to avoid memory consistency errors. Some of the classes in the java.util.concurrent package provide atomic methods that do not rely on synchronization.
Liveness
The most common kind of liveness problem is the deadlock, others are starvation and livelock.
Deadlock
Deadlock describes a situation where two or more threads are blocked forever, waiting for each other.
publicstaticvoidmain(String[] args) { newThread(()->{ synchronized (lock1){ try { // ensure both two threads acquired their first lock, // and waiting for acquired the second lock. Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (lock2){} } }).start(); newThread(()->{ synchronized (lock2){ try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (lock1){} } }).start(); } }
Starvation and Livelock
Starvation and livelock are much less common a problem than deadlock, but are still problems that every designer of concurrent software is likely to encounter.
Starvation describes a situation where a thread is unable to gain regular access to shared resources and is unable to make progress.
Livelock is a situation threads simply too busy responding to each other and unable to make further progress.
Guarded Blocks
Threads often have to coordinate their actions. The most common coordination idiom is the guarded block. Such a block begins by polling a condition that must be true before the block can proceed.
Guard by simply loop
publicvoidguardedJoy(){ while(!joy) {} System.out.println("Joy has been achived!"); }
Guard by invokes Object.wait. A more efficient guard.
publicsynchronizedvoidguardedJoy(){ while(!joy){ try{ wait(); } catch (InterruptedException e){} } System.out.println("Joy efficiently has been achived!"); }
publicsynchronizednotifyJoy() { joy = true; notifyAll(); }
Using guarded blocks can create a Producer-Consumer application.
Immutable Objects
An object is considered immutable if its state connot change after it is constructed.
Immutable objects are particularly useful in concurrent applications. Since they cannot change state, they cannot be corrupted by thread interference or observed in an inconsistent state.
Immutable objects using strategies
Don’t provide “setter” methods. Methods that modify fields or objects referred to by fields.
Make all fields final and private.
Don’t allow subclass to override methods. The simplest way to do this is to declare the class as final. A more sophisticated approach is to make the constructor private and construct instances in factory methods in this class.
If the instance fields include references to mutable objects, don’t allow those objects to be change. Don’t provide methods that modify the mutable objects. Don’t share references to the mutable objects.
High Level Concurrency Objects
These low-level APIs are adequate for very basic tasks, but higher-level building blocks are needed for more advanced tasks. This is especially useful for massively concurrent applications in multiprocessor and multi-core systems.
High Level Concurrency Features:
Lock object support locking idioms that simplify many concurrent applications.
Executors define a high-level API for launching and managing threads. Executor implementations provided by java.util.concurrent provide thread pool management suitable for large-scale applications.
Concurrent collections make it easier to manage large collections of data, and can greatly reduce the need for synchronization.
Atomic variables have features that minimize synchronization and help avoid memory consistency errors.
ThreadLocalRandom provides efficient generation of pseudorandom numbers from multiple threads.
Lock Objects
Synchronized code relies on a simple kind of reentrant lock. This kind of lock is easy to use, but many limitations. More sophisticated locking idioms are supported by the java.util.concurrent.locks package.
Lock objects work very much like the implicit locks used by synchronized code. The biggest advantage of Lock objects over implicit locks is their ability to back out of an attempt to acquire a lock. The tryLock method backs out if the lock is not available immediately or before a timeout expires.
We can use Lock objects to solve the deadlock problem. First we use Lock.tryLock() method to acquire all locks we needed, if fail to acquire, then unlock all acquired locks, else we can acquire all locks and without deadlock problem.
Executors
In large-scale applications, it makes sense to separate thread management and creation from the rest of the application. Objects that encapsulate these functions are known as executors.
Executor Interfaces: defines the three executor object types.
Thread Pools: are the most common kind of executor implementation.
Fork/Join: is a framework for taking advantage of multiple processors.
Executors: is a utility class that has factory and utility methods for Executor, ExecutorService, ScheduledExecutorService, ThreadFactory, and Callable classes.
The java.util.concurrent package defines three executor interfaces: Executor, ExecutorService, ScheduledExecutorService. Typically, variables that refer to executor objects are declared as one of these three interface types, not with an executor class type.
The Executor interface provides a single method execute() designed to be a replacement for a common thread-creation idiom.
newThread(r).start();
executor.execute(r);
The ExecutorService interface provides methods execute and submit. Like execute, the submit method accepts Runnable objects, but also accepts Callable object, which allow the task to return a value. The submit method returns a Future object.
The ScheduledExecutorService interface supplements the methods of its parent ExecutorService with schedule, which executes a Runnable or Callable task after a specified delay. In addition, the interface defines scheduleAtFixedRate and scheduleWithFixedDelay, which executes specified tasks repeatedly, at defined intervals.
Thread Pools
Most of executor implementations use thread pools. One common type of thread pool is the fixed thread pool.
One common type of thread pool is the fixed thread pool. A simple way to create an executor that uses a fixed thread pool is to invoke the newFixedThreadPool factory method of java.util.concurrent.Executors. This class also provides the following factory methods:
newCachedThreadPool
new SingleThreadExecutor
several factory methods returns ScheduledExecuteorService executors.
If none of the executors provided by the factory methods of Executors meet your needs, constructing instance of ThreadPoolExecutor or ScheduledThreadPoolExecutor will give you additional options.
To construct a ThreadPoolExecutor object, you least specify five arguments:
ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue)
Fork/Join
The fork/join framework is an implementation of ExecutorService interface that helps you take advantage of multiple processors. It is designed for work that can be broken into smaller pieces recursively.
The fork/join framework is distinct because it uses a work-stealing algorithm. Worker threads that run out of things to do can steal tasks from other threads that are still busy.
The center of the fork/join framework is the ForkJoinPool class, an extension of the AbstractExecutorService class. ForkJoinPool implements the core work-stealing algorithm and can execute ForkJoinTask processes.
You submit tasks to a ForkJoinPool similarly to how you submit tasks to an ExecutorService. You can submit two types of tasks. One is RecursiveAction that does not return any result. Another is RecursiveTask can return a result. They are a subclass of ForkJoinTask, and all of the tasks classes are abstract.
There are some generally useful features in JavaSE which are already implemented using the fork/join framework.
Arrays class methods: parallelSort()
java.util.stream package
fork/join framework pseudocode:
if (my portion of the work is small enough) do the work directly else split my work into two pieces
Concurrent collections are high concurrency and thread-safe collection implementation. For example, CopyOnWriteArrayList, ConcurrentHashMap.
Atomic Variables
The java.util.concurrent.atomic package defines classes that support atomic operations on single variable. All classes have get and set methods that work like reads and writes on volatile variables. A set has a happens-before relationship with any subsequent get on the same variable. The atomic compareAndSet method also has these memory consistency feature, as do the simple atomic arithmetic methods that apply to integer atomic variables.
Example of Basic Usage of AtomicInteger
AtomicIntegeratomicInteger=newAtomicInteger(0); atomicInteger.accumulateAndGet(10, (x, y)->{return x + y;}); System.out.println(atomicInteger.get());
Example of Using Atomic Variable instead of Synchronized Methods
The package java.util.concurrent includes a convenience class, ThreadLocalRandom, for applications that expect to use random numbers from multiple threads or ForkJoinTasks.
For concurrent access, using ThreadLocalRandom instead of Math.random() results in less contention and better performance.
Example of ThreadLocalRandom
intr= ThreadLocalRandom.current().nextInt(1, 10);
Conclusion
In multi-threads programming, allows multiple threads simultaneously access shared resources that may cause unpredicted results or corrupted values. For thread-safe, we need to consider two factors: thread interference and memory consistency errors.
Thread interference can be solved by atomic access (common using exclusive locks). Memory consistency errors can be solved by establishing a happens-before relationship (that between reads and writes the same variable).
We can simply use synchronized to solve the two thread-safe problems. But for high concurrency and thread-safe, we should use as few locks as possible, even have no locks. So we consider using a combination of explicit locks (reentrant locks), immutable objects, volatile variables, and atomic variables to solve the two thread-safe problems.
Locks may cause liveness problems: deadlock, starvation, and livelock. we can follow some coding principles to avoid these problems happens.
Guarded blocks common use for in threads cooperation. The most common example is the Producer-Consumer application.
Executors are for efficient thread creation and management.
Concurrent collections are high concurrency and thread-safe collection implementation.
This post will discuss content in Java container. It’s based Java SE 8.
Collection
Collection interface
The root interface in collection hierarchy. A collection represents a group of objects, known as its elements. Some collections allow duplicate elements and others do not. some are ordered and others unordered. The JDK does not provide any direct implementations of this interface. This interface is typically used to pass collections around and manipulate them where maximum generality is desired.
Methods of Collection
Basic Operations
boolean add(E e), boolean addAll(Collection<? extends E> c)
boolean remove(Object o), boolean removeAll(Collection<?> c)
void clear()
boolean contains(Object o), boolean contains(Collection<?> c)
int size()
boolean equals(Object o)
boolean isEmpty()
Iterator<E> iterator()
object[] toArray(), <T> T[] toArray(T[] a)
Advanced Operations
default Stream<E> stream()
default Stream<E> parallelStream()
default boolean removeIf(Predicate<? super E> filter)
boolean retainAll(Collection<?> c)
default Spliterator<E> spliterator()
AbstractCollection
This class provides a skeletal implementation of the Collection interface, to minimize the effort required to implement Collection interface.
Most of methods implementation of AbstractCollection depend on its abstract methods iterator() and size(). These two methods are the most important and useful for collection classes.
Non-abstract methods in this class may be overridden if its subclass implemented admits a more efficient implementation.
This interface control over where to insert. The user can access elements by their integer index, and search for elements in the list.
The List interface provides a special iterator, called a ListIterator, that allows element insertion add(E e) and replacement set(E e), and bidirectional access previous() in addition to the normal operations that the Iterator interface provides.
Additional methods of List
E get(int index)
int indexOf(Object o)
int lastIndexOf(Object o)
ListIterator listIterator()
void replaceAll(UnaryOperator operator)
E set(int index, E element)
void sort(Comparator c)
List<E> sublist(int fromIndex, int toIndex)
AbstractList
This class provides a skeletal implementation of the List interface to minimize the effort required to implement this interface backed by a “random access” data store (such as an array).
AbstractSequentialList
This class provides a skeletal implementation of the List interface to minimize the effort required to implement this interface backed by a “sequential access” data store (such as a linked list).
ArrayList & Vector
Resizable-array implementation of the List interface. Implements all optional list operations, and permits all elements, including null. In addition to implementing the List interface, this class provides methods to manipulate the size of the array that is used internally to store the list.
ArrayList class is roughly equivalent to Vector, except that it is unsynchronized.
Each ArrayList instance has a capacity. The capacity is the size of the array used to store the elements in the list. It is always at least as large as the list size. As elements are added to an ArrayList, its capacity grows automatically. The details of the growth policy are not specified beyond the fact that adding an element has constant amortized time cost.
An application can increase the capacity of an ArrayList instance before adding a large number of elements using the ensureCapacity operation. This may reduce the amount of incremental reallocation.
Note that this implementation is not synchronized. If multiple threads access an ArrayList instance concurrently, and at least one of the threads modifies the list structurally, it must be synchronized externally. If the list used in multithread, the list should be “wrapped” using the Collections.synchronizedList method. This is best done at creation time, to prevent accidental unsynchronized access to the list.
The iterators returned by this class’s iterator and listIterator methods are fail-fast: if the list is structurally modified at any time after the iterator is created, in any way except through the iterator’s own remove or add methods, the iterator will throw a ConcurrentModificationException. Thus, in the face of concurrent modification, the iterator fails quickly and cleanly, rather than risking arbitrary, non-deterministic behavior at an undetermined time in the future.
LinkedList
Doubly-linked list implementation of the List and Deque interfaces.
All of the operations perform as could be expected for a doubly-linked list. Operations that index into the list will traverse the list from the beginning or the end, whichever is closer to the specified index.
Note that this implementation is not synchronized. If multiple threads access a linked list concurrently, and at least one of the threads modifies the list structurally, it must be synchronized externally. If the list used in multithread, the list should be “wrapped” using the Collections.synchronizedList method. This is best done at creation time, to prevent accidental unsynchronized access to the list.
The iterators returned by this class’s iterator and listIterator methods are fail-fast. Same with ArrayList.
CopyOnWriteArrayList
What is CopyOnWriteArrayList
java.util.concurrent.CopyOnWriteArrayList a thread-safe variant of ArrayList in which all mutative operation (add, set, and so on) are implemented by making a fresh copy of the underlying array.
This is ordinarily too costly, but may be more efficient than alternatives when traversal operations vastly outnumber mutations, and is useful when you cannot or don’t want to synchronize traversals, yet need to preclude interference among concurrent threads. The “snapshot” style iterator method uses a reference to the state of the array at the point that the iterator was created. This array never changes during the lifetime of the iterator, so interference is impossible and the iterator is guaranteed not to throw ConcurrentModificationException. The iterator will not reflect additions, removals, or changes to the list since the iterator was created. Element-changing operations on iterators themselves (remove, set, and add) are not supported. These methods throw UnsupportedOperationException.
Why is the CopyOnWriteArrayList Thread-safe
All write operations have a lock. The write operations are serializable.
All mutative operations operate on new copy list and then update volatile list reference. Array update is an atomic operation and hence reads will always see the array in a consistent state.
Applicability
Updating this collection a lot will kill performance. You should only use this read when you are doing upwards of 90+% reads.
Consequences
The all read and write operations of CopyOnWriteArrayList are thread-safe. Some of methods have a ReentrantLock and copy list, some have not.
It highly improves concurrency of read operations in thread-safe condition, but write operations is very costly.
Questions
Q: Why all mutative operations need to copy list? Why read and traversal operation not need locks?
A: The key to this idea is that writes are accomplished by copying the existing value, modifying it, and replacing the reference. It also follows that once set the object pointed by the reference is always read only (i.e. no mutation is done directly on the object referred by the reference). Therefore, readers can access it safely without synchronization.
The Queue interface does not define the blocking queue methods, which are common in concurrent programming.
Queue implementations generally do not allow insertion of null elements, although some implementations do not prohibit. Null should not be inserted into a queue, as null is also used as a special return value by the poll method to indicate that the queue contains no elements.
Methods of Queue
insert
boolean add(E e) - Inserts element into this queue.
boolean offer(E e) - It is similar with add().
remove
E poll() - Retrieves and remove the head of this queue.
E reomve() - It is similar with poll()
examine
E element() - It is similar with peek().
E peek() - Retrieves, but does not remove, the head of this queue.
Deque interface
A group of elements supports element insertion and removal at both head and tail. Deque can also be used as LIFO (last-in-First-out) stacks.
The name deque is short for “double ended queue” and is usually pronounced “deck”.
java.util.concurrent.BlockingQueue is a queue additionally supports operations that wait for the queue to become non-empty when retrieve an element, and wait for space to become available in the queue when storing an element.
BlockingQueue implementations are designed to be used primarily for producer-consumer queues. A BlockingQueue can safely be used with multiple producers and multiple consumers.
BlockingQueue implementations are thread-safe. All queuing methods achieve their effects atomically using internal locks or other forms of concurrency control. However, the bulk Collection operations addAll, containsAll, retainAll and removeAll are not necessarily performed atomically. So it is possible, for example, for addAll(c) to fail after adding only some of the elements in c.
Methods of BlockingQueue
insert
boolean add(E e)
boolean offer(E e)
void put(E e) - blocks
boolean offer(E e, long time, TimeUnit unit) - blocks and times out
remove
boolean remove(Object o)
E poll()
E take() - blocks
E poll(long time, TimeUnit unit) - blocks and times out
It is the combination of BlockingQueue and Deque interface. You can use this interface for both blocking queues and blocking stacks.
TransferQueue interface
The TransferQueue interface is a refinement of the BlockingQueue interface in which producers can wait for consumers to receive elements. The BlockingQueue can only put element into queue (and block if queue is full). However, with TransferQueue, you can also block producer (whether the queue full or not) until other consumer threads receive your element.
Methods of TransferQueue
int getWaitingConsumerCount() - Returns an estimate of the number of consumers waiting to receive elements via take() or timed poll.
boolean hasWaitingConsumer() - Returns true if there is at least one consumer waiting to receive an element via take() or timed poll.
void transfer(E e) - Transfers the element to a consumer, waiting if necessary to do so.
boolean tryTransfer(E e) - Transfers the element to a waiting consumer immediately, if possible.
boolean tryTransfer(E e, long timeout, TimeUnit unit) - Transfers the element to a consumer if it is possible to do so before the timeout elapses.
Producer waiting to transfer: 0 Consumer waiting to comsume: 0 Consumer consumed: 0 Producer transfered: 0 Producer waiting to transfer: 1 Consumer waiting to comsume: 1 Consumer consumed: 1 Producer transfered: 1
Producer thread is blocked before consumer thread take product away.
AbstractQueue
This class provides skeletal implementations of some Queue operations. Its implemented methods actually implementing by calling abstract methods offer(), poll(), peek().
PriorityQueue
An unbounded priority queue based on a priority heap. The elements of the priority queue are ordered according to their natural Order (ascending order. sort by e1.compareTo(e2)), or by a Comparator provided at queue construction time, depending on which constructor is used. A priority queue does not permit null elements. A priority queue relying on natural Order also does not permit insertion of non-comparable objects.
Its implementation provides O(log(n)) time for the enqueuing and dequeuing methods (offer, poll, remove() and add); linear time for the remove(Object) and contains(Object) methods; and constant time for the retrieval methods (peek, element, and size).
Concurrent Queues
DelayQueue
An unbounded blocking queue of Delayed elements, in which an element can only be taken when its delay has expired. If no delay has expired there is no head and poll will return null.
DelayQueue<E extends Delay> is a PriorityQueue order by Delayed.
Methods of Delayed interface
long getDelay(TimeUnit unit) - Returns the remaining delay associated with this object, in the given time unit.
publicclassTest{ publicstaticvoidmain(String[] args)throws InterruptedException { DelayQueue<MyDelayed> delayQueue = newDelayQueue(); // offer delayQueue.offer(newMyDelayed("B", 3000L)); delayQueue.offer(newMyDelayed("A", 2L)); // poll // waiting for the delay is expired to poll Thread.sleep(2L); System.out.println("before poll"); // Output: MyDelayed{name='A', time=1587625828209} System.out.println(delayQueue.poll()); // Output is null, because the delay is not expired System.out.println(delayQueue.poll()); } }
SynchronousQueue
A blocking queue in which each insert operation must wait for a corresponding remove operation by another thread, and vice verse. A synchronous queue does not have any internal capacity.
The insert and remove operations of SynchronousQueue may block its thread. The insert and remove operations must occur at the same time, and the fast thread will block to wait for another operation thread.
publicstaticvoidmain(String[] args)throws InterruptedException { SynchronousQueue<Integer> synchronousQueue = newSynchronousQueue<>(); newThread(()->{ try { Thread.sleep(1000L); System.out.println("the put to wait for remove..."); synchronousQueue.put(1); System.out.println("end to put."); } catch (InterruptedException e) { e.printStackTrace(); } }).start(); newThread(()->{ try { System.out.println("the remove to wait for put..."); System.out.println(synchronousQueue.take()); System.out.println("end to remove."); } catch (InterruptedException e) { e.printStackTrace(); } }).start(); }
Output
the remove to wait for put... the put to wait for remove... end put. 1 end remove.
ConcurrentLinkedQueue
An unbound thread-safe queue based on linked nodes. This queue orders elements FIFO (first-in-first-out). A ConcurrentLinkedQueue is an appropriate choice when many threads will share access to a common collection.
A collection that contains no duplicate elements, and at most one null element.
Methods of Set interface
boolean add(E e), addAll(Collection c)
void clear()
boolean contains(Object o), containsALl(Collection c)
boolean equals(Object o)
int hashCode()
boolean isEmpty()
Iterator iterator()
boolean remove(Object o), removeAll(Collection c)
boolean retainAll(Collection c)
int size()
default Spliterator spliterator()
Object[] toArray(), T[] toArray(T[] a)
The Set interface does not have any get or find methods to find an element from the Set container. The only way to find the specified element is by using its iterator to traverse the Set.
AbstractSet Class
This class provides a skeletal implementation of the Set interface to minimize the effort required to implement this interface.
Implemented methods of Set interface:
equals(Object o)
hashCode()
removeAll(Collection c)
HashSet Class
This class implements the Set interface, backed by a hash table (actually a HashMap instance). It makes no guarantees as to the iteration order of the set.
This class offers constant time performance for the basic operation (add, remove, contains and size), assuming the hash function disperses the elements properly among the buckets.
This implementation is not synchronized.
This class’s iterator is fail-fast.
LinkedHashSet
Hash table and linked list implementation of Set interface, with predictable iteration order. This implementation differs from HashSet in that it maintains a doubly-linked list running through all of its entries. This linked list defines the iteration Order, which is insertion-order.
EnumSet Abstract Class
A specialized Set implementation for use with enum types. All of the elements in an enum set must come from a single enum type. Enum sets are represented internally as bit vectors. This representation is extremely compact and efficient.
SortedSet Interface
SortedSet is a Set that provides a total Order on its elements. The elements are ordered using their natural Order, or by a Comparator typically provided at sorted set creation time. The set’s iterator will traverse the set in ascending element order.
NavigableSet interface
NavigableSet is a SortedSet textended with navigation methods reporting closest matches for given search targets. Methods lower, floor, ceiling, and higher return elements respectively less than, less than or equal, greater than or equal, and greater than a given element, returning null if there is no such element. A NavigableSet may be accessed and traversed in either ascending or descending order.
SortedSet subSet(E fromElement, E toElement), NavigableSet subSet(E fromElement, boolean fromInclusive, E toElement, boolean toInclusive)
Reverse
NavigableSet descendingSet()
TreeSet Class
TreeSet is a NavigableSet implementation based on TreeMap. The elements are ordered using their natural ordering, or by a Comparator provided at set creation time, depending on which constructor is used.
This implementation provides guaranteed log(n) time cost for the basic operations (add, remove, and contains).
This implementation is not synchronized. If you want to use TreeSet in multithread environment, you can using the Collections.synchronizedSortedSet(new TreeSet()) to wrap it.
Concurrent Set
CopyOnWriteArraySet
CopyOnWriteArraySet is a Set that uses an internal CopyOnWriteArrayList for all of its operations.
It shares basic properties:
It is best suited for applications in which set sizes generally stay small, read-only operations vastly outnumber mutative operations, and you need to prevent interference among threads during traversal.
It is thread-safe.
Mutative operations (add, set, remove, etc.) are expensive since they usually entail copying the entire underlying array.
Iterators do not support the mutative remove operation.
Traversal via iterators is fast and cannot encounter interference from other threads. Iterators rely on unchanging snapshots of the array at the time the iterators were constructed.
ConcurrentSkipListSet
ConcurrentSkipListSet is a scalable concurrent NavigableSet implementation based on a ConcurrentSkipListMap. The elements of the set are kept sorted according to their natural ordering, or by a Comparator provided at set creation time, depending on which constructor is used.
This implementation provides expected average log(n) time cost for the contains, add, and remove operations and their variants. Insertion, removal, and access operations safely execute concurrently by multiple threads.
Beware that, unlike in most collections, the size method is not a constant-time operation. Because of the asynchronous nature of these sets, determining the current number of elements requires a traversal of the elements, and so may report inaccurate results if this collection is modified during traversal. Additionally, the bulk operations addAll, removeAll, retainAll, containsAll, equals, and toArray are not guaranteed to be performed atomically. For example, an iterator operating concurrently with an addAll operation might view only some of the added elements.
An object that maps keys to values. A map connot contain duplicate keys, and each key can map to at most one value.
The Map interface provides three collection views, a set of keys, collection of values, or set of key-value mappings.
Methods of Map interface
Basic
V get(Object key)
V put(K key, V value)
V remove(Object key)
int size()
boolean isEmpty()
void clear()
boolean containsKey(Object key)
boolean containsValue(Object value)
void putAll(Map m)
default V putIfAbsent(K key, V value)
default V getOrDefault(Object key, V defaultValue)
default boolean remove(Object key, Object value)
default V replace(K key, V value)
default boolean replace(K key, V oldValue, V newValue)
View
Set entrySet()
Set keySet()
Collection values()
Function
default V compute(K key, BiFunction remappingFunction) - Attempts to compute a mapping for the specified key and its current mapped value. computeIfAbsent(...), computeIfPresent(...)
default void forEach(BiConsumer action)
default V merge(K key, V value, BiFunction remappingFunction)
default void replaceAll(BiFunction function)
Note that map means mapping keys to values, it does not specify how to mapping. The hash function is one of the ways to map keys to values.
HashTable
This class implements a hash table, which maps keys to values.
Hashtable is synchronized. If a thread-safe implementation is not needed, it is recommended to use HashMap in place of Hashtable. If a thread-safe highly-concurrent implementation is desired, then it is recommended to use ConcurrentHashMap in place of Hashtable.
AbstractMap
This class provides a skeletal implementation of the Map interface, to minimize the effort required to implement this interface.
It simply implemented some methods of Map just using iterator, like get(), containsKey().
HashMap
Hash table based implementation of the Map interface. It permits null values and null key. This class makes no guarantees order of the map. The HashMap class is roughly equivalent to HashTable, except that it is unsynchronized and permit nulls.
This implementation is not synchronized. If want to use a HashMap object in multithread environment, you can using Collections.synchronizeMap() to wrap it.
EnumMap
A specialized Map implementation for use with enum type keys. All of the keys in an enum map must come from a single enum type that is specified, explicitly or implicitly, when the map is created.
Enum maps are maintained in the natural order of their keys (the order in which the enum constants are declared). This is reflected in the iterators returned by the collections views (keySet(), entrySet(), and values()).
Iterators returned by the collection views are weakly consistent: they will never throw ConcurrentModificationException and they may or may not show the effects of any modifications to the map that occur while the iteration is in progress.
Null keys are not permitted. Attempts to insert a null key will throw NullPointerException.
Like most collection implementations EnumMap is not synchronized. If want to use a EnumMap object in multithread environment, you can using Collections.synchronizeMap() to wrap it.
IdentityHashMap
This class implements the Map interface with a hash table, using reference-equality in place of object-equality when comparing keys (and values). In other words, in an IdentityHashMap, two keys k1 and k2 are considered equal if and only if (k1==k2). For example, the key new String(“a”) and key “a” will different key in IdentityHashMap, but it’s same key in HashMap.
This class is not a general-purpose Map implementation! While this class implements the Map interface, it intentionally violates Map's general contract, which mandates the use of the equals method when comparing objects. This class is designed for use only in the rare cases wherein reference-equality semantics are required.
This implementation is not synchronized. If want to use a IdentityHashMap object in multithread environment, you can using Collections.synchronizeMap() to wrap it.
WeakHashMap
Hash table based implementation of the Map interface, with weak keys. An entry in a WeakHashMap will automatically be removed when its key is no longer in ordinary use. More precisely, the presence of a mapping for a given key will not prevent the key from being discarded by the garbage collector, that is, made finalizable, finalized, and then reclaimed. When a key has been discarded its entry is effectively removed from the map, so this class behaves somewhat differently from other Map implementations.
Each key object in a WeakHashMap is stored indirectly as the referent of a weak reference. Therefore a key will automatically be removed only after the weak references to it, both inside and outside of the map, have been cleared by the garbage collector.
Like most collection classes, this class is not synchronized. A synchronized WeakHashMap may be constructed using the Collections.synchronizedMap method.
Applicability
When you want to automatically remove useless objects from map, you can use WeakHashMap. You can manually remove elements of any types of map, but if don’t or you forget it. It may cause a memory leak.
How does the WeakHashMap Implement?
java.lang.ref.WeakReference
SortedMap interface
A Map that further provides a total ordering on its keys. The map is ordered according to the natural ordering of its keys, or by a Comparator typically provided at sorted map creation time. This order is reflected when iterating over the sorted map’s collection views (returned by the entrySet, keySet and values methods). Several additional operations are provided to take advantage of the ordering.
NavigableMap interface
A SortedMap extended with navigation methods returning the closest matches for given search targets. Methods lowerEntry, floorEntry, ceilingEntry, and higherEntry return Map.Entry objects associated with keys respectively less than, less than or equal, greater than or equal, and greater than a given key, returning null if there is no such key. Similarly, methods lowerKey, floorKey, ceilingKey, and higherKey return only the associated keys. All of these methods are designed for locating, not traversing entries.
TreeMap
A Red-Black tree based NavigableMap implementation. The map is sorted according to the natural ordering of its keys, or by a Comparator provided at map creation time, depending on which constructor is used.
This implementation provides guaranteed log(n) time cost for the containsKey, get, put and remove operations.
This class is not synchronized. A synchronized TreeMap may be constructed using the Collections.synchronizedMap method.
Concurrent Maps
ConcurrentHashMap
What is it
A concurrent map implements by hash table, and supports full concurrency of retrievals and high concurrency for updates. It likes Hashtable, but it has more concurrency.
A hash table supporting full concurrency of retrievals and high expected concurrency for updates. This class obeys the same functional specification as Hashtable, and includes versions of methods corresponding to each method of Hashtable. However, even though all operations are thread-safe, retrieval operations do not entail locking, and there is not any support for locking the entire table in a way that prevents all access. This class is fully interoperable with Hashtable in programs that rely on its thread safety but not on its synchronization details.
ConcurrentSkipListMap
A scalable concurrent ConcurrentNavigableMap implementation. The map is sorted according to the natural ordering of its keys, or by a Comparator provided at map creation time, depending on which constructor is used.
Utility Classes
Collections
What Is It
This class consists of static methods that operate on or return collections.
static List emptyList(), emptyMap(), emptySet(), …
static List nCopies(int n, T o)
static Set singleton(T o), singletonList(T o), singletonMap(K key, V value)
Arrays
What Is It
This class contains various static methods for manipulating arrays (such as sorting and searching). This class also contains a static factory that allows arrays to be viewed as lists.
Methods of Arrays
Manipulations
static int binarySearch(byte[] a, byte key), binarySearch(byte[] a, int fromIndex, int toIndex, byte key), binarySearch(char[] a, char key), binarySearch(int[] a, int key), …
static void setAll(int[] a, IntUnaryOperator generator), …
static void parallelSetAll(int[] a, IntUnaryOperator generator), …
static void sort(int[] a), sort(int[] a, int fromIndex, int toIndex), …
static void parallelSort(int[] a), parallelSort(int[] a, int fromIndex, int toIndex), parallelSort(byte[] a)
static Spliterator spliterator(int[] a), …
static Stream stream(int[] a), …
static toString(int[] a), …
Transforms
static int[] copyOf(int[] original, int newLength), copyOf(byte[] original, int newLength), …
static int[] copyOfRange(int[] original, int from, int to), copyOfRange(byte[] original, int from, int to), …
Creations
static List asList(T... a)
Container Features
Traversal
Fail Fast
when one thread using iterator for traversal, updating the container object by add, remove operations, the iterator thread will throw a ConcurrentModificationException.
Order
Natural Order
Elements order by method compareTo() of the class.
Consistency
Happens-before
This relationship is simply a guarantee that memory writes by one specific statement are visible to another specific statement in multiple threads.
For example, an variable int counter=0 shared in thread A and thread B. If thread A increment counter by counter++, thread B print counter by System.out.println(counter), the printed value would be “1”.
Concurrent Collections
Besides Queues, this package supplies Collection implementations designed for use in multithreaded contexts: ConcurrentHashMap, ConcurrentSkipListMap, ConcurrentSkipListSet, CopyOnWriteArrayList, and CopyOnWriteArraySet. When many threads are expected to access a given collection, a ConcurrentHashMap is normally preferable to a synchronized HashMap, and a ConcurrentSkipListMap is normally preferable to a synchronized TreeMap. A CopyOnWriteArrayList is preferable to a synchronized ArrayList when the expected number of reads and traversals greatly outnumber the number of updates to a list.
The “Concurrent” prefix used with some classes in this package is a shorthand indicating several differences from similar “synchronized” classes. For example java.util.Hashtable and Collections.synchronizedMap(new HashMap()) are synchronized. But ConcurrentHashMap is “concurrent”. A concurrent collection is thread-safe, but not governed by a single exclusion lock. In the particular case of ConcurrentHashMap, it safely permits any number of concurrent reads as well as a tunable number of concurrent writes. “Synchronized” classes can be useful when you need to prevent all access to a collection via a single lock, at the expense of poorer scalability. In other cases in which multiple threads are expected to access a common collection, “concurrent” versions are normally preferable. And unsynchronized collections are preferable when either collections are unshared, or are accessible only when holding other locks.
Most concurrent Collection implementations (including most Queues) also differ from the usual java.util conventions in that their Iterators and Spliterators provide weakly consistent rather than fast-fail traversal:
they may proceed concurrently with other operations
they are guaranteed to traverse elements as they existed upon construction exactly once, and may (but are not guaranteed to) reflect any modifications subsequent to construction.
ordered, or disordered (insert Order, natural Order, comparator Order)
null value
traverse operation is fail-fast or weakly consistent
performance
read, write and traversal operations time and space cost.
thread safe: concurrency, consistency
synchronized
concurrent
consistent
How to select a effective container class to use
List
ArrayList and LinkedList are common lists. ArrayList implemented by array, it is fast to random access, but slow to insert and remove. LinkedList implemented by nodes, it is fast to insert and remove, but slow to random access.
Vector is thread-safe list by synchronized. If you want to use list in multithread environment, you can choose it.
CopyOnWriteArrayList is also thread-safe list, and It is more concurrent than synchronized, but its write operations is very costly. If you want to use list in multithread environment with high concurrent, and operations are almost read operations(90%+), you can choose it. You will have high concurrent in read and write operations.
Stack
If you want to use stack in single thread environment, you can use ArrayDeque and LinkedList, or construct by wrapper ArrayList or array. Selection priority: ArrayDeque > LinkedList
If you want to use stack with thread-safe. You can use Stack or ConcurrentLikedDeque. The Stack has strictly consistent and poor concurrency. the ConcurrentLinkedDeque has high concurrency and weakly consistent.
Queue
In single thread environments, the ArrayDeque is the most common queue. If you want a queue has priority, you can use PriorityQueue.
In multithread environments, There have two type queues: blocking queue and concurrent queue. Blocking queues is commonly using for the producer-consumer scenario. About blocking queue selection, blocking queues for general using you can select LinkedBlockingDeque, ArrayBlockingQueue, and LinkedBlockingQueue. There are many special features blocking queues LinkedTransferQueue, PriorityBlockingQueue, DelayQueue, and SynchronousQueue that for special using scenario. You can use it according to their features.
Another thread-safe queue type is concurrent queues that have high concurrency. Concurrent queues ConcurrentLinkedQueue and ConcurrentLinkedDeque are very similar, just ConcurrentLinkedDeque can use as both queue and stack.
Set
In single thread environments, the HashSet is the most common set. Other sets using in single thread environments are LinkedHashSet and TreeSet. The LinkedHashSet is similar to HashSet, but it has additional functionality that keeps elements insert order by linking them up. The TreeSet has lower performance than HashSet, but it can keep elements order with natural order or comparator.
In multithread environments, We can use thread-safe sets CopyOnWriteArraySet and ConcurrentSkipListSet. The CopyOnWriteArraySet only use when most of operations (90%+) are reads. The ConcurrentSkipListSet use when you want to keep elements Order.
Map
In single thread environments, the HashMap is the most common map. It’s for general using. Other maps using in single thread environments are LinkedHashMap, EnumMap, IdentityHashMap, WeakHashMap, TreeMap. The LinkedHashMap is similar to HashMap, but it keeps elements insert order. The EnumMap use when all keys of map are from an Enum types. The IdentityHashMap and WeakHashMap are rarely using, just using in special scenario according to their features. The TreeMap is lower performance than HashMap, but it keep elements of map order with natural order or comparators.
In multithread environments, you can choose Hashtable or concurrent maps. The Hashtable is a thread-safe map by synchronized. It’s strictly consistent and lowly concurrent. If you want to thread-safe and highly concurrent map, you can choose concurrent maps ConcurrentHashMap or ConcurrentSkipListMap. The ConcurrentHashMap is similar to HashMap, but have more concurrent. The ConcurrentSkipListMap have high concurrency and keeps elements order.
String
String is the most common class for representing a string. It’s final or immutable. If you want to concatenate multiple strings to one, you better use StringBuilder, it doesn’t generate middle strings that saving memory cost.
The StringBuffer is similar to StringBuilder, the only difference between StringBuffer and StringBuilder that is StringBuffer is thread-safe by synchronized.
This post will discuss content in Java lambda and stream. It’s based Java SE 8.
Stream
Stream
What is Stream
A sequence of elements supporting sequential and parallel aggregate operations.
Special Stream class (like IntStream) that they provide additional methods for its type of stream (like sum() of IntStream)
To perform a computation, stream operations are composed into a stream pipeline. A stream pipeline consist of source, zero or more intermediate operations, and a terminal operation or forEach(Consume). Streams are lazy; computation on the source data is only performed when the terminal operation is initiated, and source elements are consumed only as needed.
Streams do not provide a means to directly access or manipulate their elements, and are instead concerned with declaratively describing their source and the computational operations which will be performed in aggregate on that source. However, if the provided stream operations do not offer the desired functionality, the BaseStream.iterator() and BaseStream.spliterator() operations can be used to perform a controlled traversal.
A stream should be operated on (invoking an intermediate or terminal stream operation) only once. Since some stream operations may return their receiver rather than a new stream object, it may not be possible to detect reuse in all cases.
Methods of Stream interface
Intermediate Operations
Filter
Stream distinct()
Stream filter(Predicate<? super T> predicate)
Stream limit(long maxSize)
Stream skip(long n)
Map (or Convert)
Stream flatMap(Function mapper)
DoubleStream flatMapToDouble(Function mapper)
IntStream flatMapToInt(Function mapper)
LongStream flatMapToLong(Function mapper)
Stream map(Function mapper)
DoubleStream mapToDouble(ToDoubleFunction mapper)
IntStream mapToInt(ToIntFunction mapper)
LongStream mapTOLong(ToLongFunction mapper)
Operate stream elements
Stream peek(Consumer action)
Stream sorted()
Stream sorted(Comparator comparator)
Terminal Operations
Predicate
boolean allMatch(Predicate predicate)
boolean anyMatch(Predicate predicate)
boolean noneMatch(Predicate predicate)
Optional findAny()
Optional findFirst()
Collecting. Get Converted Object Value
R collect(Collector<? super T,A,R> collector)
Object[] toArray()
A[] toArray(IntFunction generator)
Reduction. Get Computing Result
long count()
Optional max(Comparator comparator)
Optional min(Comparator comparator)
Optional reduce(BinaryOperator accumulator)
T reduce(T identity, BinaryOperator accumulator)
Traverse
void forEach(Consumer action)
void forEachOrdered(Consumer action)
Static Methods
static Stream empty()
static <T> Stream.Builder<T> builder()
static Stream concat(Stream<? extends T> a, Stream<? extends T> b)
A mutable reduction operation that accumulates input elements into a mutable result container, optionally transforming the accumulated result into a final representation after all input elements have been processed. Reduction operations can be performed either sequentially or in parallel.
Collectors
Implementations of Collector that implement various useful reduction operations, such as accumulating elements into collections, summarizing elements according to various criteria.
Function
Function
Function is a functional Interface
R apply(T t)
Example
Function<Integer, Double> half = a -> a/2.0; System.out.println(half.apply(10));
Predicate
Predicate is a functional Interface
boolean test(T t)
Example
Predicate<Integer> predicate = i -> {return i > 1;}; System.out.println(predicate.test(2));
publicbooleantestPredicate(int val, Predicate<Integer> predicate){ return predicate.test(val); } publicvoidtest(){ assertTrue(testPredicate(2, i -> i > 1)); assertFalse(testPredicate(1, i -> i > 1)); }
You use lambda expressions to create anonymous methods. Sometimes, however, a lambda expression does nothing but call an existing method. In those cases, it’s often clearer to refer to the existing method by name. Method references enable you to do this; they are compact, easy-to-read lambda expressions for methods that already have a name.
Kinds of Method References
There are four kinds of method references:
Kind
Example
Reference to a static method
ContainingClass::staticMethodName
Reference to an instance method of a particular object
containingObject::instanceMethodName
Reference to an instance method of an arbitrary object of a particular type
publicstaticvoidmain(String[] args) { // Reference to an instance method of an arbitrary object of a particular type Function<People, String> getName = People::getName; System.out.println(getName.apply(newPeople("Tom"))); // Tom
// Reference to an instance method of a particular object Peoplepeople=newPeople("Jack"); Function<String, Boolean> compareName = people::compareName; System.out.println(compareName.apply("Jack")); // true
// Reference to a static method Function<String, String> convertToUpperCase = People::convertToUpperCase; System.out.println(convertToUpperCase.apply("abc")); // ABC
// Reference to a constructor Function<String, People> getInstance = People::new; System.out.println(getInstance.apply("John").getName()); // John } }
Java Util
Optional
What is Optional?
Convert a object to Optional object, if the value is not null, isPresent() will return true and get() return the value; if the value is null, isPresent() will return false and get() return empty Optional object.
In short, you can either get value or empty optional from an optional object, and you never get null.
Optional Methods
Static Methods
static Optional empty() // get empty Optional
static Optional of(T value) // if value is null, it will throw NullPointerException
static Optional ofNullable(T value) // if value is null, return empty Optional
Spliterator is for traversing and partitioning elements of a source. The source of elements covered by a Spliterator could be, for example, an array, a Collection, an IO channel, or a generator function.
When used in a parallel programming solution, bear in mind that the individual Spliterator is not thread safe; instead the parallel processing implementation must insure that each individual Spliterator is handled by one thread at a time. A thread calling the trySplit() of a Spliterator, may hand over the returned new Spliterator to a separate thread for processing. This follows the idea of decomposition of a larger task into smaller sub-tasks that can be processed in parallel individually of each other.
Note Spliterator likes Stream every operate only once. Can’t traverse the same spliterator more than once.
Stream is operations on collections, and is similar to multiple times “for loop”, but it’s more concise than multiple times “for loop” and it provides many convenient operations implementation. Additionally, it can sequential or parallel operating container elements.
Functional Interface represents the class that can be use lambda expression.
Lambda Expression is similar to implementation of anonymous inner class, but it’s more concise than anonymous inner class.
Method Reference is similar to lambda expression, but it’s more concise than Lambda Expression. It is a replacement of method implementation of an anonymous inner class when they have the same input and output of the method. It’s more convenient and concise than Lambda Expression.
This post will discuss content in Java IO streams. It is based Java SE 8.
Input and Output Stream Hierarchy
Input Stream
Input Stream Methods
int available(): return available to read the remaining byte size.
void close(): close the input stream.
void mark(int readlimit): marks the current position in this input stream.
readlimit: the number of bytes that can be read from the stream after setting the mark before the mark becomes invalid. Note the parameter for some subclass of InputStream has no meaning.
boolean markSupported(): return if this input stream supports the mark and reset methods.
int read(): reads the next byte of data.
int read(byte[] b): reads some bytes from input stream and stores them into buffer array b.
int read(byte[] b, int off, int len): reads up to len bytes of data from input stream into array of bytes.
void reset(): repositions this stream to the position at mark (default 0)
long skip(long n): skips bytes of data.
What does Input Stream actually do?
InputStream only reads data from front to back. It can skip some data to read. It can back to specified position to read again.
InputStream can read bytes one by one or read some bytes into byte array once.
If there is no available data in InputStream, the calling one of the read methods will return -1.
int read(byte b[]) => int read(byte b[], int offset, int len), abstract int read()
read() methods can implement directly (e.g ByteArrayInputStream) or implement by calling the native method (e.g FileInputStream).
InputStream Types
ByteArrayInputStream: Byte array as data
ByteArrayInputStream(byte[] b)
FileInputStream: File as data
FileInputStream(File file)
ObjectInputStream: Deserializes primitive data and objects previously written using an ObjectOutputStream.
ObjectInputStream(InputStream in)
readObject()
PipedInputStream: A piped input stream should be connected to a piped output stream; the piped input stream then provides whatever data bytes are written to the piped output stream.
PipedInputStream()
connect(PipedOutputStream src)
StringBufferStream(Deprecated): String as data.
StringBufferInputStream(String s)
SequenceInputStream: Logical concatenation of other input streams.
long getFreeSpace(), long getTotalSpace(), long getUsableSpace(), long lastModified(), long length()
FileDescriptor
Whenever a file is opened, the operating system creates an entry to represent this file and stores its information. Each entry is represented by an integer value and this entry is termed as file descriptor. [2]
Basically, Java class FileDescriptor provides a handle to the underlying machine-specific structure representing an open file, an open socket, or another source or sink of bytes. [2]
The applications should not create FileDescriptor objects, they are mainly used in creation of FileInputStream or FileOutputStream objects to contain it. [2]
File Paths
Get file, absolute filepath and inputStream in classpath, e.g src/resources/test.txt
You can get InputStream by using ClassLoader.getResourceAsStream() method.
getClass().getClassLoader().getResource("test.txt") //relative path getClass().getResource("/test.txt")); //note the slash at the beginning
Get file, absolute file path and inputStream in JAR, e.g src/resources/test.txt
This is deliberate. The contents of the “file” may not be available as a file. Remember you are dealing with classes and resources that may be part of a JAR file or other kind of resource. The classloader does not have to provide a file handle to the resource, for example the jar file may not have been expanded into individual files in the file system. [3]
Anything you can do by getting a java.io.File could be done by copying the stream out into a temporary file and doing the same, if a java.io.File is absolutely necessary. [3]
Result: You can get input stream and File by ClassLoader, but you can’t get right usable absolute file path. If you want get a usable absolute path in JAR, you can copy resource file stream to create a new temporary file, then get absolute file path of the temporary file.
while ((read = input.read(bytes)) != -1) { out.write(bytes, 0, read); } out.close(); file.deleteOnExit(); } catch (IOException ex) { Exceptions.printStackTrace(ex); } } else { //this will probably work in your IDE, but not from a JAR file = newFile(res.getFile()); }
System.out.println(file.getAbsolutePath());
Summary
IO Streams Functionality
InputStream or Reader it read data from somewhere, e.g byte or char array, file, pipe. It can read single-unit data from the stream to return or read multiple units data from the stream to store into an array every time. read() methods can implement by java code or implements by calling native method. Filter streams use the Decorator Pattern to add additional functionality (e.g unread) that implements by internal buffer array to other stream objects.
OutputStream or Writer it write data to somewhere, e.g byte or char array, file, pipe.
IO Stream Types
Byte or Char Array
File
Object
Piped
Sequence:
Filter
Buffered: Operating with buffer array.
DataInput, DataOutput: Operating with primitive Java types
Pushback: Add unread functionality.
Print:
LineNumber
Others
class java.nio.CharBuffer using in method int read(CharBuffer target)
interface java.lang.CharSequence using in method Writer append(CharSequence csq)
Questions
InputStream vs Reader? What the real difference between them? Who has more efficient?
Stream is operating bytes, and reader or writer is operating characters. Writing streams of raw bytes such as image data using OutputStream. Writing streams of characters using Writer.
CharSequence vs String
A CharSequence is a readable sequence of char values. You can call chars() method get the InputStream.
来自 query string 和 post body 的数据是聚合在 request parameter set 中的。query string 数据表示在post body 数据之前。如:一个请求的 query string 是 http://xxx?a=hello,它的 post body 是 a=goodbye&a=world,参数集的结果将是 a=(hello, goodbye, world)。
Post 表单数据转换为 parameter set 的条件:
它是一个 HTTP or HTTPS 请求。
HTTP method 是 POST。
content type 是 application/x-www-form-urlencoded
Servlet 已对请求对象上的任何 getParameter 方法族进行了初始调用。
如果上面的条件没有全部满足,post 请求的 form data 不会包含在 parameter set中,但 post data 依然可以通过 request object 的 input stream 中获取。如果所有条件都满足,post form data 将不再能从 request object 的 input stream 中读取。parameter 只能表现为 form data 和 input stream 两种方式之一。
编写 bad code 可能会导致严重的后果。bad code 会使得团队的生产力持续降低。bad code 可能导致软件无法维护,以及存在大量 bug,最终导致软件无法正常运行。最后,不得不重新设计代码,建立一个新的团队重构旧的系统,以及跟进旧系统的改变。
产生 bad code 的原因
产生 bad code 的原因有很多,如:1)需求的改变。2)时间规划太紧,没有足够的时间去做得更好。3)对一个程序很疲倦,想要早点结束。4)手上堆积了很多其它的事,想要赶紧做完它,然后做其它事情。5)管理者的管理不当。…
作为程序开发者,我们不能一味地抱怨外部原因,我们更应该反思我们自己,我们能否做得更好。1)当软件的时间规划和安排不合理时,我们应该及时的反馈我们的想法。大部分管理者想要看到事实,以及想要 good code。2)在开发过程中,我们应该保持专业的态度,持续保证代码的整洁,就像医生做手术前要洗手一样,多花一点时间保持代码的整洁,让保证代码不会变成 bad code。唯一保证 deadline 的方法就是在任何时候尽可能地保持代码地整洁,为了加快速度编写混乱的代码最终会拖垮你的进度。
// Check to see if the employee is eligible for full benefits if ((employee.flog & HOURLY_FLAG) && (employee.age > 65))
代替为
if (employee.isEligibleForFullBenefits())
Good Comments
有些注释时有必要的或有益的。始终记住真正好的注释是找到一种方式不写注释。
常见的有必要的注释
Legal Comments。版权和作者声明是有必要的和合理的。如 // Copyright (C) 2003,2004,2005 by Object Mentor, Inc. All rights reserved.
Explanation of Intent。解释为什么这么做。如 // we are greater because we are the right type.
Warning of Consequence。警告注释。如 // Don't run unless you, have some time to kill
TODO Comments。TODO 一般用于此刻没时间做或者暂时做不了的事。它解释代码的问题和未来应该做什么。如 //TODO-MdM these are not needed // We expect this to go away when we do the checkout model
Amplification。详述注释。用于详述某些重要的事情,其他人可能认为不重要的事。// the trim is real important. It removes the starting spaces that could cause the item to be recognized as another list.
Formatting
Code formatting 是重要的,编码风格和可读性会持续影响软件的维护性和扩展性。
Vertical Formatting
行数。一个源码文件不应该超过 500 行,一般最好 200 行左右。小文件通常比大文件更容易理解。
自顶向下阅读。最上面的方法提供高层次的概念和算法。细节应该是从上到下递增的。
垂直的间隔。不同概念的一组代码应该用空行分隔。
垂直的密度。垂直密度暗示了紧密的关系,相关性高得代码应该紧密排布在一起。
垂直的距离。紧密关联的概念应该保持垂直方向接近的。1)变量声明语句应该尽可能接近它们使用的地方。2)实例变量应该声明在 class 的顶端。3)依赖的方法。一个方法调用另一个方法,它们应该在垂直方向相互接近的,调用者应该在被调者的上面。4)概念上类似。相似的内容应该在垂直方向相互接近的。如方法的重载,相同的方法名,不同的参数的方法,它们的功能是类似的。
publicclassClient{ pubilc staticvoidmain(String[] args){ Element[] elements = newElement[]{newConcreteElementA(1), newConcreteElementB(2)}; // operation 1 in elements object strucutre VisitorsumVisitor=newComputeSumConcreteVisitor1(); for (Element e : elements){ e.accept(sumVisitor); } intsum= sumVisitor.getSum(); // operation 2 in elements object strucutre VisitorproductVisitor=newComputeProductConcreteVisitor2(); for (Element e : elements){ e.accept(productVisitor); } intproduct= productVisitor.getProduct(); } }