JUC并发编程
并发和并行
并发:
- cpu一核,模拟出来多条线程,快速交替
并行:
- cpu多核,多个线程可以同时执行;线程池
//获得当前电脑的核心数
public class Demo1 {
public static void main(String[] args) {
System.out.println(Runtime.getRuntime().availableProcessors());
}
}
线程的几个状态
线程状态。线程可以处于以下状态之一:
- NEW
尚未启动的线程处于此状态。 - RUNNABLE
在Java虚拟机中执行的线程处于此状态。 - BLOCKED
被阻塞等待监视器锁定的线程处于此状态。 - WAITING
正在等待另一个线程执行特定动作的线程处于此状态。 - TIMED_WAITING
正在等待另一个线程执行动作达到指定等待时间的线程处于此状态。 - TERMINATED
已退出的线程处于此状态。
一个线程可以在给定时间点处于一个状态。这些状态是不反映任何操作系统线程状态的虚拟机状态。
wait/sleep区别
- wait是Object类中的方法,sleep是Thread类中的方法
- wait会释放锁,sleep不会释放锁
- wait只能再同步代码块中使用,sleep可以再任何地方
- sleep必须捕获超时等待异常
Lock锁
如何判断锁的是谁
new:锁的是对象(this)
static:锁的是类(class)
Synchronized与Lock的区别
- Synchronized内置的java关键字,Lock是一个java类
- Synchronized无法判断锁的状态,Lock可以判断是否获得锁
- Synchronized会自动释放锁,Lock必须手动释放锁
- Lock锁可以通过lock.tryLock()方法尝试获取锁
- Synchronized 可重入锁,不可以中断的,非公平;
- Lock,可重入锁,可以判断锁,可以自己设置是否公平
- Synchronized适合锁少量代码同步问题,Lock锁大量同步代码
传统Synchronized 同步代码块
synchronized本质:队列,锁
public class SaleTicketDemo02 {
public static void main(String[] args) {
Ticket ticket = new Ticket();
new Thread(()->{
for (int i = 1; i <60 ; i++) {
ticket.sale();
}
},"1").start();
new Thread(()->{
for (int i = 1; i <60 ; i++) {
ticket.sale();
}
},"2").start();
new Thread(()->{
for (int i = 1; i <60 ; i++) {
ticket.sale();
}
},"3").start();
}
}
//资源类 OOP编程
class Ticket{
private int number = 50;
public synchronized void sale(){
if (number>0){
System.out.println(Thread.currentThread().getName()+"卖出了第"+(number--)+"张票,剩余"+number+"张票");
}
}
}
使用JUC的Lock锁
三个实现类
- ReentrantLock 可重入锁
- ReadLock 读锁
- WriteLock 写锁
ReentrantLock 中有公平锁(FairSync)和非公平锁(NonfairSync)
公平锁:十分公平,可以先来后到
非公平锁:十分不公平,可以插队(默认)
public class SaleTicketDemo03 {
public static void main(String[] args) {
Ticket2 ticket = new Ticket2();
new Thread(()->{
for (int i = 1; i <60 ; i++) {
ticket.sale();
}
},"1").start();
new Thread(()->{
for (int i = 1; i <60 ; i++) {
ticket.sale();
}
},"2").start();
new Thread(()->{
for (int i = 1; i <60 ; i++) {
ticket.sale();
}
},"3").start();
}
}
//资源类 OOP编程
//1.new 一个ReentrantLock();
//2.lock.lock(); 加锁
//3.lock.unlock();解锁
class Ticket2{
private int number = 50;
Lock lock = new ReentrantLock();
public void sale(){
//加锁
lock.lock();
try{
if (number>0){
System.out.println(Thread.currentThread().getName()+"卖出了第"+(number--)+"张票,剩余"+number+"张票");
}
}finally {
//解锁
lock.unlock();
}
}
}
生产者和消费者问题
Synchronized解决生产者消费者问题
/*
* 线程通信问题,生产者消费者问题
* */
public class A {
public static void main(String[] args) {
Data data = new Data();
new Thread(()->{
for (int i = 0; i <10 ; i++) {
try {
data.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"a").start();
new Thread(()->{
for (int i = 0; i <10 ; i++) {
try {
data.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"b").start();
new Thread(()->{
for (int i = 0; i <10 ; i++) {
try {
data.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"c").start();
new Thread(()->{
for (int i = 0; i <10 ; i++) {
try {
data.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"d").start();
}
}
//资源类
class Data{
private int number = 0;
//+1
public synchronized void increment() throws InterruptedException {
while (number!=0){
//等待
this.wait();
}
number++;
System.out.println(Thread.currentThread().getName()+"-->"+number);
//通知其他线程
this.notifyAll();
}
//-1
public synchronized void decrement() throws InterruptedException {
while (number==0){
//等待
this.wait();
}
number--;
System.out.println(Thread.currentThread().getName()+"-->"+number);
//通知其他线程
this.notifyAll();
}
}
Lock解决生产者消费者问题
public class B {
public static void main(String[] args) {
Data2 data2 = new Data2();
new Thread(()->{
for (int i = 0; i <10 ; i++) {
try {
data2.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"a").start();
new Thread(()->{ for (int i = 0; i <10 ; i++) {
try {
data2.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}},"b").start();
}
}
class Data2 {
private int number = 0;
final Lock lock = new ReentrantLock();
final Condition notFull = lock.newCondition();
final Condition notEmp = lock.newCondition();
public void increment() throws InterruptedException {
//加锁
lock.lock();
try{
while (number!=0){
//等待
notEmp.await();
}
number++;
System.out.println(Thread.currentThread().getName()+number);
//唤醒
notFull.signal();
}finally {
//解锁
lock.unlock();
}
}
public void decrement() throws InterruptedException {
//加锁
lock.lock();
try{
while (number==0){
//等待
notFull.await();
}
number--;
System.out.println(Thread.currentThread().getName()+number);
//唤醒
notEmp.signal();
}finally {
//解锁
lock.unlock();
}
}
}
集合类不安全
List不安全
public class ListTest {
public static void main(String[] args) {
/*
* java.util.ConcurrentModificationException
* 并发修改异常,线程不安全
* 解决方案:
* 1. List<String> list = new Vector<>(); JDK1.0就有了,将add方法加入synchronized关键字,不推荐
* 2. List<String> list = Collections.synchronizedList(new ArrayList<>());使用集合工具类转化
* 3. List<String> list = new CopyOnWriteArrayList<>();JUC提供的,使用Lock锁,写入的时候复制一份,写完set回去
* */
//List<String> list = new ArrayList<>();
//List<String> list = new Vector<>();
// List<String> list = Collections.synchronizedList(new ArrayList<>());
List<String> list = new CopyOnWriteArrayList<>();
for (int i = 1; i <=10 ; i++) {
new Thread(()->{
list.add(UUID.randomUUID().toString().substring(0,5));
System.out.println(list);
},String.valueOf(i)).start();
}
}
}
Set不安全
public class SetTest {
public static void main(String[] args) {
/*
* java.util.ConcurrentModificationException
* 同样的并发错误
* 解决方法:
* 1.Set<String> set = Collections.synchronizedSet(new HashSet<>());集合工具类转换
* 2.Set<String> set = new CopyOnWriteArraySet<>();
* */
// Set<String> set = new HashSet<>();
// Set<String> set = Collections.synchronizedSet(new HashSet<>());
Set<String> set = new CopyOnWriteArraySet<>();
for (int i = 1; i <= 30; i++) {
new Thread(()->{
set.add(UUID.randomUUID().toString().substring(0,5));
System.out.println(set);
},String.valueOf(i)).start();
}
}
}
HashSet的底层是什么
底层就是HashMap(源码)
public HashSet() {
map = new HashMap<>();
}
add方法是什么(源码)
就是HashMap的put方法,利用了HashMap的Key不重复的机制,令他的Value为一个常量,用Key来保存数据
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
Map不安全
public class MapTest {
public static void main(String[] args) {
/*
* java.util.ConcurrentModificationException
* 并发修改异常
* 解决方案:
* 1.Map<String, String> map = Collections.synchronizedMap(new HashMap<>());集合工具类转换
* 2.Map<String, String> map = new ConcurrentHashMap<>();
* */
// Map<String, String> map = new HashMap<>();
//Map<String, String> map = Collections.synchronizedMap(new HashMap<>());
Map<String, String> map = new ConcurrentHashMap<>();
for (int i = 1; i <=30 ; i++) {
new Thread(()->{
map.put(Thread.currentThread().getName(), UUID.randomUUID().toString().substring(0,5));
System.out.println(map);
},String.valueOf(i)).start();
}
}
}
ConcurrentHashMap的底层原理
https://www.cnblogs.com/chengxiao/p/6842045.html
Callable
- 可以有返回值
- 可以抛出异常
- 方法不同,不是run()而是call()
Callable无法直接执行,他需要利用FutureTask跟Runnable产生关系,然后执行
public class CallableTest {
public static void main(String[] args) throws ExecutionException, InterruptedException {
MyThread myThread = new MyThread();
FutureTask task = new FutureTask(myThread);
new Thread(task).start();
String o = (String) task.get();
//get方法可能会产生阻塞,把他放到最后,或者使用异步
System.out.println("返回的结果:"+o);
}
}
class MyThread implements Callable<String>{
@Override
public String call(){
System.out.println("call()");
return "1234";
}
}
常用的辅助类
1、CountDownLatch
//减法计数器
public class CountDownLatchDemo {
public static void main(String[] args) throws InterruptedException {
//总数是6
CountDownLatch countDownLatch = new CountDownLatch(6);
for (int i = 1; i <=6 ; i++) {
new Thread(()->{
System.out.println(Thread.currentThread().getName()+"Go Out");
countDownLatch.countDown();
},String.valueOf(i)).start();
}
//等待计数器归零,然后向下执行
countDownLatch.await();
System.out.println("克里斯关下门");
}
}
原理:
countDownLatch.countDown();//数量减一
countDownLatch.await(); //等待计数器归零,然后向下执行
每次调用countDown计数器减一,假设计数器变为0,countDownLatch.await();就会被唤醒继续执行
2、CyclicBarrier
//加法计数器
public class CyclicbarrierTest {
public static void main(String[] args) {
//当线程数达到指定个数,执行参数中的指定线程
CyclicBarrier cyclicBarrier = new CyclicBarrier(7,()->{
System.out.println("召唤神龙");
});
for (int i = 1; i <=7 ; i++) {
final int temp = i;
//lambda表达式读取不到i,需要利用final来辅助
new Thread(()->{
System.out.println(Thread.currentThread().getName()+"收集"+temp+"个龙珠");
try {
//等待
cyclicBarrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}).start();
}
}
}
3、Semaphore
public class SemaphoreTest {
public static void main(String[] args) {
//参数默认线程数量
Semaphore semaphore = new Semaphore(3);
for (int i = 1; i <= 6; i++) {
new Thread(() -> {
//acquire 得到
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 {
//release 释放
semaphore.release();
}
},String.valueOf(i)).start();
}
}
}
原理:
semaphore.acquire();获得信号量-1,假如已经满了,等待知道被释放为止
semaphore.release();释放,会将信号量释放+1,然后唤醒等待线程
作用:多个共享资源互斥的使用!并发限流,控制最大线程数
读写锁
读锁:共享锁
写锁:独占锁
public class ReadWriteLockTest {
public static void main(String[] args) {
MyCache myCache = new MyCache();
for (int i = 1; i <= 5; i++) {
final int temp = i;
new Thread(() -> {
myCache.put(temp + "", temp + "");
}, String.valueOf(i)).start();
}
for (int i = 1; i <= 5; i++) {
final int temp = i;
new Thread(() -> {
myCache.get(temp + "");
}, String.valueOf(i)).start();
}
}
}
//自定义一个缓存
class MyCache {
private volatile Map<String, Object> map = new HashMap<>();
final ReadWriteLock lock = new ReentrantReadWriteLock();
//写入的时候只有一个线程再写
public void put(String key, Object value) {
lock.writeLock().lock();
try {
System.out.println(Thread.currentThread().getName() + "写入" + key);
map.put(key, value);
System.out.println(Thread.currentThread().getName() + "写入OK");
} finally {
lock.writeLock().unlock();
}
}
//所有人都可以读
public Object get(String key) {
lock.readLock().lock();
Object o = null;
try {
System.out.println(Thread.currentThread().getName() + "读取" + key);
o = map.get(key);
System.out.println(Thread.currentThread().getName() + "读取OK");
} finally {
lock.readLock().unlock();
}
return o;
}
}
阻塞队列
写入:如果队列满了,就必须阻塞等待读取
读取:如果队列空的,就必须阻塞等待写入
BlockingQueue
接口的实现类
- ArrayBlockingQueue 数组
- DelayQueue 延时
- LinkedBlockingQueue 链表
- SynchronousQueue 同步队列
阻塞队列的使用:对线程并发处理,线程池!
ArrayBlockingQueue
方式 | 抛出异常 | 有返回值,无异常 | 阻塞 等待 | 超时等待 |
---|---|---|---|---|
添加 | add | offer | put | offer(,) |
移除 | remove | poll | take | poll(,) |
检测队首元素 | element | peek |
/*
* 抛出异常
* */
public static void test1(){
//队列的大小
ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);
System.out.println(blockingQueue.add("a"));
System.out.println(blockingQueue.add("b"));
System.out.println(blockingQueue.add("c"));
System.out.println("----------------------------");
System.out.println(blockingQueue.element());
System.out.println("----------------------------");
// System.out.println(blockingQueue.add("d"));//添加第四个,抛出异常 java.lang.IllegalStateException: Queue full
System.out.println(blockingQueue.remove());
System.out.println(blockingQueue.remove());
System.out.println(blockingQueue.remove());
System.out.println("----------------------------");
System.out.println(blockingQueue.element());//没有元素了,抛出异常java.util.NoSuchElementException
// System.out.println(blockingQueue.remove());//当读取第四个,抛出异常 java.util.NoSuchElementException
}
/*
* 不抛出异常
* */
public static void test2(){
//队列大小
ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);
System.out.println(blockingQueue.offer("a"));
System.out.println(blockingQueue.offer("b"));
System.out.println(blockingQueue.offer("c"));
System.out.println(blockingQueue.offer("d"));//false 不抛出异常
System.out.println(blockingQueue.poll());
System.out.println(blockingQueue.poll());
System.out.println(blockingQueue.poll());
System.out.println(blockingQueue.poll());//null 不抛出异常
System.out.println(blockingQueue.peek());//null 不抛出异常
}
/*
* 等待,阻塞(一致阻塞)
* */
public static void test3() throws InterruptedException {
ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);
//一直阻塞
blockingQueue.put("a");
blockingQueue.put("b");
blockingQueue.put("c");
//blockingQueue.put("d");//没有位置一致阻塞
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 blockingQueue = new ArrayBlockingQueue<>(3);
//阻塞
blockingQueue.offer("a");
blockingQueue.offer("b");
blockingQueue.offer("c");
blockingQueue.offer("d", 2,TimeUnit.SECONDS);
System.out.println("-----------------");
System.out.println(blockingQueue.poll());
System.out.println(blockingQueue.poll());
System.out.println(blockingQueue.poll());
System.out.println(blockingQueue.poll(2, TimeUnit.SECONDS));
}
SynchronousQueue
和其他BlockingQueue不一样,SynchronousQueue 不存储元素,put了一个元素必须从里面取出来,不然不能再put
public class SynchronousQueueTest {
public static void main(String[] args) {
SynchronousQueue<String> synchronousQueue = new SynchronousQueue<>();
new Thread(() -> {
try {
System.out.println(Thread.currentThread().getName() + "put1");
synchronousQueue.put("1");
System.out.println(Thread.currentThread().getName() + "put2");
synchronousQueue.put("2");
System.out.println(Thread.currentThread().getName() + "put3");
synchronousQueue.put("3");
} catch (InterruptedException e) {
}
}, "T1").start();
new Thread(() -> {
try {
TimeUnit.SECONDS.sleep(3);
System.out.println(Thread.currentThread().getName()+"=>" + synchronousQueue.take());
TimeUnit.SECONDS.sleep(3);
System.out.println(Thread.currentThread().getName() +"=>"+ synchronousQueue.take());
TimeUnit.SECONDS.sleep(3);
System.out.println(Thread.currentThread().getName() +"=>"+ synchronousQueue.take());
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "T2").start();
}
}
线程池
线程池的好处:线程复用,控制最大并发数,可以管理线程
- 减低资源消耗
- 提高响应的速度
- 方便管理
线程池:三大方法、七大参数、四种拒绝策略
三大方法
Executors.newSingleThreadExecutor();//单个线程
Executors.newFixedThreadPool(5);//创建固定的线程池的大小
Executors.newCachedThreadPool();//可伸缩的
//Executors 工具类的三大方法
//使用线程池来创建线程
public class Demo01 {
public static void main(String[] args) {
// ExecutorService threadpool = Executors.newSingleThreadExecutor();//单个线程
// ExecutorService threadpool = Executors.newFixedThreadPool(5);//创建固定的线程池的大小
ExecutorService threadpool = Executors.newCachedThreadPool();//可伸缩的
try {
for (int i = 0; i < 100; i++) {
//使用线程池创建线程
threadpool.execute(() -> {
System.out.println(Thread.currentThread().getName() + "ok");
});
}
} finally {
//线程池需要关闭
threadpool.shutdown();
}
}
}
七大参数
三大方法的源码
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE//21亿,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
三大方法都使用了ThreadPoolExecutor方法创建
这个方法的参数就是七大参数
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.acc = System.getSecurityManager() == null ?
null :
AccessController.getContext();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
七大参数
- int corePoolSize 核心线程池大小
- int maximumPoolSize 最大核心线程池大小
- long keepAliveTime 超时了没有人调用就会释放(空闲线程的存活时间)
- TimeUnit unit 超时单位
- BlockingQueue workQu 阻塞队列
- ThreadFactory threadFactory 线程工厂:创建线程的,一般不用动
- RejectedExecutionHandler handler 拒绝策略
四种拒绝策略
new ThreadPoolExecutor.AbortPolicy(); //超出最大承载报异常
new ThreadPoolExecutor.CallerRunsPolicy());//超出的哪来的回哪去;例如当前程序超出的就由main线程执行
new ThreadPoolExecutor.DiscardPolicy(); //队列满了,丢掉任务,不会抛出异常
new ThreadPoolExecutor.DiscardOldestPolicy();//队列满了,尝试去和最早的竞争,失败丢掉任务,成功正常执行
最大线程如何定义
-
CPU密集型:定义为CPU的核数
Runtime.getRuntime().availableProcessors()//获取当前电脑的CPU核数
-
IO 密集型:判断程序中十分耗IO的线程,可以设置为IO线程的两倍
四大函数式接口
只有一个方法的接口叫做函数式接口
Function
public interface Function<T, R> {
R apply(T t);
}
输入T类型,放回R类型
public class Demo01 {
public static void main(String[] args) {
Function<String,String> function = new Function<String,String>() {
@Override
public String apply(String o) {
return o;
}
};
System.out.println(function.apply("aaa"));
//使用lambda表达式
Function<String,String> function2 = (str)->{ return str; };
System.out.println(function2.apply("bbb"));
}
}
Predicate
断定式接口
public interface Predicate<T> {
boolean test(T t);
}
输入T类型参数,固定返回boolean类型
public class Demo02 {
public static void main(String[] args) {
Predicate<String> predicate = new Predicate<String>(){
@Override
public boolean test(String s) {
return s.isEmpty();
}
};
System.out.println(predicate.test(""));
//使用lambda表达式
Predicate<String> predicate2 = (str)->{ return str.isEmpty(); };
System.out.println(predicate2.test("123"));
}
}
Consumer
消费型接口
public interface Consumer<T> {
void accept(T t);
}
只有输入类型T ,没有返回值
public class Demo03 {
public static void main(String[] args) {
Consumer<String> consumer = new Consumer<String>() {
@Override
public void accept(String o) {
System.out.println(o);
}
};
consumer.accept("123");
//使用lambda表达式
Consumer<String> consumer2 = (str)->{ System.out.println(str); };
consumer2.accept("456");
}
}
Supplier
供给型接口
public interface Supplier<T> {
T get();
}
没有参数,只有T类型返回值
public class Demo04 {
public static void main(String[] args) {
Supplier<String> supplier = new Supplier<String>() {
@Override
public String get() {
return "1024";
}
};
System.out.println(supplier.get());
//使用lambda表达式
Supplier<String> supplier2 = ()->{ return "2048"; };
System.out.println(supplier2.get());
}
}
Stream流式计算
public class StreamTest {
/*
* 使用一行代码实现
* 1.筛选id为偶数
* 2.年龄大于23岁
* 3.用户名转为大写
* 4.用户名倒叙排列
* 5.只输出一个用户
* */
public static void main(String[] args) {
User u1 = new User(1, "a", 21);
User u2 = new User(2, "b", 23);
User u3 = new User(3, "c", 25);
User u4 = new User(4, "d", 27);
User u5 = new User(5, "e", 29);
User u6 = new User(6, "f", 30);
List<User> users = Arrays.asList(u1, u2, u3, u4, u5,u6);
users.stream()
.filter((u)->{return u.getId()%2==0;})
.filter((u)->{return u.getAge()>23;})
.map((u)->{return u.getName().toUpperCase();})
.sorted((o1,o2)->{return o2.compareTo(o1);})
.limit(1)
.forEach(System.out::println);
}
}
ForkJoin
ForkJoin(分支合并)JDK1.7,并行执行任务!提高效率。大数据量
ForkJoin特点:工作窃取
这里面维护的都是双端队列
public class Demo01 extends RecursiveTask<Long> {
private Long start;
private Long end;
//临界值
private Long temp = 10000l;
public Demo01(Long start, Long end) {
this.start = start;
this.end = end;
}
public void test(){
if ((end-start)>temp){
//分支合并计算
}else {
int sum=0;
for (int i = 0; i <=10000 ; i++) {
sum+=i;
}
System.out.println(sum);
}
}
//计算方法
@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 ;
Demo01 demo01 = new Demo01(start,middle);
demo01.fork();
Demo01 demo02 = new Demo01(middle+1,end);
demo02.fork();
return demo01.join()+demo02.join();
}
}
}
public class Test {
public static void main(String[] args) throws ExecutionException, InterruptedException {
//test1();//sum=500000000500000000时间8909
//test2();//sum=500000000500000000时间6840
test3();//sum=500000000500000000时间820
}
public static void test1(){
long start = System.currentTimeMillis();
Long sum=0l;
for (Long i =1l; i <=10_0000_0000 ; i++) {
sum+=i;
}
long end = System.currentTimeMillis();
System.out.println("sum="+sum+"时间"+(end-start));
}
public static void test2() throws ExecutionException, InterruptedException {
long start = System.currentTimeMillis();
ForkJoinPool forkJoinPool = new ForkJoinPool();
ForkJoinTask<Long> submit = forkJoinPool.submit(new Demo01(0l, 10_0000_0000l));
Long aLong = submit.get();
long end = System.currentTimeMillis();
System.out.println("sum="+aLong+"时间"+(end-start));
}
public static void test3(){
long start = System.currentTimeMillis();
//Stream并行流
long reduce = LongStream.rangeClosed(0, 10_0000_0000).parallel().reduce(0, Long::sum);
long end = System.currentTimeMillis();
System.out.println("sum="+reduce+"时间"+(end-start));
}
}
异步回调
Future :对未来的某个事件进行建模
/*
* 异步调用:CompletableFuture
* 异步执行
* 成功回调
* 失败回调
* */
public class Demo01 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
//没有返回值的异步回调runAsync
/*CompletableFuture<Void> completableFuture = CompletableFuture.runAsync(()->{
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"runAsync=>void");
});
System.out.println("1111111111111111111");
completableFuture.get();
*/
CompletableFuture<Integer> completableFuture = CompletableFuture.supplyAsync(() -> {
System.out.println(Thread.currentThread().getName() + "runAsync=>void");
int i =10/0;
return 1024;
});
System.out.println(completableFuture.whenComplete((t, u) -> {
//t正常结果
System.out.println("t=>" + t);
//u错误信息
System.out.println("u=>" + u);
}).exceptionally(
(t) -> {
System.out.println(t.getMessage());
return 233;
}).get());
}
}
JMM
Volatile 是java虚拟机提供的轻量级的同步机制
- 保证可见性
- 不保证原子性
- 禁止指令重排
什么是JMM
JMM:java内存模型,不存在的东西,概念 约定
JMM的一些同步的约定
- 线程解锁前,必须把共享变量,立刻刷回主存
- 线程加锁前,必须读取主存中的最新值到工作内存空间
- 加锁和解锁是同一把锁
内存交互操作有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操作之前,必须把此变量同步回主内存
Volatile
保证可见性
public class JMMDemo01 {
//如果不加volatile程序会陷入死循环
private volatile static int num = 0;
public static void main(String[] args) {
new Thread(()->{
while (num==0){
}
}).start();
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
num = 1;
System.out.println(num);
}
}
不保证原子性
原子性:线程再执行任务的时候,不能被打扰的,也不能分割
public class VDemo02 {
private volatile static int num = 0;
public static void add(){
num++;
}
public static void main(String[] args) {
//结果应该为2w 出来的结果不一定是2w
for (int i = 1; i <=20 ; i++) {
new Thread(() -> {
for (int j = 1; j <= 1000; j++) {
add();
}
}).start();
}
while (Thread.activeCount()>2) {
Thread.yield();
}
System.out.println(Thread.currentThread().getName()+""+num);
}
}
不使用synchronized和Lock解决
使用JUC中的原子类
public class VDemo02 {
//使用JUC中的原子性的Integer
private volatile static AtomicInteger num = new AtomicInteger(0);
public static void add(){
//加一操作,使用底层的CAS
num.getAndIncrement();
}
public static void main(String[] args) {
//结果应该为2w 出来的结果不一定是2w
for (int i = 1; i <=20 ; i++) {
new Thread(() -> {
for (int j = 1; j <= 1000; j++) {
add();
}
}).start();
}
while (Thread.activeCount()>2) {
Thread.yield();
}
System.out.println(Thread.currentThread().getName()+""+num);
}
}
禁止指令重排
什么是指令重排
源代码–>编译器优化重拍–>指令并行也可能重拍–>内存系统也可能重拍–>执行
单例模式
核心思想(构造器私有)
饿汉模式
public class Hungry {
private Hungry(){
}
private final static Hungry HUNGRY = new Hungry();
public static Hungry getInstance(){
return HUNGRY;
}
}
无论使不使用都会加载,比较耗费资源。
懒汉模式
public class LazyMan {
private static boolean dong = false;
private LazyMan() {
synchronized (LazyMan.class){
if (dong == false){
dong = true;
}else {
throw new RuntimeException("不要试图破坏");
}
}
}
//防止指令重排
private volatile static LazyMan lazyMan;
//双重检测锁模式 懒汉式单例 DCL懒汉式
public static LazyMan getInstance() {
if (lazyMan == null) {
synchronized (LazyMan.class) {
if (lazyMan == null) {
lazyMan = new LazyMan();//不是一个原子性操作
/*
* 1. 分配内存空间
* 2. 执行构造方法,初始化对象
* 3. 把这个对象指向这个空间
* */
}
}
}
return lazyMan;
}
普通的懒汉模式,可以利用反射来破坏它的单例模式。
静态内部类实现懒汉式
//静态内部类
public class Holder {
private Holder(){
}
public static Holder getInstance(){
return InnerClass.HOLDER;
}
public static class InnerClass{
private static final Holder HOLDER = new Holder();
}
}
枚举 enum 实现懒汉模式
本身就是一个Class类
//enum枚举
public enum EnumSingle {
INSTANCE;
public EnumSingle getInstance(){
return INSTANCE;
}
}
CAS
CAS
比较当前工作内存中的值和主内存中的值,如果这个值是期望的,那么执行操作,如果不是就循环。
//CAS compareAndSet :比较并交换
public class CASDemo {
public static void main(String[] args) {
AtomicInteger atomicInteger = new AtomicInteger(2020);
//如果期望的值(2020)达到了就更新(2021)
//CAS 是CPU的并发原语
atomicInteger.compareAndSet(2020,2021);
System.out.println(atomicInteger.get());
atomicInteger.compareAndSet(2020,2022);
System.out.println(atomicInteger.get());
}
}
缺点:
- 循环会耗时
- 一次性只能保证一个共享变量的原子性
- ABA问题
ABA
//ABA问题
public class CASDemo {
public static void main(String[] args) {
AtomicInteger atomicInteger = new AtomicInteger(2020);
//====================捣乱的线程=========================
//将2020该为2021,然后又将2021改为2020
atomicInteger.compareAndSet(2020,2021);
System.out.println(atomicInteger.get());
atomicInteger.compareAndSet(2021,2020);
System.out.println(atomicInteger.get());
//===================期望的线程==========================
//这里将2020改为6666,他就不知道之前这个数据被修改过
atomicInteger.compareAndSet(2020,6666);
System.out.println(atomicInteger.get());
}
}
原子引用
阿里巴巴开发手册
【强制】所有的相同类型的包装类对象之间值的比较,全部使用 equals 方法比较。
说明:对于 Integer var = ? 在-128 至 127 范围内的赋值,Integer 对象是在
IntegerCache.cache 产生,会复用已有对象,这个区间内的 Integer 值可以直接使用==进行
判断,但是这个区间之外的所有数据,都会在堆上产生,并不会复用已有对象,这是一个大坑,
推荐使用 equals 方法进行判断。
使用原子引用解决ABA问题(乐观锁原理)
//ABA问题
public class CASDemo {
public static void main(String[] args) {
//AtomicInteger atomicInteger = new AtomicInteger(2020);
//这里因为Inter的原因 数值只能在-128~127之间
AtomicStampedReference<Integer> atomicInteger = new AtomicStampedReference<>(12,1);
new Thread(()->{
int stamp = atomicInteger.getStamp();//获得版本号 1
System.out.println("a1=>"+stamp);
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
//乐观锁原理相同
System.out.println(atomicInteger.compareAndSet(12, 13, atomicInteger.getStamp(), atomicInteger.getStamp() + 1));
System.out.println("a2=>"+atomicInteger.getStamp());
System.out.println(atomicInteger.compareAndSet(13, 12, atomicInteger.getStamp(), atomicInteger.getStamp() + 1));
System.out.println("a3=>"+atomicInteger.getStamp());
},"a").start();
new Thread(()->{
int stamp = atomicInteger.getStamp();//获得版本号 1
System.out.println("b1=>"+stamp);
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(atomicInteger.compareAndSet(12, 120, stamp, stamp + 1));
System.out.println("b2=>"+atomicInteger.getStamp());
},"b").start();
}
}
各种锁
1、公平锁、非公平锁
公平锁:非常公平,不能插队,必须先来后到
非公平锁:非常不公平,可以插队
//默认非公平锁
public ReentrantLock() {
sync = new NonfairSync();
}
//当参数为true时,变为公平锁
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
2、可重入锁
可重入锁(递归锁)
//Synchronized
public class Demo01 {
public static void main(String[] args) {
Phone phone = new Phone();
new Thread(()->{phone.sms();},"a").start();
new Thread(()->{phone.sms();},"b").start();
}
}
class Phone{
public synchronized void sms(){
System.out.println(Thread.currentThread().getName()+"sms");
//这里也有锁
call();
}
public synchronized void call(){
System.out.println(Thread.currentThread().getName()+"call");
}
}
//Lock
public class Demo02 {
public static void main(String[] args) {
Phone2 phone = new Phone2();
new Thread(() -> {
phone.sms();
}, "a").start();
new Thread(() -> {
phone.sms();
}, "b").start();
}
}
class Phone2 {
Lock lock = new ReentrantLock();
public void sms() {
lock.lock();
try {
System.out.println(Thread.currentThread().getName() + "sms");
//这里也有锁
call();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
3、自旋锁
//自旋锁
public class SpinlockDemo {
AtomicReference<Thread> atomicReference = new AtomicReference<>();
//加锁
public void myLock(){
Thread thread = Thread.currentThread();
System.out.println(thread.getName()+"-->myLock");
//当atomicReference中的线程为空时,才会停止循环
while (!atomicReference.compareAndSet(null,thread)){
}
}
//解锁
public void myUnLock(){
Thread thread = Thread.currentThread();
System.out.println(thread.getName()+"-->myUnLock");
//释放锁将atomicReference中的线程设置为null;方便下一个解锁
atomicReference.compareAndSet(thread,null);
}
}
测试(必须a解锁后b才能解锁)
public class TestSpinLock {
public static void main(String[] args) throws InterruptedException {
SpinlockDemo spinlockDemo = new SpinlockDemo();
new Thread(()->{
spinlockDemo.myLock();
try {
TimeUnit.SECONDS.sleep(3);
}catch (Exception e){
e.printStackTrace();
}finally {
spinlockDemo.myUnLock();
}
},"a").start();
TimeUnit.SECONDS.sleep(1);
new Thread(()->{
spinlockDemo.myLock();
try {
TimeUnit.SECONDS.sleep(1);
}catch (Exception e){
e.printStackTrace();
}finally {
spinlockDemo.myUnLock();
}
},"b").start();
}
}
4、死锁
public class DeadLockDemo {
public static void main(String[] args) {
String LockA = "LockA";
String LockB = "LockB";
new Thread(new MyThread(LockA,LockB)).start();
new Thread(new MyThread(LockB,LockA)).start();
}
}
class MyThread implements Runnable{
private String LockA;
private String LockB;
public MyThread(String lockA, String lockB) {
LockA = lockA;
LockB = lockB;
}
@Override
public void run() {
synchronized (LockA){
System.out.println(Thread.currentThread().getName()+"有"+LockA+"还想要"+LockB);
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (LockB){
System.out.println(Thread.currentThread().getName()+"有"+LockB+"还想要"+LockA);
}
}
}
}
怎样排除死锁
-
使用 jps-l 定位进程号
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-G8lC08vB-1594274266075)(C:\Users\42084\AppData\Roaming\Typora\typora-user-images\image-20200709134238528.png)]
-
使用 jstack 进程号找到死锁问题
S.sleep(3);
}catch (Exception e){
e.printStackTrace();
}finally {
spinlockDemo.myUnLock();
}
},“a”).start();
TimeUnit.SECONDS.sleep(1);
new Thread(()->{
spinlockDemo.myLock();
try {
TimeUnit.SECONDS.sleep(1);
}catch (Exception e){
e.printStackTrace();
}finally {
spinlockDemo.myUnLock();
}
},“b”).start();
}
}
#### 4、死锁
```java
public class DeadLockDemo {
public static void main(String[] args) {
String LockA = "LockA";
String LockB = "LockB";
new Thread(new MyThread(LockA,LockB)).start();
new Thread(new MyThread(LockB,LockA)).start();
}
}
class MyThread implements Runnable{
private String LockA;
private String LockB;
public MyThread(String lockA, String lockB) {
LockA = lockA;
LockB = lockB;
}
@Override
public void run() {
synchronized (LockA){
System.out.println(Thread.currentThread().getName()+"有"+LockA+"还想要"+LockB);
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (LockB){
System.out.println(Thread.currentThread().getName()+"有"+LockB+"还想要"+LockA);
}
}
}
}
怎样排除死锁
- 使用 jps-l 定位进程号
- 使用 jstack 进程号找到死锁问题