Comprehensive Guide to Java Multithreading and Concurrency Utilities
This article provides an in‑depth overview of Java multithreading, covering thread creation, lifecycle methods, synchronization mechanisms, volatile semantics, thread‑local storage, high‑level concurrency utilities such as ReentrantLock, CountDownLatch, CyclicBarrier, Semaphore, Executors, Callable/Future, and atomic classes, with practical code examples and usage tips.
1. What Is a Thread?
A thread is an independent sub‑task that runs within a process.
2. Ways to Create a Thread
Method 1: Extend java.lang.Thread and override run().
Method 2: Implement java.lang.Runnable and provide a run() method. The second approach is recommended because it offers greater flexibility and reduces coupling.
3. Getting the Current Thread
Thread.currentThread()4. Thread Types
Daemon threads and user threads. By default, a new thread is a user thread. Use setDaemon(true) to mark a thread as daemon.
5. Parent‑Child Relationship
A thread started from another thread becomes its child; the original thread is the parent. The main thread is the parent of any thread started from main().
6. Thread API Overview
start()– moves the thread to the runnable state and eventually invokes run(). stop() – deprecated and unsafe; use interrupt() instead. suspend() / resume() – deprecated; avoid using them. yield() – hints that the current thread is willing to give up the CPU. join() – waits for the target thread to finish.
7. synchronized Keyword
Provides atomicity, memory visibility, and re‑entrancy. If an exception occurs inside a synchronized block, the lock is released automatically.
8. volatile Keyword
Ensures visibility of variable updates across threads but does not guarantee atomicity.
9. Thread Communication
Two main mechanisms: shared memory and explicit synchronization.
Besides synchronized, you can use Object.wait(), notify(), and notifyAll() inside a synchronized block to coordinate threads.
synchronized (lock) {
try {
lock.wait(); // wait for a signal, releases lock
} catch (InterruptedException e) {
e.printStackTrace();
}
// after being notified
}
synchronized (lock) {
lock.notify(); // wake up one waiting thread
// other operations
}10. ThreadLocal and InheritableThreadLocal
ThreadLocal gives each thread its own isolated variable; InheritableThreadLocal allows child threads to inherit the parent’s value.
11. ReentrantLock Usage
Provides explicit lock control with optional fairness, timed try‑lock, and condition variables.
private java.util.concurrent.locks.Lock lock = new ReentrantLock();
public void method() {
try {
lock.lock(); // acquire lock
// critical section
} finally {
lock.unlock(); // release lock
}
}Key features: fair vs. non‑fair lock, tryLock() with timeout, ability to query lock state, and support for multiple Condition objects.
12. ReentrantReadWriteLock
Separates read (shared) and write (exclusive) locks to improve read‑heavy performance.
private final ReadWriteLock lock = new ReentrantReadWriteLock();
public void readMethod() {
lock.readLock().lock();
try { /* read */ } finally { lock.readLock().unlock(); }
}
public void writeMethod() {
lock.writeLock().lock();
try { /* write */ } finally { lock.writeLock().unlock(); }
}13. Synchronous vs. Concurrent Containers
Synchronous containers (e.g., Vector, Hashtable, Collections.synchronizedMap) lock the whole collection, which can become a bottleneck.
Concurrent containers (e.g., ConcurrentHashMap, CopyOnWriteArrayList, ConcurrentSkipListMap) use finer‑grained locking or lock‑free algorithms to achieve higher throughput.
14. CountDownLatch
A latch that allows one or more threads to wait until a set of operations complete.
public long timeTasks(int nThreads, final Runnable task) throws InterruptedException {
final CountDownLatch startSignal = new CountDownLatch(1);
final CountDownLatch doneSignal = new CountDownLatch(nThreads);
for (int i = 0; i < nThreads; i++) {
new Thread(() -> {
try {
startSignal.await();
task.run();
} catch (InterruptedException ignored) {}
finally { doneSignal.countDown(); }
}).start();
}
long start = System.currentTimeMillis();
startSignal.countDown();
doneSignal.await();
return System.currentTimeMillis() - start;
}15. CyclicBarrier
Blocks a set of threads until all have reached a common barrier point, then optionally runs a barrier action.
int THREAD_NUM = 5;
CyclicBarrier barrier = new CyclicBarrier(THREAD_NUM, () -> System.out.println("---Barrier Action---"));
for (int i = 0; i < THREAD_NUM; i++) {
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + " pre-working");
barrier.await();
System.out.println(Thread.currentThread().getName() + " working");
}).start();
}16. Semaphore
A counting semaphore controls access to a limited number of permits.
Semaphore semaphore = new Semaphore(3); // only 3 permits
semaphore.acquire(); // block if no permit
// critical section
semaphore.release(); // return permit17. Executors Framework (Thread Pools)
Provides ready‑made thread‑pool implementations such as newSingleThreadExecutor, newFixedThreadPool, newCachedThreadPool, and scheduled executors.
ExecutorService service = Executors.newFixedThreadPool(100);
service.submit(() -> System.out.println("task"));
service.shutdown();18. ThreadPoolExecutor Details
Constructor parameters: corePoolSize, maximumPoolSize, keepAliveTime, TimeUnit, workQueue, threadFactory, RejectedExecutionHandler. The diagram in the original article illustrates the state transitions.
19. Callable, Future, and FutureTask
Callable<V>can return a result or throw a checked exception. Future<V> represents the pending result of an asynchronous computation.
FutureTask<Integer> future = new FutureTask<>(() -> new Random().nextInt(100));
new Thread(future).start();
Integer result = future.get();20. Atomic Classes
Atomic variables (e.g., AtomicInteger, AtomicLong, AtomicReference) provide lock‑free thread‑safe operations. There are also array‑based and updater‑based variants, each with specific usage constraints (non‑static, non‑final, volatile, visibility).
21. Summary
Thread safety means that concurrent execution yields the same result as sequential execution. Synchronization (via synchronized or explicit locks) and proper communication (wait/notify, Condition, pipelines) are essential to avoid data races and ensure correct program behavior.
For more Java concurrency tips, follow the "Java团长" WeChat channel.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Java Captain
Focused on Java technologies: SSM, the Spring ecosystem, microservices, MySQL, MyCat, clustering, distributed systems, middleware, Linux, networking, multithreading; occasionally covers DevOps tools like Jenkins, Nexus, Docker, ELK; shares practical tech insights and is dedicated to full‑stack Java development.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
