>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设计一个缓存: