>JUC ? 就是Java API 中這三個包的簡稱:
>concurrent 包
1.線程池
對於N個任務,可以來一個任務就開闢一個線程。而線程池的思想是,用一個線程池去處理這N個任務。
使用線程池一般步驟:
- 使用Executor類的靜態方法創建ExecutorService對象,該對象就代表一個線程池。
- 使用這個線程池的execute 或submit 方法來提交一個任務,而這個任務就是一個線程。
- 要關閉一個線程池,可以使用ExecutorService對象的shutdown 方法。
public class TestThreadPool {
public static void main(String[] args) {
// 固定大小線程池
Executor threadPool = Executors.newFixedThreadPool(4);
// 拓展線程池 根據線程使用率進行清空
Executor threadPool2 = Executors.newCachedThreadPool();
// 單線程
Executor threadPool3 = Executors.newSingleThreadExecutor();
Random r = new Random();
Runnable target = new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + "第" + i + "個任務");
try {
Thread.sleep(r.nextInt(500));
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
};
for (int i = 0; i < 10; i++) {
// new Thread(target).start();
threadPool2.execute(target);
}
}
}
2.future
前面學習的“Future提前完成任務模式”。其實實現了一種異步計算結果,主程序不受調用程序處理時間長短而控制,提前返回控制權。而這裏JUC提供的Callable和Future就是完成了這一功能。
Callable類似於一個增強的Runnable 接口,與之不同的是Callable提供了一個call() 方法來執行線程代碼,call() 方法可以有返回值,也可以聲明式地拋出異常。
爲什麼要使用Future?多線程的返回值問題
使用流派:
- 繼承Callable接口,實現call() 方法,這個call() 方法不僅是線程的執行體,也可以有返回值。
- 使用FutureTask 對象包裝Callable 對象,因爲FutureTask實現了Runnable 接口,可以作爲Thread 的執行載體。FutureTask封裝了call() 方法的返回值。
- 使用FutureTask 對象作爲Thread 載體創建線程。
- 使用FutureTask 對象的get() 方法獲得線程執行結果。
public class TestCallable {
public static void main(String[] args) throws Exception {
TestCallable tc = new TestCallable();
tc.go();
}
private void go() throws Exception {// 一個小注意點:內部類也是成員,非靜態的情況不能直接在main中new,成員方法中可以
MyThread mt = new MyThread();
FutureTask<Integer> ft = new FutureTask<Integer>(mt);
new Thread(ft).start();
System.out.println("我想幹點別的,等待結果來了之後我拿結果");
// Integer i = ft.get();//直接寫get(),任務結束後取值
Integer j = ft.get(1, TimeUnit.SECONDS);// 拿結果的最長等待時間,如果超時等不到就報錯。
// Exception in thread "main"
// java.util.concurrent.TimeoutException
System.out.println(j);
}
/**
* 增強版線程: 1、可以有返回值 2、可以throws 異常
*/
class MyThread implements Callable<Integer> {
@Override
public Integer call() throws Exception {
Thread.sleep(2000);
return 1;
}
}
}
如何在線程池中使用Callable 和 Future?
線程池中,也可以使用Callable和Future,用法很簡單。只是不必使用Future 的實現類FutureTask ,因爲線程池可以直接提交一個Callable任務,而不像使用Thread 必須有一個Runnable 載體。
使用流派:
- 創建線程池、Callable 對象。
- 使用線程池 submit() 提交Callable 對象。返回Future對象。
- 使用Future對象的 get() 方法,獲得返回值。
public class TestCallableThreadPool {
public static void main(String[] args) {
TestCallableThreadPool t = new TestCallableThreadPool();
t.go();
}
private void go() {
ExecutorService threadPool = Executors.newCachedThreadPool();
Future<String> future = threadPool.submit(new MyThread());
try {
String string = future.get();
System.out.println(string);
} catch (InterruptedException | ExecutionException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
class MyThread implements Callable<String> {
@Override
public String call() throws Exception {
Thread.sleep(2000);
return "hello world";
}
}
}
3.同步容器、併發容器
>3.1
傳統的容器比如線程不安全的ArrayList:
線程安全的Vector(區別就是加了鎖),即使需要線程安全的容器,我們也不使用Vector,使用工具類Collections轉換爲線程安全的類:
典型的代理模式實現,對ArrayList 的方法進行包裝,最終幹活的還是ArrayList:
>3.2迭代時刪除問題
首先,不要在 foreach 循環裏進行元素的 remove/add 操作
public class TestTh {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list = Collections.synchronizedList(list);
for (int i = 0; i < 100; i++) {
list.add(String.valueOf(i));
}
for (String string : list) {
if (string.equals("30")) {
System.out.println(string);
list.remove(string);
}
}
}
}
會報錯:
併發容器CopyOnWriteArrayList,解決遍歷時修改的問題,滿足了讀一致性,
注意與同步容器(解決線程安全問題)區分:
public class Test {
public static void main(String[] args) {
Test t = new Test();
t.go();
}
private void go() {
V v = new V();
new Thread(new Runnable() {
@Override
public void run() {
v.show();
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
v.modify();
}
}).start();
}
class V {
// 兩個線程同時操作一個線程(一個線程監測,一個修改就報錯),也有問題
// private List<Integer> list = new ArrayList<>();
// 併發容器,與前面的同步容器進行區分
private List<Integer> list = new CopyOnWriteArrayList<>();
public V() {
for (int i = 0; i < 100; i++) {
list.add(i);
}
// list=Collections.synchronizedList(list);
}
public void show() {
for (int i : list) {
System.out.println(i);
try {
Thread.sleep(20);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public void modify() {
for (int i = 200; i < 300; i++) {
list.add(i);
}
try {
Thread.sleep(20);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
>3.3使用併發容器ConcurrentHashMap設計一個緩存: