上一篇文章请参考猫头鹰的深夜翻译:核心JAVA并发(一)java
发布一个对象是指该对象的引用对当前的域以外也可见(好比,从getter方法中获取一个引用)。要确保一个对象被安全的发布(即在初始化完成以后发布),可能须要使用同步。能够经过如下方法实现安全的发布:面试
class StaticInitializer { // Publishing an immutable object without additional initialization public static final Year year = Year.of(2017); public static final Set<String> keywords; // Using static initializer to construct a complex object static { // Creating mutable set Set<String> keywordsSet = new HashSet<>(); // Initializing state keywordsSet.add("java"); keywordsSet.add("concurrency"); // Making set unmodifiable keywords = Collections.unmodifiableSet(keywordsSet); } }
class Volatile { private volatile String state; void setState(String state) { this.state = state; } String getState() { return state; } }
class Atomics { private final AtomicInteger state = new AtomicInteger(); void initializeState(int state) { this.state.compareAndSet(0, state); } int getState() { return state.get(); } }
class Final { private final String state; Final(String state) { this.state = state; } String getState() { return state; } }
确保
this
引用不会再初始化过程当中泄漏
class ThisEscapes { private final String name; ThisEscapes(String name) { Cache.putIntoCache(this); this.name = name; } String getName() { return name; } } class Cache { private static final Map<String, ThisEscapes> CACHE = new ConcurrentHashMap<>(); static void putIntoCache(ThisEscapes thisEscapes) { // 'this' reference escaped before the object is fully constructed. CACHE.putIfAbsent(thisEscapes.getName(), thisEscapes); } }
class Synchronization { private String state; synchronized String getState() { if (state == null) state = "Initial"; return state; } }
不变对象的一个很是棒的属性时,他们是现成安全的,全部无需在其上进行同步。是一个对象成为不变对象的要求为:segmentfault
final
类型this
引用在初始化期间不会泄露不变对象的例子:安全
// Marked as final - subclassing is forbidden public final class Artist { // Immutable object, field is final private final String name; // Collection of immutable objects, field is final private final List<Track> tracks; public Artist(String name, List<Track> tracks) { this.name = name; // Defensive copy List<Track> copy = new ArrayList<>(tracks); // Making mutable collection unmodifiable this.tracks = Collections.unmodifiableList(copy); // 'this' is not passed to anywhere during construction } // Getters, equals, hashCode, toString } // Marked as final - subclassing is forbidden public final class Track { // Immutable object, field is final private final String title; public Track(String title) { this.title = title; } // Getters, equals, hashCode, toString }
java.lang.Thread
类用来表示一个应用或是一个JVM现场。其代码一般在某个进程类的上下文中执行。(使用Thread#currentThread
来获取当前线程自己)微信
线程的状态和相应的描述:并发
NEW: 还未启动
RUNNABLE: 启动并运行
BLOCKED: 在控制器上等待 - 该线程正视图获取锁并进入关键区域
WAITING: 等待另外一个线程执行特殊操做(notify/notifyAll
,LockSupport#unpark
)
TIMED_WAITING: 和WAITING相似,可是有超时设置
TERMINATED: 中止
Thread的方法和相应的描述:异步
start: 启动一个Thread
实例而且执行run()
方法
join: 阻塞直到线程完成
interrupt: 中断线程。若是该线程在响应终端的方法中阻塞着,则会在另外一个线程中抛出InterruptedException
,不然将会被设置为中断状态。
stop,suspend,resume,destroy: 这些方法都已经失效了。
InterruptedException
InterruptedException
,应该使用Thread.currentThread().interrupt()
将中断标识回复为true,而后在该层抛出异常。将中断标识设为true很重要,它使得异常在能够在更高的层次上进行处。Threads能够设置UncaughtExceptionHandler
,它会在程序忽然中断的时候收到通知。函数
Thread thread = new Thread(runnable); thread.setUncaughtExceptionHandler((failedThread, exception) -> { logger.error("Caught unexpected exception in thread '{}'.", failedThread.getName(), exception); }); thread.start();
当多个线程在等待彼此释放持有的资源,从而造成了资源占有和等待的循环时,就产生了死锁。可能产生死锁的例子:this
class Account { private long amount; void plus(long amount) { this.amount += amount; } void minus(long amount) { if (this.amount < amount) throw new IllegalArgumentException(); else this.amount -= amount; } static void transferWithDeadlock(long amount, Account first, Account second){ synchronized (first) { synchronized (second) { first.minus(amount); second.plus(amount); } } } }
死锁可能会这样产生:spa
避免死锁的方法有:
class Account { private long id; private long amount; // Some methods are omitted static void transferWithLockOrdering(long amount, Account first, Account second){ boolean lockOnFirstAccountFirst = first.id < second.id; Account firstLock = lockOnFirstAccountFirst ? first : second; Account secondLock = lockOnFirstAccountFirst ? second : first; synchronized (firstLock) { synchronized (secondLock) { first.minus(amount); second.plus(amount); } } } }
class Account { private long amount; // Some methods are omitted static void transferWithTimeout( long amount, Account first, Account second, int retries, long timeoutMillis ) throws InterruptedException { for (int attempt = 0; attempt < retries; attempt++) { if (first.lock.tryLock(timeoutMillis, TimeUnit.MILLISECONDS)) { try { if (second.lock.tryLock(timeoutMillis, TimeUnit.MILLISECONDS)) { try { first.minus(amount); second.plus(amount); } finally { second.lock.unlock(); } } } finally { first.lock.unlock(); } } } } }
当全部的线程都在协商对资源的访问,或是预防死锁,从而致使没有一个线程真正在运行时,会产生活锁。当一个线程长时间占据一个锁致使别的线程没法进展时,会产生线程饥饿现象。
线程池的核心接口是ExecutorService
,java.util.concurrent
还提供了一个静态工厂Executors
,它包含建立具备最多见配置的线程池的工厂方法。
工厂方法以下:
newSingleThreadExecutor: 返回一个只有一个线程的ExecutorService
newFixedThreadPool: 返回一个具备固定数目线程的ExecutorService
newCachedThreadPool: 返回一个可变大小的线程池ExecutorService
newSingleThreadScheduledExecutor: 返回只有一个线程的ScheduledExecutorService
newScheduledThreadPool: 返回包含一组线程的ScheduledExecutorService
newWorkStealingPool: 返回一个带有并行级别的ExecutorService
当调整线程池大小时,最好基于机器运行该应用时分配的逻辑内核数。能够经过调用
Runtime.getRuntime().availableProcessors()
来得到该值。
线程池的实现类
任务经过ExecutorService#submit
,ExecutorService#invokeAll
或ExecutorService#invokeAny
提交,它们对不一样类型的任务有多种重载。
任务的功能性接口:
Runnable: 表示一个没有返回值的任务
Callable: 表示一个包含返回值的计算。它还声明能够抛出原始异常,因此不须要对检查异常进行包装
Future
是对全部的异步计算的抽象。它表示这些计算的结果,在某些时候可用。大多数的ExecutorService
方法都是用Future
做为返回值。它包含检查当前future的状态以及阻塞当前读取操做直至结果能够被读取等方法。
ExecutorService executorService = Executors.newSingleThreadExecutor(); Future<String> future = executorService.submit(() -> "result"); try { String result = future.get(1L, TimeUnit.SECONDS); System.out.println("Result is '" + result + "'."); } catch (InterruptedException e) { Thread.currentThread().interrupt(); throw new RuntimeException(e); } catch (ExecutionException e) { throw new RuntimeException(e.getCause()); } catch (TimeoutException e) { throw new RuntimeException(e); } assert future.isDone();
Lockjava.util.concurrent.locks
包中有一个标准的Lock
接口,ReentrantLock
实现复制了synchronized
关键字的功能,同时提供了一些额外的功能,好比获取当前锁状态的信息,非阻塞的tryBlock()
方法,以及可中断的锁。下面是使用具体的ReentrantLock
实例的例子:
class Counter { private final Lock lock = new ReentrantLock(); private int value; int increment() { lock.lock(); try { return ++value; } finally { lock.unlock(); } } }
ReadWriteLockjava.util.concurrent.locks
包还包含了ReadWriteLock
接口(以及ReentrantReadWriteLock
实现),它被定义为一组读写锁,支持多个同步读者和单一写者。
class Statistic { private final ReadWriteLock lock = new ReentrantReadWriteLock(); private int value; void increment() { lock.writeLock().lock(); try { value++; } finally { lock.writeLock().unlock(); } } int current() { lock.readLock().lock(); try { return value; } finally { lock.readLock().unlock(); } } }
CountDownLatchCountDownLatch
经过一个数值初始化。线程会调用await()
方法阻塞本身,等待计数值为0后再继续运行。其它的线程(或是同一个线程)调用countDown()
来减小计数。一旦计数为0后,该倒计时器便不能够重复使用。用来在达到某个条件后,启动一组未知数量的线程
CompletableFutureCompletableFuture
是异步计算的一个抽象。不一样于Future
,只能经过阻塞获取结果,该类鼓励注册回调函数来建立一组任务,从而在获得返回值或是出现异常时执行该任务。
想要了解更多开发技术,面试教程以及互联网公司内推,欢迎关注个人微信公众号!将会不按期的发放福利哦~