Fork me on GitHub
Mao's Blog

  • 首页

  • 归档

  • 分类

  • 标签

  • Java编程思想

  • Java并发编程实战

  • 搜索

Java并发编程实战———Java内存模型

发表于 2019-01-25 | 分类于 Java | 本文字数: 659 | 阅读时长 ≈ 0:01

Java内存模型是通过各种操作来定义的,包括对变量的读/写操作,监视器的加锁和释放操作,以及线程的启动和合并操作。JMM为程序中所有的操作定义了一个偏序关系,称之为Happens-Before。要想保证执行操作B的线程看到操作A的结果,那么在A和B之间必须满足Happens-Before关系。如果两个操作之间缺乏Happens-Before关系,那么JVM可以对它们任意地重排序。

阅读全文 »

Java并发编程实战———AbstractQueuedSynchronizer

发表于 2019-01-22 | 分类于 Java | 本文字数: 1.4k | 阅读时长 ≈ 0:02

在基于AQS构建的同步器类中,最基本的操作包括各种形式的获取操作和释放操作。获取操作是一种依赖状态的操作,并且通常会阻塞。当使用锁或信号量时,“获取”操作的含义即获取的是锁或者许可,并且调用者可能会一直等待直到同步器类处于可被获取的状态。在使用CountDownLatch时,“获取”操作意味着“等待并直到闭锁到达结束状态”,而在使用FutureTask时,则意味着“等待并直到任务已经完成”。“释放”并不是一个可阻塞的操作,当执行“释放”操作时,所有在请求时被阻塞的线程都会开始执行。

阅读全文 »

Java并发编程实战———显式锁

发表于 2019-01-16 | 分类于 Java | 本文字数: 1.3k | 阅读时长 ≈ 0:02

Lock与ReentrantLock

Lock提供了一种无条件的、可轮询的、定时的以及可中断的锁获取操作,所有加锁和解锁的方法都是显示的。

1
2
3
4
5
6
7
8
9
//Lock接口
public interface Lock {
void lock();
void lockInterruptibly() throws InterruptedException;
boolean tryLock();
boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException;
void unlock();
Condition newCondition();
}

ReentrantLock实现了Lock接口,并提供了与synchronized相同的互斥性和内存可见性。

1
2
3
4
5
6
7
8
9
10
//使用ReentrantLock来保护对象状态
Lock lock = new ReentrantLock();
...
lock.lock();
try {
//更新对象状态
//捕获异常
} finally {
lock.unlock();
}
阅读全文 »

Java并发编程实战———线程池的使用

发表于 2019-01-01 | 分类于 Java | 本文字数: 6.1k | 阅读时长 ≈ 0:10

在任务与执行策略之间的隐形耦合

在一些任务中,需要拥有或排除某种特定的执行策略。如果某些任务依赖于其他的任务,那么会要求线程池足够大,从而确保它们依赖任务不会被放入等待队列中或被拒绝,而采用线程封闭机制的任务需要串行执行。

线程饥饿死锁

在线程池中,如果任务依赖于其他任务,那么可能产生死锁。在单线程的Executor中,如果一个任务将另一个任务提交到同一个Executor,并且等待这个被提交任务的结果,那么通常会引发死锁。只要线程池中的任务需要无限期地等待一些必须由池中其他任务才能提供的资源或条件,就会发生线程饥饿死锁。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//在单线程Executor中任务发生死锁
public class ThreadDeadlock {
ExecutorService exec = Executors.newSingleThreadExecutor();

public class RenderPageTask implements Callable<String> {
public String call() throws Exception {
Future<String> header = exec.submit(new LoadFileTask("header.html"));
Future<String> footer = exec.submit(new LoadFileTask("footer.html"));
String page = renderBody();
//将发生死锁--由于任务在等待子任务的结果
return header.get() + page + footer.get();
}
}
}

每当提交了一个有依赖性的Executor任务时,要清楚地知道可能会出现线程饥饿死锁,因此需要在代码或配置Executor的配置文件中记录线程池的大小限制或配置限制。

阅读全文 »

Java并发编程实战———取消与关闭

发表于 2018-12-19 | 分类于 Java | 本文字数: 5.6k | 阅读时长 ≈ 0:09

要使任务和线程能安全、快速、可靠地停止下来,并不是一件容易的事。Java没有提供任何机制来安全地终止线程。但它提供了中断(Interruption),这是一种协作机制,能够使一个线程终止另一个线程的当前工作。

任务取消

如果外部代码能在某个操作正常完成之前将其置入“完成”状态,那么这个操作就称为可取消的。在Java中没有一种安全的抢占式方法来停止线程,因此也就没有安全的抢占方式来停止任务。只有一些协作式的机制,使请求取消的任务和代码都遵循一种协商好的协议。

其中一种协作机制能设置某个“已请求取消”标志,而任务将定期地查看该标志。如果设置了这个标记,那么任务将提前结束。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
//使用volatile类型的域来保存取消状态
public class PrimeGenerator implements Runnable {
private final List<BigInteger> primes = new ArrayList<BigInteger>();
private volatile boolean cancelled;

public void run() {
BigInteger p = BigInteger.ONE;
while (!cancelled) {
p = p.nextProbablePrime();
synchronized (this) {
primes.add(p);
}
}
}

public void cancel () {
cancelled = true;
}

public synchronized List<BigInteger> get() {
return new ArrayList<BigInteger>(primes);
}
}
//一个仅允许一秒钟的素数生成器
List<BigInteger> aSecondOfPrimes() throws InterruptionException {
PrimeGenrator generator = new PrimeGenerator();
new Thread(generator).start();
try {
SECONDS.sleep(1);
} finally {
generator.cancel();
}
return generator.get();
}
阅读全文 »
12…14
云逸云飞

云逸云飞

A Java Programmer

70 日志
8 分类
23 标签
RSS
GitHub E-Mail
© 2018 – 2019 云逸云飞