一:Runnable和Callable的區別
最本質的區別在於,Runnable沒有返回結果,Callable會有一個返回結果,返回結果是泛型,可以自己定義。舉例子說明:
public class ThreadRunnable {
public static void main(String[] args) throws ExecutionException, InterruptedException {
MyRunnable runnable = new MyRunnable();
new Thread(runnable).start();
MyCallable callable = new MyCallable();
FutureTask task = new FutureTask(callable);
new Thread(task).start();
System.out.println("callable: " + task.get());
}
}
class MyRunnable implements Runnable {
@Override
public void run() {
return;
}
}
class MyCallable implements Callable<String> {
@Override
public String call() throws Exception {
return "hello";
}
}
上述例子中可以看到,callable可以定義一個返回結果,通過FutureTask的get方法可以獲得線程執行後的結果(阻塞等待結果)。
源碼查看:
/**
* Allocates a new {@code Thread} object. This constructor has the same
* effect as {@linkplain #Thread(ThreadGroup,Runnable,String) Thread}
* {@code (null, target, gname)}, where {@code gname} is a newly generated
* name. Automatically generated names are of the form
* {@code "Thread-"+}<i>n</i>, where <i>n</i> is an integer.
*
* @param target
* the object whose {@code run} method is invoked when this thread
* is started. If {@code null}, this classes {@code run} method does
* nothing.
*/
public Thread(Runnable target) {
init(null, target, "Thread-" + nextThreadNum(), 0);
}
可以看到,Thread裏可以放Runnable,但是不可以放callable,繼續查看FutureTask:
public class FutureTask<V> implements RunnableFuture<V> {
/*
* Revision notes: This differs from previous versions of this
* class that relied on AbstractQueuedSynchronizer, mainly to
* avoid surprising users about retaining interrupt status during
* cancellation races. Sync control in the current design relies
* on a "state" field updated via CAS to track completion, along
* with a simple Treiber stack to hold waiting threads.
*
* Style note: As usual, we bypass overhead of using
* AtomicXFieldUpdaters and instead directly use Unsafe intrinsics.
*/
/**
* The run state of this task, initially NEW. The run state
* transitions to a terminal state only in methods set,
* setException, and cancel. During completion, state may take on
* transient values of COMPLETING (while outcome is being set) or
* INTERRUPTING (only while interrupting the runner to satisfy a
* cancel(true)). Transitions from these intermediate to final
* states use cheaper ordered/lazy writes because values are unique
* and cannot be further modified.
*
* Possible state transitions:
* NEW -> COMPLETING -> NORMAL
* NEW -> COMPLETING -> EXCEPTIONAL
* NEW -> CANCELLED
* NEW -> INTERRUPTING -> INTERRUPTED
*/
private volatile int state;
private static final int NEW = 0;
private static final int COMPLETING = 1;
private static final int NORMAL = 2;
private static final int EXCEPTIONAL = 3;
private static final int CANCELLED = 4;
private static final int INTERRUPTING = 5;
private static final int INTERRUPTED = 6;
/** The underlying callable; nulled out after running */
private Callable<V> callable;
/** The result to return or exception to throw from get() */
private Object outcome; // non-volatile, protected by state reads/writes
/** The thread running the callable; CASed during run() */
private volatile Thread runner;
/** Treiber stack of waiting threads */
private volatile WaitNode waiters;
}
/**
* A {@link Future} that is {@link Runnable}. Successful execution of
* the {@code run} method causes completion of the {@code Future}
* and allows access to its results.
* @see FutureTask
* @see Executor
* @since 1.6
* @author Doug Lea
* @param <V> The result type returned by this Future's {@code get} method
*/
public interface RunnableFuture<V> extends Runnable, Future<V> {
/**
* Sets this Future to the result of its computation
* unless it has been cancelled.
*/
void run();
}
上面的代碼看到了:最終FutureTask也是實現了Runnable。
二:自定義一個FutureTask類
FutureTask的get可以阻塞等待結果,那麼在寫之前思考下,需要哪些步驟?
步驟如下:
1:需要去重寫繼承的Runnable接口的run方法;
2:需要一個callable變量,用來執行call()方法;
3:需要一個泛型的變量,用來存放結果;
4:還需要一個隊列,用來存放那些阻塞還未返回結果的線程;
5:需要一個標誌,判斷線程是否執行完,可以返回結果;
6:最後,需要一個獲取結果的方法。
代碼實現如下,有註釋可以方便查看:
public class ThreadFutureTask<T> implements Runnable {
//判斷線程有沒有執行完,可以返回結果
public static volatile boolean isEnding = false;
//callable對象
public Callable<T> callable;
//線程執行後的結果
public volatile T result;
//隊列,用於存放未執行完的線程,即阻塞的線程,放到隊列中
public LinkedBlockingDeque<Thread> waiter = new LinkedBlockingDeque<>();
//構造函數
public ThreadFutureTask(Callable<T> callable) {
this.callable = callable;
}
//實現Runnable的run方法
@Override
public void run() {
try {
//獲取callable執行的結果
result = callable.call();
} catch (Exception e) {
e.printStackTrace();
} finally {
//最後,不管有沒有執行完,都結束獲取結果
isEnding = true;
}
//把隊列中阻塞的線程,喚醒,否則獲取不到結果
while (true) {
Thread thread = waiter.poll();
//要是隊列中沒有線程在掛起,就直接返回
if (thread == null) {
break;
}
//喚醒掛起的線程
LockSupport.unpark(thread);
}
}
//獲取結果的方法
public T get() {
//如果線程執行完了,就獲取結果,否則就掛起
if (!isEnding) {
waiter.offer(Thread.currentThread());
}
//讓線程掛起
while (!isEnding) {
LockSupport.park();
}
//返回結果
return result;
}
}
然後再去上面的runnable和callable區別的測試類裏測試一下:
public static void main(String[] args) throws ExecutionException, InterruptedException {
// MyRunnable runnable = new MyRunnable();
// new Thread(runnable).start();
MyCallable callable = new MyCallable();
ThreadFutureTask task = new ThreadFutureTask(callable);
// FutureTask task = new FutureTask(callable);
new Thread(task).start();
System.out.println("callable: " + task.get());
}
返回結果如下:
可以看到,返回結果和FutureTask一樣,這樣就實現了FutureTask的功能。
三:CountDownLatch
CountDownLatch是一個同步工具類,它是讓一個或者多個線程等待,直到其他線程執行完再執行;CountDownLatch是通過倒計數器來實現的,它的初始值就是線程的數量,每當一個線程執行完畢,計數器就減一,直到減到0,輸出所有線程執行的結果,等待的線程就可以恢復繼續執行。
public static AtomicLong num = new AtomicLong(0);
public static void main(String[] args) throws InterruptedException {
//倒計時計數器,
CountDownLatch latch = new CountDownLatch(10);
for (int i=0; i<10; i++) {
new Thread(() -> {
for (int j=0; j<100000; j++) {
num.getAndIncrement();
}
//計數器減1
latch.countDown();
}).start();
}
//打開開關
latch.await();
System.out.println(num.get());
}
四:Semaphore
Semaphore是一個計數信號量,可以控制訪問資源的線程數量,也就是說,它是一個可以控制併發的共享鎖,也就是限流。
public static void main(String[] args) {
//初始值就是可以通過的數量
Semaphore semaphore = new Semaphore(2);
for (int i=0; i<100; i++) {
new Thread(() -> {
try {
//拿到通道
semaphore.acquire();
System.out.println("開始通過橋樑。。。。");
} catch (InterruptedException e) {
e.printStackTrace();
}
test("222");
//執行完釋放通道
semaphore.release();
}).start();
}
}
public static void test(String name) {
System.out.println(name + "成功通過");
LockSupport.parkNanos(1000 * 1000 * 1000 * 3000);
}
五:CyclicBarrier
CyclicBarrier循環柵欄,可以循環利用的屏障,它的作用就是讓線程都等待完成後纔會再去執行。
比如:去遊樂園做摩天輪,假設只能上齊四個人才允許啓動,那麼再沒有上齊之前,其他人就在等待。
public static void main(String[] args) {
//初始值就是每次可以處理的數量
CyclicBarrier barrier = new CyclicBarrier(2);
for (int i=0; i<100; i++) {
new Thread(() -> {
try {
//等待
barrier.await();
System.out.println("摩天輪啓動了。。。。");
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
}).start();
}
}
好了,到此,本篇文章就結束了。