概述
Introduction
并行计算是一种计算的形式, 一些计算被并行的执行而不是串行。 Java 语言通过线程的使用被设计为支持并发编程。对象和资源能够被多线程访问, 每个线程能够在程序中潜在的访问任何对象, 并且程序设计者在多线程中必须确保读和写的对象访问被正确的同步。
Examples
Callable and Future
虽然Runnable
提供了一种包裹要被在一个不同的线程中执行的代码的方式。它有一个缺陷, 不能从执行中返回结果。仅有的一种从一个Runnable
的执行中返回值的方式是把结果赋值给一个在Runnable
外部作用域中可访问的变量。
Callable
在java 5中被引入, 作为Runnable
的一个对等(peer)。 Callable
除了有一个Call
方法而不是Run
方法外(和Runnable
)本质相同。 Call
方法有其它的能力去返回一个结果并且允许抛出检查时异常。
Callable
任务提交所产生的结果可用后能够被Future
获取
Future
可以被看作一个整理存储Callable
计算结果的容器。callable的计算能持续在另一个线程中, 并且任何获取一个Future
结果的尝试都将被阻塞,并且一旦(结果)变得可用,便会返回结果。
Callable 接口
public interface Callable<V> {
V call() throws Exception;
}
Future
interface Future<V> {
V get();
V get(long timeout, TimeUnit unit);
boolean cancel(boolean mayInterruptIfRunning);
boolean isCancelled();
boolean isDone();
}
使用Callable
和 Future
示例
public static void main(String[] args) throws Exception {
ExecutorService es = Executors.newSingleThreadExecutor();
System.out.println("Time At Task Submission : " + new Date());
Future<String> result = es.submit(new ComplexCalculator());
// the call to Future.get() blocks until the result is available.So we are in for about a 10 sec wait now
//对Future.get()的调用将被阻塞,直到结果变得可用, 所以我们将要有10秒的等待
System.out.println("Result of Complex Calculation is : " + result.get());
System.out.println("Time At the Point of Printing the Result : " + new Date());
}
我们的Callable
做了一个漫长的计算
public class ComplexCalculator implements Callable<String> {
@Override
public String call() throws Exception {
// just sleep for 10 secs to simulate a lengthy computation
Thread.sleep(10000);
System.out.println("Result after a lengthy 10sec calculation");
return "Complex Result"; // the result
}
}
输出
Time At Task Submission : Thu Aug 04 15:05:15 EDT 2016
Result after a lengthy 10sec calculation
Result of Complex Calculation is : Complex Result
Time At the Point of Printing the Result : Thu Aug 04 15:05:25 EDT 2016
其它在Future
上被允许的操作
当然get()
是一个从Future 提供者中抽取实际结果的方法。
get(long timeout, TimeUnit unit)
定义了等待在当前线程中等待结果的最大时间- 调用
cancel(mayInterruptIfRunning)
取消任务, 标记mayInterrupt
表示如果任务已经开始并且此刻正在运行,那么应该被中断。 - 通过调用
isDone();
检查一个任务是否已经被完成 通过调用
isCancelled();
检查任务是否被取消基本的多线程
如果你有很多任务要去执行, 并且所有的任务并不依赖前一个的执行结果, 如果你的计算机允许, 你可以使用多线程使用多个处理器同时处理所有的工作。这能够使你的程序执行的更快,如果你有一些大的非互相依赖的任务。
class CountAndPrint implements Runnable {
private final String name;
CountAndPrint(String name) {
this.name = name;
}
/** This is what a CountAndPrint will do */
@Override
public void run() {
for (int i = 0; i < 10000; i++) {
System.out.println(this.name + ": " + i);
}
}
public static void main(String[] args) {
// Launching 3 parallel threads
for (int i = 1; i <= 3; i++) {
// `start` method will call the `run` method
// of CountAndPrint in another thread
new Thread(new CountAndPrint("Instance " + i)).start();
}
// Doing some others tasks in the main Thread
for (int i = 0; i < 10000; i++) {
System.out.println("main: " + i);
}
}
}
各个CountAndPrint
实例中的run方法中的代码将会以一种不可预期的次序执行。一个样例执行片段可能看起来如下:
Instance 4: 1
Instance 2: 1
Instance 4: 2
Instance 1: 1
Instance 1: 2
Main: 1
Instance 4: 3
Main: 2
Instance 3: 1
Instance 4: 4
...
CountDownLatch
一个同步助手 允许一个或多个线程等待直到一组操作在其它的线程中被执行完。
1. 一个`CountDownLatch ` 以一个给定的计数 进行初始化
2. `await` 方法阻塞直到由于`countDown()` 方法的调用,当前计数器到达0, 之后所有的线程被放行并且后续的`await` 调用也会很快被返回。
3. 这是只会出现一次的现象——这个计数器count不能够被重置。如果你需要一个能够重置计数器count的版本, 考虑使用`CyclicBarrier` 。
关键方法:
public void await() throws InterruptedException
造成当前线程等待直到latch减为0, 除非线程被中断
public void countDown()
latch的减少, 释放所有的等待线程如果计数到达0
示例:
import java.util.concurrent.*;
class DoSomethingInAThread implements Runnable {
CountDownLatch latch;
public DoSomethingInAThread(CountDownLatch latch) {
this.latch = latch;
}
public void run() {
try {
System.out.println("Do some thing");
latch.countDown();
} catch(Exception err) {
err.printStackTrace();
}
}
}
public class CountDownLatchDemo {
public static void main(String[] args) {
try {
int numberOfThreads = 5;
if (args.length < 1) {
System.out.println("Usage: java CountDownLatchDemo numberOfThreads");
return;
}
try {
numberOfThreads = Integer.parseInt(args[0]);
} catch(NumberFormatException ne) {
}
CountDownLatch latch = new CountDownLatch(numberOfThreads);
for (int n = 0; n < numberOfThreads; n++) {
Thread t = new Thread(new DoSomethingInAThread(latch));
t.start();
}
latch.await();
System.out.println("In Main thread after completion of " + numberOfThreads + " threads");
} catch(Exception err) {
err.printStackTrace();
}
}
}
输出:
java CountDownLatchDemo 5
Do some thing
Do some thing
Do some thing
Do some thing
Do some thing
In Main thread after completion of 5 threads
解释:
CountDownLatch
以一个为5的计数器在Main线程中初始化- Main 线程通过调用
await()
方法处于等待状态 - 五个
DoSomethingInAThread
实例被创建, 每一个实例通过countDown()
方法减少计数器counter。 一旦计数器counter 变成0, Main线程将会重新开始执行
Locks as Synchronisation aids
Java 5 之前的cocurrent 包中引入的线程非常低级。之后的包中引入提供了一些更加高级的并发编程工具/设计
锁是和同步块以及关键词本质上相同有相同目的的线程同步机制。
锁的本质
int count = 0; // shared among multiple threads
public void doSomething() {
synchronized(this) {
++count; // a non-atomic operation
}
}
使用锁来同步
int count = 0; // shared among multiple threads
Lock lockObj = new Lock();
public void doSomething() {
try {
lockObj.lock()
++count; // a non-atomic operation
} finally {
lockObj.unlock(); // sure to release the lock without fail
}
}
有一些锁变量的变体, 更详细参考api文档here
Runnable 对象
Runnable
接口定义了单个方法, run()
, 包含了在线程中被执行的代码。
Runnable
对象被传递到Thread
构造器里, 然后线程的start()
方法被调用
示例
public class HelloRunnable implements Runnable {
@Override
public void run() {
System.out.println("Hello from a thread");
}
public static void main(String[] args) {
new Thread(new HelloRunnable()).start();
}
}
在java 8 示例
public static void main(String[] args) {
Runnable r = () -> System.out.println("Hello world");
new Thread(r).start();
}
Runnable vs Thread subclass
Runnable
对象的使用更为常见, 因为Runable
对象能够成为一个类的子类而不是Thread
的。
Thread
子类在一个简单的应用中更容易使用, 但是受限于你的任务类必须是Thread
类的子类的事实。
一个Runnable
对象适合更高级的线程管理APIs。
最后
以上就是眼睛大魔镜为你收集整理的译 -- Java 并发编程(多线程)一 | Callable and Future | CountDownLatch | Runable的全部内容,希望文章能够帮你解决译 -- Java 并发编程(多线程)一 | Callable and Future | CountDownLatch | Runable所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复