多线程
参考资料: 马士兵多线程讲解
synchronized
-
锁定的是什么?
对象的锁只有一个,线程1拿到,其他线程就无法操作这个对象。进入阻塞状态,直到synchronized(){}执行结束 -
.synchronized(this)、synchronized(.class)、synchronized(任意对象)区别
synchronized (this)代码块是锁定当前对象,所得是用一个对象
synchronized(任意自定义对象)进行同步操作,对象监视器必须是同一个对象。否则将进行异步操作
synchronized(.class)是对该类的所有实例化对象的所有属性和方法加锁,强制同步。当线程1访问实例化的同步代码,除了线程1,其他线程进入阻塞状态 -
类锁()与对象锁
实现方式
类锁的实现方式:
被static和synchronized修饰的方法
synchronized(*.class)
对象锁的实现方式:
被synchronized修饰的方法
synchronized(this)范围
类锁的范围是整个实体类
对象锁的范围是当前对象 -
同步方法和异步方法是否可以同时调用?
可以,同步方法需哟拿到对象的锁,异步方法不需要拿到对象的锁,互不影响 -
一个同步方法可以调用另一个同步方法么?
同一个对象锁可以,不同对象的锁就不行 -
子类的同步方法是否可以调用父类的同步方法?
可以,synchronized是可重入锁 -
synchronized保证了原子性和可见性,因此,效率低
-
被synchronized锁定的代码块如果在执行的过程中出现异常,没有使用try-catch捕获会立即中程序,释放对象锁
-
不要以字符常量作为锁定对象,否则会出现死锁阻塞
-
使用synchronized的代码块越少越好,执行效率越高
-
锁的信息记录在堆内存的对象中
-
synchronized是重量级锁、互斥锁、可重入锁、不公平锁
wait,notify与notifyAll
- wait使当前线程阻塞,前提是线程必须获得锁,所以一般配合synchronized使用。
- 线程执行wait的时候,会释放当前的锁,然后让出cpu,进入等待状态。
- wait方法需要被try catch包含。
- notify,唤醒一个阻塞的线程,由jvm调度
- notify无法指定某个阻塞的线程
- notify,其唤醒的线程,拿到对象锁才可以执行
- notifyAll,唤醒所有阻塞的线程
- wait、notify与notifyAll都是object的方法
- wait, notify, notifyAll一定要在共享对象同步方法或同步代码块中执行,否则会在运行时抛出IllegalMonitorStateException的异常。因为wait, notify, notifyAll包含了对共享对象锁的操作,所以之前一定要先synchronized获取对象锁。
- 唤醒线程执行完共享对象的notify或notifyAll方法后,仍然要执行完synchronized修饰的同步代码块中后面的代码才能释放对象锁,因此,通常notify后面尽量减少执行代码,让对象锁尽快释放。
- 永远在循环(loop)里调用 wait 和 notify, notifyAll,不是在 If 语句,避免死锁情况发生。
volatile
- 保证了可见性,没有保证原子性
- volatile会检测主内存的值发生改变,会同时通知使用值的线程重新从栈内存获取值
- volatile 并不能保证多个线程共同修改running变量时所带来的不一致问题
AtomXXX类
- 此方法可以替代操作数据,++,–操作
- 此方法是原子性的,但不能保证多个方法连续调用是原子性的
- 代码示例:
class T {
AtomicInteger count=new AtomicInteger(0);
/*synchronized*/ void m(){
for (int i = 0; i < 10000000; i++) {
/*if (count.get()<1000000)*/
count.incrementAndGet(); //替代count++的
}
}
public static void main(String[] args) {
T t=new T();
List<Thread> threads=new ArrayList<>();
for (int i = 0; i < 10; i++) {
threads.add(new Thread(t::m,"thread-"+i));
}
threads.forEach((o)->o.start());
threads.forEach((o)->{
try {
o.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
});
System.out.println(t.count);
}
}
运行结果:
100000000
Process finished with exit code 0
Countdownlatch
- CountDownLatch是一个同步工具类,它允许一个或多个线程一直等待,直到其他线程执行完后再执行。
- CountDownLatch是在java1.5被引入的,跟它一起被引入的并发工具类还有CyclicBarrier、Semaphore、ConcurrentHashMap和BlockingQueue,它们都存在于java.util.concurrent包下
- CountDownLatch是通过一个计数器来实现的,计数器的初始化值为线程的数量。每当一个线程完成了自己的任务后,计数器的值就相应得减1。当计数器到达0时,表示所有的线程都已完成任务,然后在闭锁上等待的线程就可以恢复执行任务。
- await() 此方法将会使当前线程在锁存器倒计数至零之前一直等待,除非线程被中断。
- countDown() 此方法将递减锁存器的计数,如果计数到达零,则释放所有等待的线程
- 使用示例:
public class MyContainer5 {
//添加volatile,使t2能够得到通知
volatile List lists=new ArrayList();
public void add(Object o){
lists.add(o);
}
public int size(){
return lists.size();
}
public static void main(String[] args) {
MyContainer5 c=new MyContainer5();
CountDownLatch latch=new CountDownLatch(1);
new Thread(()->{
System.out.println("t2启动");
if (c.size()!=5){
try {
latch.await();
//也可以指定等待时间
//latch.await(5000,TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("t2结束");
},"t2").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{
System.out.println("t1启动");
for (int i = 0; i < 10; i++) {
c.add(new Object());
System.out.println("add "+i);
if (c.size()==5){
//打开门闩,让t2得以执行
latch.countDown();
}
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("t1结束");
},"t1").start();
}
}
运行结果
reentrantlock
- lock的子类,如果遇到异常,需要使用try-catch-finally语句捕获异常
- finally中需要手动释放锁unlock()
- 可重入锁
- 特殊的方法
trylock(),尝试获取锁且可以指定等待时间,获取不到锁可以选择停止
Iockinterruptibly()和interrupt可以打断线程的长时间等待
例子:
线程1线程永远等待,线程2一直等待锁的释放,发现等不到所的释放,t2使用interrupt呼叫别等了,线程1的lockInterruptibly做出响应,就抛出异常停止
代码:
public static void main(String[] args) {
Lock lock=new ReentrantLock();
Thread t1=new Thread(()->{
lock.lock();
try {
System.out.println("t1 start");
TimeUnit.SECONDS.sleep(Integer.MAX_VALUE);
System.out.println("t1 end");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
});
t1.start();
Thread t2=new Thread(()->{
try {
lock.lockInterruptibly(); //可以对interrupt()做出响应
System.out.println("t2 start");
TimeUnit.SECONDS.sleep(5);
System.out.println("t2 end");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
});
t2.start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
t2.interrupt();//打断线程2的等待
}
结果:
t1 start
java.lang.InterruptedException
at java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireInterruptibly(AbstractQueuedSynchronizer.java:898)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireInterruptibly(AbstractQueuedSynchronizer.java:1222)
at java.util.concurrent.locks.ReentrantLock.lockInterruptibly(ReentrantLock.java:335)
at yxxy.c_020.ReentrantLock4.lambda$main$10(ReentrantLock4.java:33)
at yxxy.c_020.ReentrantLock4$$Lambda$2/258952499.run(Unknown Source)
at java.lang.Thread.run(Thread.java:745)
Exception in thread "Thread-1" java.lang.IllegalMonitorStateException
at java.util.concurrent.locks.ReentrantLock$Sync.tryRelease(ReentrantLock.java:151)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.release(AbstractQueuedSynchronizer.java:1261)
at java.util.concurrent.locks.ReentrantLock.unlock(ReentrantLock.java:457)
at yxxy.c_020.ReentrantLock4.lambda$main$10(ReentrantLock4.java:40)
at yxxy.c_020.ReentrantLock4$$Lambda$2/258952499.run(Unknown Source)
at java.lang.Thread.run(Thread.java:745)
- 可以指定公平锁在创建线程的时候指定
ReentrantLock lock=new ReentrantLock(true);//参数为true表示为公平锁
高并发容器
容器
-
ConcurrentHashMap
1.5加入,效率最高且是同步的 -
ConcurrentSkipListMap
1.6加入,高并发且排序 -
CopyOnWriteArrayList
1.5加入,写时复制容器 写时效率低,读时效率高 适合写少读多的环境
事件监听器 -
使集合同步的一个方法,使用Collection.SynchronizedXXX,1.2加入
代码示例:
public class T01_ConcurrentMap {
public static void main(String[] args) {
Map<String,String> map= new ConcurrentHashMap<>();
// Map<String,String> map= new ConcurrentSkipListMap<>(); //高并发并且排序
// Map<String,String> map= new Hashtable<>();
// Map<String,String> map= new HashMap<>();
//TreeMap
Random r=new Random();
Thread[] ths=new Thread[100];
CountDownLatch latch=new CountDownLatch(ths.length);
long start=System.currentTimeMillis();
for (int i = 0; i < ths.length; i++) {
ths[i] = new Thread(()->{
for (int j = 0; j < 10000; j++) {
map.put("a"+r.nextInt(100000),"a"+r.nextInt(100000));
latch.countDown();
}
});
}
Arrays.asList(ths).forEach(t->t.start());
try {
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
long end=System.currentTimeMillis();
System.out.println(end-start);
}
}
运行结果:
分别测试:
ConcurrentHashMap
48
ConcurrentSkipListMap
314
HashTable 同步
58
HashMap 不同步
86
高并发队列
- LinkedBlockingQueue
1.5加入,此队列是无界队列,且是阻塞队列,一旦容器空了,就会让消费者线程等待,唤醒生产者线程 - ArrayBlockingQueue
1.5加入,容器数量满了,继续添加
add会报Queue full 异常
offer不会报异常,此方法的返回值是boolean,告诉你你的添加是否成功true成功,false失败,后面加入参数表示等待一秒后添加
put满了就会等待,线程阻塞 - DelayQueue
延时队列,1.5加入
适合使用实时执行任务的场景
使用需要注意:
重写以下两个方法:
CompareTo(Delayed o):Delayed接口继承了Comparable接口,因此有了这个方法。
getDelay(TimeUnit unit):这个方法返回到激活日期的剩余时间,时间单位由单位参数指定。 - LinkedTransferQueue
1.7加入,特殊方法:
transfer
用于消费者多线程先启动,生产者启动,先去查看有没有消费者线程,有的话,直接传给消费 者线程,同时此生产者线程阻塞,知道消费者使用take从队列读取消息
take
从队列中获取消息
TransferQueue用于更高的并发情况,可用于实时消息处理
容量是有数量的 - SynchronousQueue
1.5加入,一种特殊的TransferQueue容器,阻塞队列
容量为0
代码示例:
public class T09_SynchronusQueue { //容量为0
public static void main(String[] args) throws InterruptedException {
BlockingQueue<String> strs=new SynchronousQueue<>();
new Thread(()->{
try {
System.out.println(strs.take());
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
strs.put("aaa"); //阻塞等待消费者消费
//strs.add("aaa");
System.out.println(strs.size());
}
}
使用add添加数据
Exception in thread "main" java.lang.IllegalStateException: Queue full
at java.util.AbstractQueue.add(AbstractQueue.java:98)
at yxxy.c_025.T09_SynchronusQueue.main(T09_SynchronusQueue.java:20)
使用putt添加数据
0
aaa
Process finished with exit code 0
put的实现调用了transfer,设计的初衷是为了做实时任务,生产者的使用之前,必须看到消费者线程唤醒或出现
线程池
线程池的最顶级接口是
Executor
只有一个方法void execute(Runnable command)
执行线程,没有返回值
ExecutorService
继承了Executor接口,里面定义了线程池的一些常用方法
**isShutdown(),submit(),isTerimated()**等等
callable
类似于Runnable,是一个任务队列,队列开始,执行call(),只不过call()方法有返回值,也可以抛出异常
Runnable的run()方法是重写的,没有返回值,不能抛出异常
Executors
为execuotr,executorservice,scheduleExecutorService,ThreadFactory,Callable类提供方法实现的一个工厂类
ThreadPool
线程池:一个池子,用来存放线程,提高了线程的重复使用率,提高了响应速度。不用频繁打开线程,过度消耗cpu资源
FixedThreadPool
创建固定数量的线程池
若等待的线程超过线程池的数量,则固定数量的线程池会将等待的线程放到此放到任务队列中,等线程池执行完成,就将等待的任务队列放入线程池中
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
源码,是将值传给ThreadPoolExecutot,将个数的值传给ThreadPoolExecutor构造方法实例化
代码示例:
public class T05_ThreadPool {
public static void main(String[] args) throws InterruptedException {
ExecutorService service= Executors.newFixedThreadPool(5); //execute submit
for (int i = 0; i < 6; i++) {
service.execute(()->{
try {
TimeUnit.MILLISECONDS.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName());
});
}
System.out.println(service);
service.shutdown();
System.out.println(service.isTerminated());
System.out.println(service.isShutdown());
System.out.println(service);
TimeUnit.SECONDS.sleep(5);
System.out.println(service.isTerminated());
System.out.println(service.isShutdown());
System.out.println(service);
}
}
方法解读:
Shutdown()
正常关闭,所有线程执行完,才会关闭
shutdownnow()
立即关闭线程池,不等待线程池执行线程完成
isTerminated()
线程池的所有线程是否执行完
isShutdown()
是否关闭
运行结果:
java.util.concurrent.ThreadPoolExecutor@85ede7b[Running, pool size = 5, active threads = 5, queued tasks = 1, completed tasks = 0]
false
true
java.util.concurrent.ThreadPoolExecutor@85ede7b[Shutting down, pool size = 5, active threads = 5, queued tasks = 1, completed tasks = 0]
pool-1-thread-4
pool-1-thread-1
pool-1-thread-2
pool-1-thread-3
pool-1-thread-5
pool-1-thread-4
true
true
java.util.concurrent.ThreadPoolExecutor@85ede7b[Terminated, pool size = 0, active threads = 0, queued tasks = 0, completed tasks = 6]
CacheThreadPool
缓存线程池
当一个线程需要过来请求时,缓存线程池开启一个新的线程,开始执行线程,若此时又来一个线程请求,缓存线程池会查看池中是否有可用线程,有的话,就使用开启过的线程,没有的话,在开启一个线程执行新请求的线程
空闲时间超过60s,会将线程示例自动销毁,减少内存消耗
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
源码,设置数量为0-int类型的最大值,ThreadPoolExecutor构造方法实例化
将此参数传给但是创建数量与设备有关
代码示例:
public class T08_CachedPool {
public static void main(String[] args) throws InterruptedException {
ExecutorService service= Executors.newCachedThreadPool();
System.out.println(service);
for (int i = 0; i < 2; i++) {
service.execute(()->{
try {
TimeUnit.MILLISECONDS.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName());
});
}
System.out.println(service);
TimeUnit.SECONDS.sleep(80);
System.out.println(service);
}
}
运行结果:
java.util.concurrent.ThreadPoolExecutor@677327b6[Running, pool size = 0, active threads = 0, queued tasks = 0, completed tasks = 0]
java.util.concurrent.ThreadPoolExecutor@677327b6[Running, pool size = 2, active threads = 2, queued tasks = 0, completed tasks = 0]
pool-1-thread-2
pool-1-thread-1
java.util.concurrent.ThreadPoolExecutor@677327b6[Running, pool size = 2, active threads = 0, queued tasks = 0, completed tasks = 2]
Process finished with exit code 1
SIngleThreadPool
单个线程池
只有1个数量的线程池
当被线程占用时,其他的任务需要进入队列等待
只有一个线程 保证线程按顺序执行
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
源码,线程池数量设置为1,ThreadPoolExecutor构造方法实例化
代码示例:
public class T09_SingleThreadPool {
public static void main(String[] args) {
ExecutorService service= Executors.newSingleThreadExecutor();
for (int i = 0; i < 5; i++) {
final int j=i;
service.execute(()->{
System.out.println(j+" "+Thread.currentThread().getName());
});
}
}
}
运行结果:
0 pool-1-thread-1
1 pool-1-thread-1
2 pool-1-thread-1
3 pool-1-thread-1
4 pool-1-thread-1
Process finished with exit code 1
SchedulePool
定时器线程池/延时线程池
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue());
}
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
最终,也是调用ThreadPoolExecutor
newScheduledThreadPool(int corePoolSize)
corepoolsize为定时器线程池/延时线程池的数量
代码示例:
public class T10_SchedulePool {
public static void main(String[] args) {
ScheduledExecutorService service= Executors.newScheduledThreadPool(4);
service.scheduleAtFixedRate(()->{
try {
TimeUnit.MILLISECONDS.sleep(new Random().nextInt(1000));
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName());
},0,500,TimeUnit.MILLISECONDS);
}
}
运行结果:
pool-1-thread-1
pool-1-thread-1
pool-1-thread-2
pool-1-thread-1
pool-1-thread-1
pool-1-thread-1
pool-1-thread-1
pool-1-thread-1
pool-1-thread-1
pool-1-thread-4
pool-1-thread-4
pool-1-thread-4
pool-1-thread-4
Process finished with exit code 1
WorkStealingThreadPool
工作窃取
主动找活干,自己的线程池的线程执行完成,会去找其他线程池里未完成的线程,执行该线程
会看cpu几核,会启动多少默认的线程
精灵线程daemon线程,mian线程不阻塞的话,看不到输出 jvm不停止,这个线程就一直运行
public static ExecutorService newWorkStealingPool() {
return new ForkJoinPool
(Runtime.getRuntime().availableProcessors(),
ForkJoinPool.defaultForkJoinWorkerThreadFactory,
null, true);
}
代码示例:
public class T11_WorkStealingPool {
public static void main(String[] args) throws IOException {
ExecutorService service= Executors.newWorkStealingPool();
System.out.println(Runtime.getRuntime().availableProcessors());
service.execute(new R(1000));
service.execute(new R(2000));
service.execute(new R(2000));
service.execute(new R(2000));
service.execute(new R(2000));
//由于产生的精灵线程(守护线程、后台线程),主线程不阻塞的话,看不到输出
System.in.read();
}
static class R implements Runnable{
int time;
R(int t){
this.time=t;
}
@Override
public void run() {
try {
TimeUnit.MILLISECONDS.sleep(time);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(time+" "+Thread.currentThread().getName());
}
}
}
运行结果:
4
1000 ForkJoinPool-1-worker-1
2000 ForkJoinPool-1-worker-3
2000 ForkJoinPool-1-worker-2
2000 ForkJoinPool-1-worker-0
2000 ForkJoinPool-1-worker-1
Process finished with exit code 1
可以看出,前四行中第一个在1秒钟线程完成,其他三个cpu都是2秒,此时cpu1处于空闲,去找到空闲的线程执行,所以最后一行还是cpu1
ForkJoinThreadPool
分叉合并
源码不是通过ThreadPoolExecutor类实现的,是一个独立的类,是通过AbstractExecutorService来实现的
如果有一个特别大的任务,回切分成小的子任务,可以指定子任务的大小,分完之后,将结果加到一起 ,大数据处理使用
使用需要继承RecursiveAction和RecursiveTask 区别
RecursiveAction没有返回值,需要手动打印
RecursiveTask 有返回值
public ForkJoinPool() {
this(Math.min(MAX_CAP, Runtime.getRuntime().availableProcessors()),
defaultForkJoinWorkerThreadFactory, null, false);
}
代码示例:
public class T12_ForkJoinPool {
static int[] nums = new int[1000000];
static final int MAX_NUM = 50000;
static Random r = new Random();
static {
for (int i = 0; i < nums.length; i++) {
nums[i] = r.nextInt(100);
}
System.out.println(Arrays.stream(nums).sum());
}
/*static class AddTask extends RecursiveAction{
int start,end;
AddTask(int s,int e){
this.start=s;
this.end=e;
}
@Override
protected void compute() {
if (end-start<=MAX_NUM){
long sum=0L;
for (int i = start; i < end; i++) {
sum+=nums[i];
}
System.out.println("from:"+start+" to:"+end+" = "+sum);
}else{
int middle=start+(end-start)/2;
AddTask subTask1=new AddTask(start,middle);
AddTask subTask2=new AddTask(middle,end);
subTask1.fork();
subTask2.fork();
}
}
}*/
static class AddTask extends RecursiveTask<Long> {
int start, end;
AddTask(int s, int e) {
this.start = s;
this.end = e;
}
@Override
protected Long compute() {
if (end - start <= MAX_NUM) {
long sum = 0L;
for (int i = start; i < end; i++) {
sum += nums[i];
}
return sum;
}
int middle = start + (end - start) / 2;
AddTask subTask1 = new AddTask(start, middle);
AddTask subTask2 = new AddTask(middle, end);
subTask1.fork();
subTask2.fork();
return subTask1.join() + subTask2.join();
}
}
public static void main(String[] args) throws IOException {
ForkJoinPool fjp = new ForkJoinPool();
AddTask task = new AddTask(0, nums.length);
fjp.execute(task);
long result = task.join();
System.out.println(result);
//System.in.read();
}
}
两种运行结果:
RecursiveAction
49472075
from:468750 to:500000 = 1552625
from:968750 to:1000000 = 1551344
from:437500 to:468750 = 1535352
from:937500 to:968750 = 1542129
from:406250 to:437500 = 1544944
from:906250 to:937500 = 1553500
from:375000 to:406250 = 1540861
from:875000 to:906250 = 1547955
from:343750 to:375000 = 1541029
from:843750 to:875000 = 1539541
from:312500 to:343750 = 1540001
from:281250 to:312500 = 1547936
from:250000 to:281250 = 1544375
from:812500 to:843750 = 1548957
from:218750 to:250000 = 1547361
from:187500 to:218750 = 1549289
from:781250 to:812500 = 1554749
from:156250 to:187500 = 1544155
from:750000 to:781250 = 1556202
from:125000 to:156250 = 1540425
from:718750 to:750000 = 1543888
from:93750 to:125000 = 1545415
from:62500 to:93750 = 1553452
from:687500 to:718750 = 1540641
from:656250 to:687500 = 1553069
from:31250 to:62500 = 1549308
from:0 to:31250 = 1551091
from:593750 to:625000 = 1548623
from:562500 to:593750 = 1549207
from:531250 to:562500 = 1546033
from:500000 to:531250 = 1529665
from:625000 to:656250 = 1538953
RecursiveTask
49516750
49516750
Process finished with exit code 0
ThreadPoolExecutor
此类除了ForkPoolExecutor,以上的其他线程池都是由ThreadPoolExecutor实现的
parallelStream
多线程,必然讲到效率的问题
1.8的parallelStream对于数据的处理能力很高,多线程编写的
比较效率时间
代码示例:
public class T14_ParallelStreamApi {
public static void main(String[] args) {
List<Integer> nums=new ArrayList<>();
Random r=new Random();
for (int i = 0; i < 10000; i++) {
nums.add(1000000+r.nextInt(1000000));
}
//System.out.println(nums);
long start=System.currentTimeMillis();
nums.forEach(v->isPrime(v));
long end=System.currentTimeMillis();
System.out.println(end-start);
//使用parallel stream api
start=System.currentTimeMillis();
nums.parallelStream().forEach(T14_ParallelStreamApi::isPrime);
end=System.currentTimeMillis();
System.out.println(end-start);
}
private static boolean isPrime(Integer num) {
for (int i = 2; i < num/2; i++) {
if (num % i == 0){
return false;
}
}
return true;
}
}
运行结果:
2762
1095
Process finished with exit code 0
可以看出ParalleStream的效率比平常方式高出一半
本文章将持续更新,欢迎互相学习