JUC
第一节:什么是JUC ?
JUC就是java.util.concurrent工具包的简称。这是一个处理线程的工具包,在JDK1.5开始出现的。
第二节:进程和线程
2.1 简介
- 进程:是并发执行的程序在执行过程中分配和管理资源的基本单位,是一个动态概念,竞争计算机系统资源的基本单位。
- 线程:是进程的一个执行单元,是进程内科调度实体。比进程更小的独立运行的基本单位。线程也被称为轻量级进程。
- 一个程序至少一个进程,一个进程至少一个线程。
2.2 进程与线程间的区别
- 地址空间:同一进程的线程共享本进程的地址空间,而进程之间则是独立的地址空间。
- 资源拥有:同一进程内的线程共享本进程的资源如内存、I/O、cpu等。但是进程之间的资源是独立的。
- 执行过程:每个独立的进程都有一个程序运行的入口,顺序执行序列和程序入口。但是线程不能独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。
- 线程是处理调度器的基本单位,但是进程不是。
- 两者均可并发执行。
- 一个进程崩溃后,在保护模式下不会对其他进程产生影响,但是一个线程崩溃后整个进程都死掉了。所以多进程要比多线程强壮。
- 进程切换时,消耗的资源大,效率高。所以涉及到频繁的切换时,使用线程要好于进程。通过如果要求同时进行并且又要共享某些变量的并发操作,只能用线程不能用进程。
2.3 进程和线程的优缺点
- 线程执行开销小,但是不利于资源的管理和保护。线程适合在SMP机器(双CPU系统)上运行。
- 进程执行开销大,但是能够很好的进行资源管理和保护。进程可以跨机器前移。
2.4 何时使用多进程,何时使用多进程
对资源的管理和保护要求高,不限制开销和效率时,使用多进程。要求效率高,频繁切换时,资源的保护管理要求不是很高时,使用多线程。
拓展:Java真的可以开启线程吗?
public synchronized void start() {
/**
* This method is not invoked for the main method thread or "system"
* group threads created/set up by the VM. Any new functionality added
* to this method in the future may have to also be added to the VM.
*
* A zero status value corresponds to state "NEW".
*/
if (threadStatus != 0)
throw new IllegalThreadStateException();
/* Notify the group that this thread is about to be started
* so that it can be added to the group's list of threads
* and the group's unstarted count can be decremented. */
group.add(this);
boolean started = false;
try {
start0();
started = true;
} finally {
try {
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
/* do nothing. If start0 threw a Throwable then
it will be passed up the call stack */
}
}
}
//不能开启,底层的C++,JAVA无法直接操作硬件
private native void start0();
第三节:并发编程中的并发与并行
-
并发(concurrency)(多线程操作同一个资源)
- CPU 一核,模拟出来多条线程,实际通过快速交换CPU的使用权限来实现的。
-
并行(parallellism)(过个人一起行走)
- CPU多核,多个线程可以同时指向;线程池。
public static void main(String[] args) {
//获取当前系统的CPU核数(CPU密集型,IO密集型)
System.out.println(Runtime.getRuntime().availableProcessors());
}
并发编程的本质:充分利用系统资源
第四节:线程的回顾
4.1 线程的状态
public enum State {
/**
* Thread state for a thread which has not yet started.
* 新生
*/
NEW,
/**
* Thread state for a runnable thread. A thread in the runnable
* state is executing in the Java virtual machine but it may
* be waiting for other resources from the operating system
* such as processor.
* 运行
*/
RUNNABLE,
/**
* Thread state for a thread blocked waiting for a monitor lock.
* A thread in the blocked state is waiting for a monitor lock
* to enter a synchronized block/method or
* reenter a synchronized block/method after calling
* {@link Object#wait() Object.wait}.
* 阻塞
*/
BLOCKED,
/**
* Thread state for a waiting thread.
* A thread is in the waiting state due to calling one of the
* following methods:
* <ul>
* <li>{@link Object#wait() Object.wait} with no timeout</li>
* <li>{@link #join() Thread.join} with no timeout</li>
* <li>{@link LockSupport#park() LockSupport.park}</li>
* </ul>
*
* <p>A thread in the waiting state is waiting for another thread to
* perform a particular action.
*
* For example, a thread that has called <tt>Object.wait()</tt>
* on an object is waiting for another thread to call
* <tt>Object.notify()</tt> or <tt>Object.notifyAll()</tt> on
* that object. A thread that has called <tt>Thread.join()</tt>
* is waiting for a specified thread to terminate.
* 等待,死死的等
*/
WAITING,
/**
* Thread state for a waiting thread with a specified waiting time.
* A thread is in the timed waiting state due to calling one of
* the following methods with a specified positive waiting time:
* <ul>
* <li>{@link #sleep Thread.sleep}</li>
* <li>{@link Object#wait(long) Object.wait} with timeout</li>
* <li>{@link #join(long) Thread.join} with timeout</li>
* <li>{@link LockSupport#parkNanos LockSupport.parkNanos}</li>
* <li>{@link LockSupport#parkUntil LockSupport.parkUntil}</li>
* </ul>
* 超时等待
*/
TIMED_WAITING,
/**
* Thread state for a terminated thread.
* The thread has completed execution.
* 终止
*/
TERMINATED;
}
4.2 wait和sleep的区别
- 来自不同的类:wait ==> Object sleep == > thread
- 关于锁的释放:wait会释放锁,sleep睡觉了会抱着锁睡,不释放锁。
- 使用范围不同:wait必须在同步代码块中使用,sleep可以在任何地方使用。
- 是否需要异常捕获:都需要
4.3 Synchronized 关键字
package com.seapp.lock;
import sun.security.mscapi.CPublicKey;
import java.util.concurrent.TimeUnit;
/**
* @author seapp
* @date 2021/6/10 17:10
* 真正的多线程开发,降低耦合性,线程就是一个单独的资源类,没有任何的附属的操作
*/
public class SaleTicketDemo {
public static void main(String[] args) {
//并发,多线程操作同一资源类
Ticket ticket = new Ticket();
//学生A买票
new Thread(()->{
for (int i = 1; i <= 40 ; i++) {
ticket.sale();
}
},"A").start();
//学生B买票
new Thread(()->{
for (int i = 1; i <= 40 ; i++) {
ticket.sale();
}
},"B").start();
//学生A买票
new Thread(()->{
for (int i = 1; i <= 40 ; i++) {
ticket.sale();
}
},"B").start();
}
}
/**
* 资源类
*/
class Ticket{
//初始化变量
private int num = 30;
//卖票
public synchronized void sale(){
if(num >0){
System.out.println(Thread.currentThread().getName() + "买到了" + num-- + "张票,剩余" + num);
}
}
}
4.4 lock
package com.seapp.lock;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* @author seapp
* @date 2021/6/10 17:39
* <p>
* lock锁的三部曲:
* 1、new ReentrantLock();
* 2. lock.lock();
* 3. finally ==> lock.unlock();
*/
public class LockDemo {
public static void main(String[] args) {
//并发,多线程操作同一资源类
Ticket02 ticket = new Ticket02();
//学生A买票
new Thread(() -> {
for (int i = 1; i <= 50; i++) {
ticket.sale();
}
}, "A").start();
//学生B买票
new Thread(() -> {
for (int i = 1; i <= 50; i++) {
ticket.sale();
}
}, "B").start();
//学生A买票
new Thread(() -> {
for (int i = 1; i <= 50; i++) {
ticket.sale();
}
}, "B").start();
}
}
/**
* 资源类
*/
class Ticket02 {
//初始化变量
private int num = 30;
Lock lock = new ReentrantLock();
//卖票
public synchronized void sale() {
lock.lock();
try {
if (num > 0) {
System.out.println(Thread.currentThread().getName() + "买到了" + num-- + "张票,剩余" + num);
}
}finally {
lock.unlock();
}
}
}
4.5 Synchronized和lock的区别
- Synchronized内置的Java关键字,Lock是一个Java类
- Synchronized无法判断获取锁的状态,Lock可以判断是否获取到了锁。
- Synchronized 会自动释放锁,lock必须要手动释放锁!如果不释放锁,死锁。
- Synchronized 线程1(获取锁,阻塞)、线程2(等待,傻傻的等);Lock锁就不一定会等待下去。
- Synchronizecd可重入锁,不可以中断的,非公平。Lock,可重入锁,可以判断锁,非公平(可以自己设定)
- Synchronized 适合锁少量的代码同步问题,Lock适合锁大量的同步代码!
第七节:Callable
- 可以有返回值
- 可以抛出异常值
- 方法不同,run()/call()
package com.seapp.callable;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
/**
* @author seapp
* @date 2021/6/12 15:22
*/
public class CallableDemo {
public static void main(String[] args) throws ExecutionException, InterruptedException {
// new Thread(new Runnable()).start();
// new Thread(new FutureTask<V>()).start();
// new Thread(new FutureTask<V>( Callable)).start();
//如何启动callable呢?
MyThread myThread = new MyThread();
FutureTask<Integer> futureTask = new FutureTask<>(myThread);
new Thread(futureTask,"A").start();
new Thread(futureTask,"B").start();//两个线程调用该callable,但只执行了一次。被缓存了。
//Callable返回结果的获取
Integer integer = futureTask.get();//get方法可能是异步,会产生阻塞,需放在最后处理。
System.out.println("integer = " + integer);
}
}
class MyThread implements Callable<Integer> {
@Override
public Integer call() throws Exception {
System.out.println("call方法被调用了");
return 3;
}
}
第八节:常用的辅助类
8.1 CountDownLatch
package com.seapp.sample;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
/**
* @author seapp
* @date 2021/6/15 8:40
*/
public class CountDownLatchDemo {
public static void main(String[] args) throws InterruptedException {
//设置countDownLatch初始化计数
CountDownLatch countDownLatch = new CountDownLatch(6);
for (int i = 0; i < 6; i++) {
new Thread(()->{
System.out.println(" go out");
//在异步线程中,每一个线程执行完进行减1操作。
countDownLatch.countDown();
}).start();
}
//等待,直到countDownLatch计数为0时,才执行下属方法。
countDownLatch.await();
System.out.println("执行完成了...");
}
}
8.2 CyclicBarrier
package com.seapp.sample;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
/**
* @author seapp
* @date 2021/6/15 9:15
* 与CountDownLatch作用相同,当达到某种界定值时,执行对应的
*/
public class CyclicBarrierDemo {
public static void main(String[] args) {
CyclicBarrier cyclicBarrier = new CyclicBarrier(7, () -> {
System.out.println("集齐七龙族,神龙再现");
});
for (int i = 0; i < 7; i++) {
final int I = i;
new Thread(()->{
try {
System.out.println("集齐了第" + I + "颗龙珠");
cyclicBarrier.await();//等待,直到达到cyclicBarrier设定的界定值。
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}).start();
}
}
}
8.3 Semaphore
package com.seapp.sample;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
/**
* @author seapp
* @date 2021/6/15 10:12
*/
public class SemaphoreDemo {
//以抢车位的案例阐述:
public static void main(String[] args) {
//semaphore信号量
Semaphore semaphore = new Semaphore(2);
for (int i = 0; i < 6; i++) {
new Thread(()->{
try {
semaphore.acquire();//得到许可证
System.out.println(Thread.currentThread().getName() + "抢到了车位");
TimeUnit.SECONDS.sleep(2);
System.out.println(Thread.currentThread().getName() + "离开了车位");
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
semaphore.release();//释放许可证
}
},String.valueOf(i)).start();
}
}
}
- semaphore.acquire():获得,假设已经满了,等待。等待被释放为止。
- semaphore.release():释放,会将当前的信号释放量+1。然后唤醒等待的线程。
作用:多个共享资源互斥的使用。并发限流,控制最大的线程数。
第九节:读写锁
9.1 ReadWriteLock
package com.seapp.sample;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* @author seapp
* @date 2021/6/15 14:24
*/
public class ReadWriteLockDemo {
public static void main(String[] args) {
//通过myCache类进行缓存
// MyCache myCache = new MyCache();
MyCacheLock myCache = new MyCacheLock();
//写入数据
for (int i = 0; i < 5; i++) {
final int temp = i;
new Thread(()->{
myCache.put(temp+"",temp + "");
},String.valueOf(temp)).start();
}
for (int i = 0; i < 5; i++) {
final int temp = i;
new Thread(()->{
myCache.get(temp+ "");
},String.valueOf(temp)).start();
}
}
}
class MyCacheLock{
private volatile Map<String,Object> map = new HashMap<>();
//读写锁,更加细类度的控制
private ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
private Lock lock = new ReentrantLock();
/**
* 写入数据
* @param key
* @param object
*/
public void put(String key,Object object){
//写锁
readWriteLock.writeLock().lock();
try {
System.out.println(Thread.currentThread().getName() + "开始写入数据: " + key);
map.put(key,object);
System.out.println(Thread.currentThread().getName() + "写入完成了");
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
readWriteLock.writeLock().unlock();
}
}
public void get(String key){
readWriteLock.readLock().lock();//读锁
try {
System.out.println(Thread.currentThread().getName() + "开始读取数据:" + key);
Object o = map.get(key);
System.out.println(Thread.currentThread().getName() + "读取数据完成并返回:" + o.toString());
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
readWriteLock.readLock().unlock();
}
}
}
/**
* 自定义缓存
*/
class MyCache{
private volatile Map<String,Object> map = new HashMap<>();
public void put(String key,Object object){
System.out.println(Thread.currentThread().getName() + "开始写入数据:" + key);
map.put(key,object);
System.out.println(Thread.currentThread().getName() + "写入数据:OK");
}
public void get(String key){
System.out.println(Thread.currentThread().getName() + "开始读取数据:" + key);
Object o = map.get(key);
System.out.println(Thread.currentThread().getName() + "读取数据完成:" + o.toString());
}
}
第十节:阻塞队列
**什么情况下会使用阻塞队列:**多线程并发处理、线程池!
package com.seapp.queue;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;
/**
* @author seapp
* @date 2021/6/15 15:28
*/
public class BlockingQueueDemo {
public static void main(String[] args) throws InterruptedException {
// test1();//抛出异常
// test2();//不抛出异常,有返回值
// test3();//一直等待下去
test4();//等待,等超时后停止等待。并给出返回值。
}
/**
* 抛出异常
*/
public static void test1(){
ArrayBlockingQueue<Integer> blockingQueue = new ArrayBlockingQueue<>(3);
//添加add
blockingQueue.add(1);
blockingQueue.add(2);
blockingQueue.add(3);
//测试添加多余初始设定时,是否会报错(Exception in thread "main" java.lang.IllegalStateException: Queue full)
// blockingQueue.add(4);
//移除
System.out.println(blockingQueue.remove());
System.out.println(blockingQueue.remove());
System.out.println(blockingQueue.remove());
//当队列元素为空时,再执行移除操作会报异常(Exception in thread "main" java.util.NoSuchElementException)
System.out.println(blockingQueue.remove());
}
/**
* 有返回值,不抛出异常
*/
private static void test2() {
ArrayBlockingQueue<Integer> blockingQueue = new ArrayBlockingQueue<>(3);
//添加元素
System.out.println(blockingQueue.offer(1));
System.out.println(blockingQueue.offer(2));
System.out.println(blockingQueue.offer(3));
//当使用blockingQueue.offer添加元素多于初始设定大小时,不会抛出异常。执行结果返回false
System.out.println(blockingQueue.offer(4));
//获取元素
System.out.println(blockingQueue.poll());
System.out.println(blockingQueue.poll());
System.out.println(blockingQueue.poll());
//当使用blockingQueue.poll获取元素多于存入值时,不会抛出异常。执行结果为null
System.out.println(blockingQueue.poll());
}
public static void test3() throws InterruptedException {
ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue(3);
blockingQueue.put(1);
blockingQueue.put(2);
blockingQueue.put(3);
//当给队列中添加的元素多余初始设定值时,会一直等待...
// blockingQueue.put(4);
System.out.println(blockingQueue.take());
System.out.println(blockingQueue.take());
System.out.println(blockingQueue.take());
//当给队列中获取的元素多余初始设定值时,会一直等待...
System.out.println(blockingQueue.take());
}
public static void test4() throws InterruptedException {
ArrayBlockingQueue<Object> blockingQueue = new ArrayBlockingQueue<>(3);
System.out.println(blockingQueue.offer(1));
System.out.println(blockingQueue.offer(2));
System.out.println(blockingQueue.offer(3));
//当使用blockingQueue.offer添加队列数量超过初始设定值时,会等待设定的时长。等超时后,退出等待。
System.out.println(blockingQueue.offer(4, 2, TimeUnit.SECONDS));
System.out.println(blockingQueue.poll());
System.out.println(blockingQueue.poll());
System.out.println(blockingQueue.poll());
System.out.println(blockingQueue.poll(2,TimeUnit.SECONDS));//超出时,等待。超时后,返回处理结果null
}
}
第十一节:线程池
**线程池:**三大方法、七大参数、四种拒绝策略。
- 池化技术:程序的运行,本质是占用系统的资源!优化资源的使用====》池化技术。
- 池化技术事先准备好一些资源,有人要用,就来我这里拿,用完之后还给我。
线程池的好处:
- 降低资源的消耗
- 提高响应的速度
- 方便管理。
线程复用,可以控制最大并发数、管理线程。
11.1 线程池的三大方法:
ExecutorService threadPool = Executors.newSingleThreadExecutor();//单个线程
ExecutorService threadPool = Executors.newFixedThreadPool(5);//指定数目的线程池
ExecutorService threadPool = Executors.newCachedThreadPool();//可伸缩的,依照需求量来定
/**
* @param corePoolSize //核心线程池大小
* @param maximumPoolSize //最大核心线程池大小
* @param keepAliveTime //超时了没被调用,就会被释放
* @param unit //超时单位
* @param workQueue //阻塞队列
* @param threadFactory //线程工厂,创建线程的。一般不用动
* @param handler //拒绝策略
*/
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
}
银行实例
//线程池本质上是ThreadPoolExecutor,自定义线程池
/**
// * @param corePoolSize //核心线程池大小
// * @param maximumPoolSize //最大核心线程池大小
// * @param keepAliveTime //超时了没被调用,就会被释放
// * @param unit //超时单位
// * @param workQueue //阻塞队列
// * @param threadFactory //线程工厂,创建线程的。一般不用动
// * @param handler //拒绝策略
// */
ExecutorService threadPool = new ThreadPoolExecutor(5,10,3,TimeUnit.SECONDS,
new LinkedBlockingDeque<>(),Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy());
11.2 四种拒绝策略
11.3 池的最大和最小如何去设置?
了解:IO密集型和CPU密集型(调优)
第十二节:lambda表达式,链式编程,函数式编程、Stream流式计算
lambda表达式,链式编程,函数式编程、Stream流式计算
12.1 四大函数式接口:只有一个方法的接口
- Function函数式编程
package com.seapp.function;
import java.util.function.Function;
/**
* @author seapp
* @date 2021/6/16 9:20
* function函数式接口,有一个输入参数,有一个返回参数
* 只要是函数式接口,就可以用lambda表达式简化
*/
public class FunctionDemo {
public static void main(String[] args) {
// Function<String, Integer> stringIntegerFunction = new Function<String,Integer>() {
//
// @Override
// public Integer apply(String s) {
// return Integer.valueOf(s);
// }
// };
//
// Integer apply = stringIntegerFunction.apply("5");
// System.out.println("apply = " + apply);
//lambda表达式实现
Function<String,Integer> function = (str)->{return Integer.valueOf(str);};
System.out.println(function.apply("5"));
}
}
- 断定型接口:有一个输入型参数,返回值只能是布尔值。
package com.seapp.function;
import com.sun.deploy.util.StringUtils;
import java.util.function.Predicate;
/**
* @author seapp
* @date 2021/6/16 9:34
* 断定型接口:有一个输入值,输出只能是布尔值
*
*/
public class PredicateDemo {
public static void main(String[] args) {
/* Predicate<String> stringPredicate = new Predicate<String>() {
@Override
public boolean test(String s) {
return s.isEmpty();
}
};
System.out.println(stringPredicate.test("test"));*/
//lambda表达式测试
Predicate<String> predicate = (str)->{
return str.isEmpty();
};
System.out.println(predicate.test(""));
}
}
- Consumer:消费型接口。只有输入,没有返回值
package com.seapp.function;
import java.util.function.Consumer;
/**
* @author seapp
* @date 2021/6/16 9:53
* 消费型接口:只有输入没有输出
*/
public class ConsumerDemo {
public static void main(String[] args) {
Consumer<String> consumer = new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println("s = " + s);
}
};
consumer.accept("consumer");
//lambda表达式测试:
Consumer<String> lambda = (str)->{
System.out.println("str = " + str);
};
lambda.accept("lambdaConsumer");
}
}
- Supplier 供给型接口
package com.seapp.function;
import java.util.function.Supplier;
/**
* @author seapp
* @date 2021/6/16 10:00
* 供给型接口:没有输入,只有返回值
*/
public class SupplierDemo {
public static void main(String[] args) {
Supplier<Integer> supplier = new Supplier<Integer>() {
@Override
public Integer get() {
System.out.println("get方法被调用了");
return 1024;
}
};
System.out.println(supplier.get());
//lambda表示式的调用
Supplier<Integer> lambdaSupplier = ()->{
System.out.println("get方法被调用了");
return 2048;
};
System.out.println(lambdaSupplier.get());
}
}
12.2 Stream流式计算
大数据:存储+计算
集合、Mysql本质上就是存储数据
计算都应该交给流来操作。
实例验证:
package com.seapp.stream;
import java.util.Arrays;
import java.util.List;
/**
* 流式计算验证
* @author seapp
* @date 2021/6/16 10:18
*/
public class StreamDemo {
/**
* 题目:现有5个用户,筛选
* 1、ID必须是偶数
* 2、年龄必须大于23岁
* 3、用户名转为大写字母
* 4、用户字母倒着排序
* 5、只输出一个用户
* @param args
*/
public static void main(String[] args) {
User u1 = new User(1,"a",21);
User u2 = new User(2,"b",22);
User u3 = new User(3,"c",23);
User u4 = new User(4,"d",24);
User u5 = new User(5,"e",25);
List<User> list = Arrays.asList(u1, u2, u3, u4, u5);
list.stream().filter(user ->{return user.getId()%2 ==0;})
// .filter(user -> {return user.getAge() > 23;}) //lambda表达式中return的省略,方式如下:
.filter(user -> user.getAge() > 23)
.map(user ->{return user.getName().toUpperCase();})
.limit(1)
.forEach(System.out::println);
}
}
第十三节:ForkJoin
什么是ForkJoin: 在JDK1.7中,并行执行任务!提高效率。大数据量!
大数据: Map Reduce(把大任务拆分成小任务)
ForkJoin特点:工作窃取 (任务队列使用的是双端队列)
package com.seapp.forkjoin;
import java.util.concurrent.RecursiveTask;
/**
*
* @author seapp
* @date 2021/6/16 10:45
* forkJoin的使用
* 1.通过forkJoinPool来执行
* 2.计算任务:forkJoinPool.execute(ForkJoinTask task)
* 3.计算类要继承 ForkJoinTask;
*
*/
public class ForkJoinDemo extends RecursiveTask<Long> {
private Long start;//起始值
private Long end;//结束值
private Long temp = 10000L;//临界值
public ForkJoinDemo(Long start, Long end) {
this.start = start;
this.end = end;
}
@Override
protected Long compute() {
if((end-start) <temp){//小于临界值,执行加运算
Long sum = 0L;
for (Long i = start; i <end ; i++) {
sum +=i;
}
return sum;
}else {
//大于临界值,进行拆分。并递归执行加运行
Long middle = (start + end)/2;
//前半段的运算
ForkJoinDemo task1 = new ForkJoinDemo(start, middle);
task1.fork();//将拆分的任务队列压入栈中
ForkJoinDemo task2 = new ForkJoinDemo(middle, end);
task2.fork();//将拆分的任务队列压入栈中
//获取任务执行的结果
return task1.join() + task2.join();
}
}
}
package com.seapp.forkjoin;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
import java.util.stream.LongStream;
/**
* @author seapp
* @date 2021/6/16 10:56
* forkJoin测试类
*/
public class ForkJoinTest {
public static void main(String[] args) throws ExecutionException, InterruptedException {
// test1();//简单的加法运算 sum = 499999999500000000 耗时时间:5003
// test2();//通过forkJoin进行计算aLong = 499999999500000000 耗时时长:8838
test3();//通过流式计算来计算和:sum = 499999999500000000 耗时时间:509
}
private static void test1() {
Long sum = 0L;
long start = System.currentTimeMillis();
for (long i = 1L; i < 1000_000_000L; i++) {
sum += i;
}
long end = System.currentTimeMillis();
System.out.println("sum = " + sum);
System.out.println("耗时时间:" + (end - start));
}
private static void test2() throws ExecutionException, InterruptedException {
long start = System.currentTimeMillis();
ForkJoinDemo task = new ForkJoinDemo(0L, 1000_000_000L);
ForkJoinPool forkJoinPool = new ForkJoinPool();
ForkJoinTask<Long> submit = forkJoinPool.submit(task);
Long aLong = submit.get();
long end = System.currentTimeMillis();
System.out.println("aLong = " + aLong);
System.out.println("耗时时长:"+(end-start));
}
private static void test3() {
long start = System.currentTimeMillis();
long sum = LongStream.range(0L,1000_000_000L).parallel().reduce(0,Long::sum);
System.out.println("sum = " + sum);
long end = System.currentTimeMillis();
System.out.println("耗时时间:" + (end - start));
}
}
第十四节:异步回调
Future: 设计的初衷,对将来的某个事件的结果进行建模。
package com.seapp.future;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
/**
* 异步调用测试
* @author seapp
* @date 2021/6/16 14:44
*/
public class FutureDemo {
public static void main(String[] args) throws ExecutionException, InterruptedException {
/* //异步执行无返回值得调用
CompletableFuture<Void> future = CompletableFuture.runAsync(()->{
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " runAsync--->Void");
});
System.out.println(Thread.currentThread().getName() + " main ");//主线程先调用。
//执行异步回调
future.get();
*/
//有返回值得回调。supplyAsync
CompletableFuture<Integer> completableFuture = CompletableFuture.supplyAsync(()->{
System.out.println(Thread.currentThread().getName() + "supplyAsync--->Integer" );
int i = 10/0;
return 1024;
});
//获取结果
completableFuture.whenComplete((t,u)->{
System.out.println("t = " + t); //成功的返回结果
System.out.println("u = " + u); //异常信息
}).exceptionally((e)->{
System.out.println(e.getMessage()); //异常信息
return 400;
});
}
}
执行结果:
第十五节:JMM
15.1 谈谈对Volatile的理解:
Volatile是Java虚拟机提供轻量级的同步机制,有三个重要特性:
- 保障可见性
- 不保障原子性
- 禁止指令重排
15.2 什么是JMM:
JMM: Java内存模型,不存在的东西。概念!约定!
- 线程解锁前,必须把共享变量立刻刷回主内存。
- 线程加锁前,必须读取主内存中的最新值到工作内存中。
- 加锁和解锁是同一把锁。
JMM的8中操作:
内存交互操作有8种,虚拟机实现必须保证每一个操作都是原子的,不可在分的(对于double和long类
型的变量来说,load、store、read和write操作在某些平台上允许例外)
- lock (锁定):作用于主内存的变量,把一个变量标识为线程独占状态
- unlock (解锁):作用于主内存的变量,它把一个处于锁定状态的变量释放出来,释放后的变量
才可以被其他线程锁定 - read (读取):作用于主内存变量,它把一个变量的值从主内存传输到线程的工作内存中,以便
随后的load动作使用 - load (载入):作用于工作内存的变量,它把read操作从主存中变量放入工作内存中
- use (使用):作用于工作内存中的变量,它把工作内存中的变量传输给执行引擎,每当虚拟机
遇到一个需要使用到变量的值,就会使用到这个指令 - assign (赋值):作用于工作内存中的变量,它把一个从执行引擎中接受到的值放入工作内存的变
量副本中 - store (存储):作用于主内存中的变量,它把一个从工作内存中一个变量的值传送到主内存中,
以便后续的write使用 - write (写入):作用于主内存中的变量,它把store操作从工作内存中得到的变量的值放入主内
存的变量中
JMM对这八种指令的使用,制定了如下规则: - 不允许read和load、store和write操作之一单独出现。即使用了read必须load,使用了store必须
write - 不允许线程丢弃他最近的assign操作,即工作变量的数据改变了之后,必须告知主存
- 不允许一个线程将没有assign的数据从工作内存同步回主内存
- 一个新的变量必须在主内存中诞生,不允许工作内存直接使用一个未被初始化的变量。就是怼变量
实施use、store操作之前,必须经过assign和load操作 - 一个变量同一时间只有一个线程能对其进行lock。多次lock后,必须执行相同次数的unlock才能解
锁 - 如果对一个变量进行lock操作,会清空所有工作内存中此变量的值,在执行引擎使用这个变量前,
必须重新load或assign操作初始化变量的值 - 如果一个变量没有被lock,就不能对其进行unlock操作。也不能unlock一个被其他线程锁住的变量
对一个变量进行unlock操作之前,必须把此变量同步回主内存
存在的东西。概念!约定!**
- 线程解锁前,必须把共享变量立刻刷回主内存。
- 线程加锁前,必须读取主内存中的最新值到工作内存中。
- 加锁和解锁是同一把锁。
JMM的8中操作:
[外链图片转存中…(img-PFpk4Opk-1634117381689)]
[外链图片转存中…(img-RkIZG3Pw-1634117381690)]
内存交互操作有8种,虚拟机实现必须保证每一个操作都是原子的,不可在分的(对于double和long类
型的变量来说,load、store、read和write操作在某些平台上允许例外)
- lock (锁定):作用于主内存的变量,把一个变量标识为线程独占状态
- unlock (解锁):作用于主内存的变量,它把一个处于锁定状态的变量释放出来,释放后的变量
才可以被其他线程锁定 - read (读取):作用于主内存变量,它把一个变量的值从主内存传输到线程的工作内存中,以便
随后的load动作使用 - load (载入):作用于工作内存的变量,它把read操作从主存中变量放入工作内存中
- use (使用):作用于工作内存中的变量,它把工作内存中的变量传输给执行引擎,每当虚拟机
遇到一个需要使用到变量的值,就会使用到这个指令 - assign (赋值):作用于工作内存中的变量,它把一个从执行引擎中接受到的值放入工作内存的变
量副本中 - store (存储):作用于主内存中的变量,它把一个从工作内存中一个变量的值传送到主内存中,
以便后续的write使用 - write (写入):作用于主内存中的变量,它把store操作从工作内存中得到的变量的值放入主内
存的变量中
JMM对这八种指令的使用,制定了如下规则: - 不允许read和load、store和write操作之一单独出现。即使用了read必须load,使用了store必须
write - 不允许线程丢弃他最近的assign操作,即工作变量的数据改变了之后,必须告知主存
- 不允许一个线程将没有assign的数据从工作内存同步回主内存
- 一个新的变量必须在主内存中诞生,不允许工作内存直接使用一个未被初始化的变量。就是怼变量
实施use、store操作之前,必须经过assign和load操作 - 一个变量同一时间只有一个线程能对其进行lock。多次lock后,必须执行相同次数的unlock才能解
锁 - 如果对一个变量进行lock操作,会清空所有工作内存中此变量的值,在执行引擎使用这个变量前,
必须重新load或assign操作初始化变量的值 - 如果一个变量没有被lock,就不能对其进行unlock操作。也不能unlock一个被其他线程锁住的变量
对一个变量进行unlock操作之前,必须把此变量同步回主内存