java and memo

Posted by phat_hip_prog on Sun, 19 May 2019 12:40:08 +0200

Insecure "Check before Execution", code form as follows:

	if(Conditional satisfaction){   //Thread security issues are prone to occur here
		//doSomething
	}else{
		//doOther
	}

Read-modify-write

Atomic operation: Using CAS technology, first read the value A from V and calculate the new value B according to A, then change the value of V from A to B atomically by CAS (as long as no thread changes the value of V to other values during this period).

voliate modifier:

Atomic classes in the java.util.concurrent.atomic package, such as

  • Atomic Integer: int's atomic operation
  • Atomic long: long Atomic Operation
  • Atomic boolean: boolean atomic operation
  • AtomicReference <V>: Atomic operations of reference types

Using CAS to realize non-blocking stack:

  	public class ConcurrentStack<E> {
  		AtomicReference<Node<E>> top = new AtomicReference<Node<E>>();
  		public void push(E item) {
  			Node<E> newHead = new Node<E>(item);
  			Node<E> oldHead;
  			do {
  				oldHead = top.get();
  				newHead.next = oldHead;
  			}while(!top.compareAndSet(oldHead, newHead));
  		}
  		public E pop() {
  			Node<E> oldHead;
  			Node<E> newHead;
  			do {
  				oldHead = top.get();
  				if(oldHead == null){
  					return null;
  				}
  				newHead = oldHead.next;
  			}while(!top.compareAndSet(oldHead, newHead));
  			return oldHead.item;
  		}
  		private static class Node<E> {
  			public final E item;
  			public Node<E> next;
  			public Node(E item) {
  				this.item = item;
  			}
  		}
  	}

synchronized fine-grained control code block:

	public class CachedFactorizer implements Servlet {
		@GuardedBy("this") private BigInteger lastNumber;
		@GuardedBy("this") private BigInteger[] lastFactors;
		@GuardedBy("this") private long hits;
		@GuardedBy("this") private long cacheHits;

		public synchronized long getHits() {
			return hits;
		}

		public synchroized double getCacheHitRatio() {
			return (double) cacheHits / (double) hits;
		}

		public void service(ServletRequest req, SevletResponse resp) {
			BigInteger i = extractFromRequest(req);
			BigInteger[] factors = null;
			synchroized (this) {
				++ hits;
				if(i.equals(lastNumber)) {
					++ cacheHits;
					factors = lastFactors.clone();
				}
			}

			if(factors == null) {
				factors = factor(i);
				synchronized (this) {
					lastNumber = i;
					lastFactors = factors.clone();
				}
			}
			encodeIntoResponse(resp, factors);
		}

	}

Thread security of jsp built-in objects

Of the eight class variables provided in jsp,

  • OUT,REQUEST,RESPONSE,SESSION,CONFIG,PAGE,PAGECONXT are thread-safe.
  • Because each thread corresponds to a request, the response object is different.
  • There is no sharing problem. APPLICATION is used throughout the system, so it is not thread-safe.

Thread security for other objects

  • Member variables: Member variables are allocated on the stack and shared by all threads belonging to the instance, so they are not thread-safe.
  • Local variables: Local variables are allocated in the stack, because each thread has its own stack, so it is thread-safe.
  • Static classes: Static classes can be used directly without being instantiated, and are not thread-safe
  • External resources: In the program there will be multiple threads simultaneously operating the same resources (such as database access), at this time also pay attention to synchronization.

java memory model: JMM

jmm is mainly designed to specify some relationships between threads and memory. According to the design of jmm,

  • There is a main memory in the system. All instance variables in java are stored in the main memory and shared by all threads.
  • Each thread has its own working memory, which consists of a cache and a stack.
  • The cache holds copies of variables in main memory, and the cache may not always synchronize with the main memory. That is to say, changes to variables in the cache may not be written to the main memory immediately.
  • The local variables of threads are stored in the stack. Threads can not directly access the variables in the stack.

synchronized keywords:

synchronized is the keyword of Java language. When modifying a method or a code block, it can ensure that at most one thread executes the code at the same time.

  1. When two concurrent threads access this synchronized(this) block of synchronized code in the same object, only one thread can be executed in one time. Another thread must wait for the current thread to execute the code block before it can execute the code block.
  2. However, when a thread accesses a synchronized(this) block of synchronized code in an object, another thread can still access a non-synchronized(this) block of synchronized code in that object.
  3. Especially critical is that when a thread accesses a synchronized(this) block of synchronized code in an object, access to all other synchronized(this) blocks of synchronized code in the object will be blocked by other threads.
  4. Point 3 also applies to other synchronization blocks. That is, when a thread accesses a synchronized(this) block of synchronized code for an object, it acquires the object lock for that object. As a result, other threads temporarily block access to all synchronization code parts of the object object object.
  5. The above rules also apply to other object locks.
A few points need to be made clear
  1. Whether synchronized keywords are added to methods or objects, the locks it obtains are objects, not a piece of code or function as locks. And synchronization methods are likely to be accessed by objects in other threads.
  2. Each object has only one lock associated with it.
  3. Synchronization is costly and may even cause deadlocks, so try to avoid unnecessary synchronization control.
  4. There is also a lock for each class, so the synchronized static method can prevent concurrent access to static data within the scope of the class.

Thread pool

Executors'static method, which responds to requests, reduces the creation time of threads and improves the response speed. When there is "new Thread(runnable).start()" in the program, you can consider using Execute instead of Thread.

Executor that starts a new thread for each request:

public class ThreadPerTaskExecutor implemets Executor {
	public void execute(Runnable r){
		new Thread(r).start();
	}
}

Several large thread pools built into jdk:

  1. newCachedThreadPool(): When executing, first find out if there are the same threads in the thread pool, and then use the threads in the thread pool, otherwise create a new thread. After the thread is executed, there will be no other operation for a certain period of time, and the thread will be terminated.
  2. New Fixed ThreadPool (int nThreads): Create a specified number of threads, and if the number of thread pools reaches the line at execution time, the program will wait for other threads to complete execution before execution.
class TaskExecutionWebServer {
	private static final int NTHREADS = 100;
	private static final Executor exec = Executors.newFixedThreadPool(NTHREADS);
	public static void main(String[] args){
		ServerSocket socket = new ServerSocket(80);
		while(true) {
			final Socket connection = socket.accpet();
			Runnable task = new Runnable() {
				public void run() {
					handleRequest(connection);
				}						
			};
			exec.execute(task);
		}
	}
}
  1. newSingleThreadExecutor(): FixedThreadPool equivalent to 1 thread count, single thread execution.
  2. New Scheduled ThreadPool (): Create a fixed-length thread pool and perform tasks in a delayed or timed manner, similar to Timer.

DelayQueue: Delay Queue

Completion Service: Executor and BlockingQueue

Completion Service integrates Executor and BlockingQueue functions, submits Callable tasks to it for execution, and then uses methods such as take and poll similar to queue operations to obtain completed results, which will be encapsulated as Future upon completion. Executor Completion Service implements the Completion Service and delegates the computational part to an Executor.

public abstract class Renderer {
    private final ExecutorService executor;
    public Renderer(ExecutorService executor) {
        this.executor = executor;
    }
    void renderPage(CharSequence source) {
        final List<ImageInfo> info = scanForImageInfo(source);
        CompletionService<ImageData> completionService =
                new ExecutorCompletionService<ImageData>(executor);
        for (final ImageInfo imageInfo : info)
            completionService.submit(new Callable<ImageData>() {
                public ImageData call() {
                    return imageInfo.downloadImage();
                }
            });
        renderText(source);
        try {
            for (int t = 0, n = info.size(); t < n; t++) {
                Future<ImageData> f = completionService.take();   	//Get the Future object
                ImageData imageData = f.get();						//Threads are blocked here, waiting for the download to complete before running
                renderImage(imageData);
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        } catch (ExecutionException e) {
            throw launderThrowable(e.getCause());
        }
    }			
}

Thread pool creation example:

	ExecutorService exec = Executors.newCachedThreadPool();
	exec.xxxXxxx();       //Call related methods
  • ExecutorService Interface: Life Cycle Management Approach to Task Execution:
  • Shutdown method: will perform a gentle shutdown process: no longer accept new tasks, while waiting for submitted tasks
  • The shutdownNow method: Execute a rough shutdown process: try to cancel all running tasks and no longer start tasks that have not yet started in the queue.
Examples: web servers that support shutdown operations:
class ListcycleWebserver {
	private final ExecutorSevice exec = ... ;
	public void start() throws IOException {
		ServerSocket socket = new ServerSocket(80);
		while (! exec.isShutdown()){
			try{
				final Socket conn = socket.accep();
				exec.execute(new Runnable(){
					public void run(){
						handleRequest(conn);
					}
				});
			}catch(RejectedExecutionException e){
				if(!exec.isShutdown()){
					log("task submission rejected", e);
				}
			}
		}
	}
	public void stop() {
		exec.showdown();
	}
	void handleRequest(Socket connection) {
		Request req = readRequest(connection);
		if(isShutdownReqeust(req)){
			stop();
		}else{
			dispatchRequest(req);
		}
	}
}

Generate a return value from a task

Runnable is a separate task that performs work and does not return any values. If a value is returned when the desired task is completed, the Callable interface can be implemented to override the call() method, use the ExecutorService.submit() method to call, and submit() method to return the Future object. The example is as follows:

	class TaskWithResult implements Callable<String> {
		private int id;
		public TaskWithResult(int id){
			this.id = id;
		}
		public String call() {
			return "result of TaskWithResult: " + id;
		}
	}
	public class CallableDemo {
		public static void main(String[] args) {
			ExecutorService exec = Executors.newCachedThreadPool();
			ArrayList<Future<String>> results = new ArrayList<Future<String>> ();
			for(int i = 0; i < 10; i++){
				results.add(exec.submit(new TaskWithResult(i)));
			}
			for(Future<String> fs : results) {
				try{
					System.out.println(fs.get());
				}catch(Exception e){
					e.printStackTrace();
				}finally{
					exec.shutdown();
				}
			}
		}
	}

Solving Shared Resource Competition-Locking Code

When using Lock class to lock, the lock must be released in the final code block to prevent the lock from unlocking when the exception occurs.

Lock lock = new ReentrantLock();  
lock.lock();  
try {   
  // update object state  
}finally {  
  lock.unlock();  //Make sure you release the lock. Typically try-final is used. 
}  

ReentrantLock also supports other locks

1. timing lock
Deadlock can be avoided by using tryLock(long timeout, TimeUnit unit) method: if all locks needed are not acquired within a specified time, all locks acquired are released:
public boolean trySendOnSharedLine(String message, long timeout, TimeUnit unit) throw Exception {
	long nanosToLock =  unit.toNanos(timeout) - estimatedNanosToSend(message);
	if(!lock.tryLock(nanosToLock, NANOSECONDES)) {
		return false;
	}
	try{
		return sendOSharedLine(message);
	}finally{
		lock.unlock();
	}
}
2. Interruptible lock acquisition operation
Interruptible lock acquisition operations can also use locks in cancelable operations. Lock Interruptibly method can acquire locks while maintaining the response to interruptions:
public boolean sendOnsharedLine(String message) throws Exception {
	lock.lockInterruptibly();
	try{
		return cancellableSendOnSharedLine(message);
	}finally{
		lock.unlock();
	}
}
private boolean cancellableSendOnSharedLine(String message) throws Exception {
	...
}
3. Read-write Lock--ReentrantReadWriteLock
Similar to ReentrantLock, ReentrantReadWriteLock can be created with the option of a non-fair lock or a fair lock. In a fair lock, the thread with the longest waiting time will get the lock first. If the lock is held by a read thread and another thread requests a write lock, no other read thread can get the read lock until the write thread is used up and the write lock is released. In an unfair lock, the order in which threads are licensed is uncertain. It is possible for a write thread to be degraded to a read thread, but it is not possible to upgrade from a read thread to a write thread.

Example: Wrapping Map s with Read-Write Locks

public class ReadWriteMap<K, V>{
	private final Map<K, V> map;
	private final ReadWriteLock lock = new ReentrantReadWriteLock();
	private final Lock r = lock.readLock();
	private final Lock w = lock.writeLock();
	public ReadWriteMap(Map<K, V> map) {
		this.map = map;
	}
	public V put(K key, Value value) {
		w.lock();
		try{
			return map.put(key, value);
		}finally{
			w.unclock();
		}
	}
	public V get(Object key) {
		r.lock();
		try{
			return map.get(key);
		}finally{
			r.unlock();
		}
	}
}
4. When traversing containers, you can clone a copy and traverse it to solve thread insecurity.
  • If the container is locked directly, the efficiency will be very low when the container is large.
  • Container implicit traversal occurs: methods such as hashCode and equals perform iteration indirectly, which occurs when the container is another element or key value. Similarly, containsAll/removeAll and retainAll methods, as well as the container as a parameter construction method, will iterate over empty strings.
5. Substitution schemes for collection classes in high concurrency scenarios
  • Replace Map with Concurrent HashMap in the java.util.concurrent package
  • CopyOnWriteArrayList in the java.util.concurrent package replaces the traversal-based List
  • CopyOnWriteArraySet in the java.util.concurrent package replaces traversal-based Set
  • ConcurrentSkipListMap in the java.util.concurrent package instead of SortedMap
  • ConcurrentSkipListSet in the java.util.concurrent package instead of SortedSet

Multiple implementations of BlockingQueue

  • LinkedBlockingQueue and Array BlockingQueue are FIFO queues, which are similar to LinkedList and Array List respectively.
  • Priority Blocking Queue is a priority queue that can process elements in some order rather than FIFO.
  • SynchronousQueue: It's not a real queue, it doesn't maintain the storage space of the elements, it maintains a set of threads, which operate the elements.

ConcurrentHasMap

Concurrent HasMap uses segmented locks for thread access control, with very little performance loss in a single-threaded environment. Concurrent ModificationException is not thrown as a "timely failure" and the size and isEmpty obtained in a concurrent environment may be outdated.

atresia

A latch acts as a door: until the latch reaches the end state, the door is closed and no thread passes through. When the latch reaches the end state, the door opens and allows all threads to pass through. When the lock reaches the end state, it will not change the state. The door will always open.

1) CountDownLatch

A flexible latch implementation that allows one or more threads to wait for a set of events to occur. The latch state includes a counter that is initialized as a positive number to indicate the number of events to wait. The countDown method decreases the counter, indicating that an event has occurred, and the awit method blocks until the counter reaches zero, or the waiting thread interrupts, or the waiting timeout.

public long timeTasks(int nThreads, final Runnable task) throws Exception {
	final CountDownLatch startGate = new CountDownLatch(1);
	final CountDownLatch endGate = new CountDownLatch(nThreads);
	for(int i = 0; i < nThreads; i++) {
		Thread t = new Thread() {
			public void run() {
				try{
					startGate.await(); //startGate blocking
					try{
						task.run();
					}finally{
						endGate.countDown();
					}
				}catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		};
		t.start();
	}
	long start = System.nanoTime();
	startGate.countDown();  //startGate count s down to 0, and the created thread starts executing
	endGate.await();  		//endGate blocks until the thread created has finished running
	long end = System.nanoTime();
	return end - start;
}
2) FutureTask

FutureTask represents an abstract computational result that can be generated. FutureTask implements the computational result by Callable, which is equivalent to a Runnable that can generate the result. It can be in three states: waiting for operation, running and running. When FutureTask enters the completion state, it will stop in that state forever.

  • Future.get's behavior depends on the state of the task. If the task has been completed, get will return the result immediately. Otherwise, get will block until the task enters the state of completion, and then return the result or throw an exception.
class Preloader {
	//Define a future, retrieve product information from the database, and return the result with call
	private final FutureTask<ProductInfo> future = new FutureTask<ProductInfo> (new Callable<ProductInfo>(){
		public ProductInfo call() throws Exception {    //This method can return the obtained result.
			return loadProdectInfo();
		}

	});
	private final Thread thread = new Thread (future);   //Put it into a thread to execute
	public void start() {
		thread.start();
	}
	public ProductInfo get() throws Exception {  //Getting product information will block if it is not loaded
		return future.get();
	}
}
3) Semaphore-semaphore

Counting semaphores are used to control the number of operations accessing a particular resource at the same time, or to perform a specified operation at the same time. They can also implement a resource pool or impose boundaries on containers.

  • acquire(): A licensed thread can only be executed, otherwise it will block until a license is obtained.
  • release(): release permit.
public class SemaphoreTest {
	private final Set<T> set;
	private final Semaphore sem;		
	public SemaphoreTest(int bound) {
		this.set = Collections.synchronizedSet(new HashSet<T>());
		sem = new Semaphore(bound);
	}			
	public boolean add(T o) throws Exception{
		sem.acquire();                 //Get a license. If it's insufficient, block until it's licensed.
		boolean wasAdded = false;
		try {
			wasAdded = set.add(o);
			return wasAdded;
		} finally {
			if(!wasAdded) {
				sem.release();			//Release permit
			}
		}
	}			
	public boolean remvoe(Object o) {
		boolean wasRemove = set.remove(o);
		if(wasRemove){
			sem.release();
		}
		return wasRemove;
	}			
}
4) Fence--Cyclic Barrier

Cyclic Barrier is a counter similar to CountDownLatch. The difference is that the Cyclic Barrier number is the number of threads that call Cyclic Barrier. await () to enter the waiting state. When the number of threads reaches the number specified at the beginning of Cyclic Barrier, all threads that enter the waiting state are awakened and continue.

Cyclic Barrier can also start with a Runnable parameter. The Runnable task is executed before all other threads are awakened after the number of Cyclic Barriers has reached.

public class CyclicBarrierTest {
	public static class ComponentThread implements Runnable {
		CyclicBarrier barrier;// Counter
		int ID; // Component identification
		int[] array; // Data array
		// Construction method
		public ComponentThread(CyclicBarrier barrier, int[] array, int ID) {
			this.barrier = barrier;
			this.ID = ID;
			this.array = array;
		}
		public void run() {
			try {
				array[ID] = new Random().nextInt(100);
				System.out.println("Component " + ID + " generates: " + array[ID]);
				// Wait here for Barrier
				System.out.println("Component " + ID + " sleep");
				barrier.await();
				System.out.println("Component " + ID + " awaked");
				// Compute the current and subsequent values in the data array
				int result = array[ID] + array[ID + 1];
				System.out.println("Component " + ID + " result: " + result);
			} catch (Exception ex) {
			}
		}
	}
	// Testing the use of Cyclic Barrier
	public static void testCyclicBarrier() {
		final int[] array = new int[3];
		CyclicBarrier barrier = new CyclicBarrier(2, new Runnable() {
			// Execute when all threads arrive at Barrier
			public void run() {
				System.out.println("testCyclicBarrier run");
				array[2] = array[0] + array[1];
			}
		});
		// Startup thread
		new Thread(new ComponentThread(barrier, array, 0)).start();
		new Thread(new ComponentThread(barrier, array, 1)).start();
	}
	public static void main(String[] args) {
		CyclicBarrierTest.testCyclicBarrier();
	}
}
```	

7.Future Represents the lifecycle of a task, and provides a way to determine whether it has been completed or cancelled, and
  //Get the result of the task and cancel the task.
  	//Wait for a specified time:

Page renderPageWithAd() throws InterruptedException { long endNanos = System.nanoTime() + TIME_BUDGET; Future<Ad> f = exec.submit(new FetchAdTask()); Page page = renderPageBody(); Ad ad; try{ long timeLeft = endNanos - System.nanoTime(); ad = f.get(timeLeft, NANOSECONDS); }catch(ExecutionException e){ ad = DEFAULT_AD; }catch(TimeoutException e){ ad = DEFAULT_AD; f.cancel(ture); } page.setAd(ad); return page; }

  	Create n tasks, submit them to a thread pool, reserve n Future, and use the time-limited get method to obtain each result serially through Future, using invokeAll method:

Private class QuoteTask implements Callable < TravelQuote > {// implements Callable interface private final TravelCompany company; private final TravelInfo traveInfo; ...... public TravelQuote call() throws Exception {*// rewrite call() method return company.solicitQuote(travelInfo); } } publc List<TravelQuote> getRankedTravelQuotes (TravelInfo travelInfo, Set<TravelCompany> companies, Comparator<TravelQuote> ranking, long time, TimeUtil unit) throws InterruptedException { List<QuoteTask> task = new ArrayList<QuoteTask>(); for(TravelCompany company : companies) { Tasks. add (new QuoteTask (company, travelInfo); // Add tasks to a List } List < Future < Travel Quote > futures = exec. invokeAll (tasks, time, unit); // Call all tasks List<TravelQuote> quotes = new ArrayList<TravelQuote>(tasks.size()); Iterator<QuoteTask> taskIter = tasks.iterator(); for(Future<TravelQuote> f : futures) { QuoteTask task = taskIter.next(); try{ Quotes. add (f. get (); / / query the result of task execution, if not completed or timed out, the thread will block }catch(ExecutionException e){ quotes.add(task.getFailureQuote(e.getCause())); }catch(CancellationException e){ quotes.add(task.getTimeoutQuote(e)); } } Collections.sort(quotes, ranking); reutrn quotes; }


8.lock
	1)Spinlock: In order for a thread to wait, it is possible for a thread to execute a busy loop (spin), which is called spinlock.
	
9.Thread Cancellation
	(1)Cancel by interruption
	```
class PrimeProducer extends Thread {
	private final BlockingQueue<BigInteger> queue;
	PrimeProducer(BolckingQueue<BigInteger> queue) {
		this.queue = queue;
	}
	public void run() {
		try{
			BigInteger p = new BigInteger.ONE;
			while(!Thread.currentThread().isInterrupted()) {
				queue.put(p = p.nextProbablePrime());
			}
		}catch(InterruptedException e) {
			/* Allow threads to exit */
		}
	}
	public void cancel() {
		interrupt();
	}
}
(2) Cancellation through Future
public static void timedRun(Runnable r, long timeout, TimeUnit unit) {
	Future<?> task = taskExec.submit(r);
	try{
		task.get(timeout, unit);
	}catch(TimeoutException e) {
		//The next task was cancelled
	}catch(ExecutionException e) {
		//If an exception is thrown in a task, the exception is re-thrown
		throw launderThrowable(e.getCause);
	}finally{
		//If the task is over, the cancellation operation will not have any impact.
		task.cancel(true);	//If the task is running, it will be interrupted
	}
}
(3) Closing example of log service:
public class LogService {
	private final BlockingQueue<String> queue;
	private final LoggerThread loggerThread;
	private final PrintWriter writer;
	
	private boolean isShutdown;
	private int reservations;
	
	public LogService(Writer writer) {
        this.queue = new LinkedBlockingQueue<String>();
        this.loggerThread = new LoggerThread();
        this.writer = new PrintWriter(writer);
    }
	
	public void start() {
		loggerThread.start();
	}
	
	public void stop() {
		synchronized(this) {
			isShutdown = true;
		}
		loggerThread.interrupt();
	}
	
	public void log(String msg) throws InterruptedException {
		synchronized (this) {
			if(isShutdown) {
				throw new IllegalStateException("...");
			}
			++reservations;
		}
		queue.put(msg);
	}
	
	private class LoggerThread extends Thread {
		public void run() {
			try{
				while(true) {
					try{
						synchronized(LogService.this) {
							if(isShutdown && reservations == 0) {
								break;
							}
						}
						String msg = queue.take();
						synchronized(LogService.this){
							--reservations;
						}
						writer.println(msg);
					}catch(InterruptedException e) {
						//doSomething
					}
				}
			}finally{
				writer.close();
			}
		}
	}
}

10. Set the size of the thread pool To set the thread pool size correctly, the characteristics of computing environment, resource budget and task must be analyzed. How many cpu s, how much memory, whether the task is compute-intensive, IO-intensive or both in the deployed system, whether they need scarce resources like jdbc connection, and if different types of tasks need to be performed, and the behavior differences between them are very large, then we should consider using multiple thread pools, so that each thread pool can be combined according to each one. Adjust from your workload. For computationally intensive tasks: On systems with n CPUs, when the size of the thread pool is n+1, the optimal utilization is usually achieved. For tasks that contain IO operations or other blocking operations: Because threads do not execute at one address, the size of the thread pool should be larger. To set the size of the thread pool correctly, the ratio of waiting time to computing time of tasks must be estimated. This estimate does not need to be very accurate and can be obtained through some analysis or monitoring tools. To achieve the desired utilization, we can calculate it in the following way: nThread = nCPU * u * (1 + w/c), where nThread represents the optimal number of threads, nCpu represents the number of cpus, and u represents the utilization rate of cpu. w denotes waiting time and c denotes computing time (cpu usage time)

11. Extending ThreadPool Executor ThreadPool Executor is extensible and can rewrite beforeExecutor/afterExecute/terminated to extend the behavior of ThreadPool Executor.

public class TimingThreadPool extends ThreadPoolExecutor {
	private final ThreadLocal<Long> startTime = new ThreadLocal<Long>();
	private final Logger log = Logger.getLogger(TimingThreadPool.class);
	private final AtomicLong numTasks = new AtomicLong();
	private final AtomicLong totalTime = new AtomicLong();
	
	public TimingThreadPool() {
        super(1, 1, 0L, TimeUnit.SECONDS, null);
    }
	
	protected void beforeExecute(Thread t, Runnable r) {
        super.beforeExecute(t, r);
        log.info(String.format("Thread %s: start %s", t, r));
        startTime.set(System.nanoTime());
    }

    protected void afterExecute(Runnable r, Throwable t) {
        try {
            long endTime = System.nanoTime();
            long taskTime = endTime - startTime.get();
            numTasks.incrementAndGet();
            totalTime.addAndGet(taskTime);
            log.info(String.format("Thread %s: end %s, time=%dns",
                    t, r, taskTime));
        } finally {
            super.afterExecute(r, t);
        }
    }			
	protected void terminated() {
		try {
			log.info(String.format("Terminated: avg time = %dns", totalTime.get() / numTasks.get()));
		}finally{
			super.terminated();
		}
	}
	
}

12. deadlock Use public calls to avoid deadlocks

  - Deadlock example:
class Taxi {
	private Point location, destination;
	private final Dispatcher dispatcher;
	...
	public synchronized Point getLocation() {
		return location;
	}
	public synchronized void setLocation(Point location) {
		this.location = location;		
		if(location.equals(destionation)) {
			dispatcher.notifyAvailable(this);
		}
	}
}	
class Dispatcher {
	private final Set<Taxi> taxis;
	private final Set<Taxi> avaiableTaxis;
	...
	public synchronized void notifyAvailable(Taxi taxi) {
		availableTaxis.add(taxi);
	}
	public synchronized Image getImage(){
		Image image = new Image();
		for(Taxi t : taxis) {
			image.drawMarket(t.getLocation);
		}
		return image;
	}
}
	Because both setLocation and notify Available are synchronous methods, the thread calling setLocation will first acquire the Taxi lock and then the Dispatcher. Similarly, the thread calling getImage will first acquire the Dispatcher lock and then each Taxi lock. Two threads acquire two locks in different order, which may result in deadlocks. If you call an external method with a lock, you need to be alert to deadlocks.
  • Use public calls to avoid deadlock examples
class Taxi {
	private Point location, destination;
	private final Dispatcher dispatcher;
	...
	public synchronized Point getLocation() {
		return location;
	}
	public void setLocation(Point location) {
		boolean readchDestination;
		synchronized(this) {  //Locking is no longer a method but a block of code
			this.location = location;
			readchDestination = location.equals(destionation);
		}
		if(readchDestination) {
			dispatcher.notifyAvailable(this);
		}
	}
}	
class Dispatcher {
	private final Set<Taxi> taxis;
	private final Set<Taxi> avaiableTaxis;
	...
	public synchronized void notifyAvailable(Taxi taxi) {
		availableTaxis.add(taxi);
	}
	public Image getImage(){
		Set<Taxi> copy;
		synchronized(this) {
			copy = new HashSet<Taxi>(taxis);  //Copy a copy
		}
		Image image = new Image();
		for(Taxi t : copy) {
			image.drawMarket(t.getLocation);
		}
		return image;
	}
}
	If you do not need to hold a lock when calling a method, this call is called an open call. The core idea of this approach is not to call another synchronization block in one synchronization block

13. Optimize lock performance: 1) Lock decomposition technology: Lock this object:

public class ServerStatus {
	public final Set<String> users;
	public final Set<String> queries;
	public synchronized void addUser(String u) {
		users.add(u);
	}
	public synchronized void addQuery(String q) {
		queries.add(q);
	}
	public synchronized void removeUser(String u) {
		users.remove(u);
	}
	public synchronized void removeQuery(String q) {
		queries.remove(q);
	}
}
  Decomposition of locks: Two member variables are used in Server Status: users and queries, which can be locked separately when used.
public class ServerStatus {
	public final Set<String> users;
	public final Set<String> queries;
	public void addUser(String u) {
		synchronized(users){
			users.add(u);
		}
	}
	public void addQuery(String q) {
		synchronized(queries){
			queries.add(q);
		}				
	}
	public void removeUser(String u) {
		synchronized(users){
			users.remove(u);
		}
	}
	public void removeQuery(String q) {
		synchronized(queries){
			queries.remove(q);
		}
	}
}
2) Lock Segmentation Technology
	In some cases, the lock decomposition technique can be further extended to decompose locks on a set of independent objects, which is called lock segmentation. For example, in the implementation of Current HashMap, an array of 16 locks is used. Each lock protects one-sixteenth of all hash buckets, of which the N hash bucket is protected by the 16 locks of N/mod:
public class StripedMap {
	//Synchronization strategy: buckets[n] are protected by locks [n%-N_LOCKS]
	private static final int N_LOCKS = 16;
	private final Node[] buckets;
	private final Object[] locks;
	private static class Node{...}
	public StripedMap(int numBuckets) {
		buckets = new Node[numBuckets];
		locks = new Object[N_LOCKS];
		for(int i = 0; i < N_LOCKS; i++) {
			locks[i] = new Object();
		}
	}
	private final int hash(Object key) {
		return Math.abs(key.hashCode % buckets.length);
	}
	public Object get(Object key) {
		int hash = hash(key);
		synchronized(locks[hash % N_LOCKS]) {  //Lock up
			for(Node m = buckets[hash]; m != null; m = m.next()) {
				if(m.key.equals(key)) {
					return m.value();
				}
			}
		}
		return null;
	}
	public void clear() {
		for(int i = 0; i < buckets.length; i++){
			synchronized(locks[i % N_LOCKS]) {  //Lock up
				buckets[i] = null;
			}
		}
	}
}

13. Base Classes for Implementing Bounded Cache

public abstract class BaseBoundedBuffer<V> {
	private final V[] buf;
	private int tail;
	private int head;
	private int count;
	protect BaseBoundedBuffer(int capacity) {
		this.buf = (V[]) new Object[capacity];
	}
	protected synchronized final void doPut(V v) {
		buf[tail] = v;
		if(++tail == buf.length) {
			tail = 0;
		}
		++count;
	}
	protected synchronized final V doTake() {
		V v = buf[head];
		buf[head] = null;
		if(++head == buf.length) {
			head = 0;
		}
		--count;
		return v;
	}
	public synchronized final boolean isFull() {
		return count == buf.length;
	}
	public synchronized final boolean isEmpty() {
		return count == 0;
	}
}

Blocking cache implementation: When multiple threads compete, it still runs slowly

public class SleepyBoundedBuffer<V> extends BaseBoundedBuffer<V> {
	public SleepyBoundedBuffer(int size) {super(size);}
	public void put(V v) throws InterruptedException {
		while(true) {
			synchronized(this) {
				if(!isFull()){
					doPut(v);
					return;
				}
			}
			Thread.sleep(SLEEP_GRANULARITY);
		}
	}
	public V take() throws InterruptedException {
		while(true) {
			synchronized(this) {
				if(!isEmpty()){
					doTake();
					return;
				}
			}
			Thread.sleep(SLEEP_GRANULARITY);
		}
	}
}

Conditional queue (better than the previous implementation): Blocking cache implementation: When multiple threads compete, it still runs slowly

public class BoundedBuffer<V> extends BaseBoundedBuffer<V> {
	public BoundedBuffer(int size) {super(size);}
	public synchronized void put(V v) throws InterruptedException {
		while(isFull()) {
			wait();
		}
		doput(v);
		notifyAll();
	}
	public synchronized V take() throws InterruptedException {
		while(isEmpty()) {
			wait();
		}
		V v = doTake();
		notifyAll();
		return v;
	}
}

Use conditional notification to achieve blocking queues: notifyAll() in conditional queues can be inefficient, especially when all threads are awakened, if only one thread satisfies the condition, then other threads will wait again, so wake-up-wait requires a lot of thread switching overhead, so conditional queues can be used to control: when the queue is empty or full, it will be notified. He thread.

public class BoundedBuffer<V> extends BaseBoundedBuffer<V> {
	public BoundedBuffer(int size) {super(size);}
	public synchronized void put(V v) throws InterruptedException {
		while(isFull()) {
			wait();
		}
		boolean wasEmpty = isEmpty();  	//The order of these two statements is important.
		doput(v);						//Deadlock may be found by reversing the order
		if(wasEmpty){     //When the thread is empty, it notifies the other threads. When the other threads wake up, the thread that executes the put method executes and the thread that executes the take method waits again.
			notifyAll();
		}		
	}
	public synchronized V take() throws InterruptedException {
		while(isEmpty()) {
			wait();
		}
		boolean wasFull = isFull();   	//The order of these two statements is important.
		V v = doTake();					//Deadlock may be found by reversing the order
		if(wasFull){	  //When the thread is full, the other threads are notified. When the other threads wake up, the thread executing the take method executes and the thread executing the put method waits again.
			notifyAll();			
		}
		return v;
	}
}

Using bounded caching to display conditional variables A Condition is associated with a Lock, just like a queue is associated with a built-in lock. In the Condition object, the corresponding methods to wait, notify and notifyAll are await, signal and signalAll, respectively. However, Condition extends Object, so it also includes awit and notify methods. Be sure to use the correct version.

public class ConditionBoundedBuffer<T> {
	protected final Lock lock = new ReentrantLock();
	private final Condition notFull = lock.newCondition();
	private final Condition notEmpty = lock.newCondition();
	private final T[] items = (T[]) new Object[BUFFER_SIZE];
	private int tail, head, count;
	public void put(T x) throws InterruptedExcetpion {    //Blocking until notFull
		lock.lock();
		try{
			while(count == items.length){
				notFull.await();
			}
			item[tail] = x;
			if(++tail == items.length) {
				tail = 0;
			}
			++count;
			notEmpty.signal();
		}finally{
			lock.unlock();
		}
	}
	public T take() throws InterruptedExcetpion {		//Blocking until notEmpty
		lock.lock();
		try{
			while(count == 0) {
				notEmpty.await();
			}
			T x = items[head];
			items[head] = null;
			if(++ head == items.length) {
				head =0l
			}
			--count;
			notFull.signal();
			return x;
		}finally{
			lock.unlock();
		}
	}
}

14.Synchronizer analysis Use Lock to implement semaphores

public class SemaporeOnLock {
	private final Lock lock = new ReentrantLock();
	private final Condition permitsAvailable = lock.newCondition();
	private int permits;
	SemaporeOnLock(int initialPermits) {
		lock.lock();
		try{
			permits = initialPermits;
		}finally{
			lock.unlock();
		}
	}
	public void acquire() throws InterruptedException {
		lock.lock();
		try{
			while(permits <= 0){
				permitsAvailable.await();
			}
			--permits;
		}finally{
			lock.unlock();
		}
	}
	public void release(){
		lock.lock();
		try{
			++permits;
			permitsAvailable.signal();
		}finally{
			lock.unlock();
		}
	}
}

Topics: Java socket JSP Database