准备工作:导入maven依赖包
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<version>1.18.8version>
dependency>
第二步,将project里的modules的language level改为jdk8
第三步,将javaCompile改为jdk8
JUC简称:Java util.concurrent 并发
java.util.concurrent.atomic 原子性
java.util.concurrent.locks
业务:普通的线程代码 Thread
Runnable:没有返回值,效率相比于Callable相对较低
一个进程可以有多个线程,且至少包含一个。
java默认线程:main ,gc线程,三种开启线程方式:Thread,Runnable,Callable
java是不能真正的开启多线程,源码中是调用了本地native方法,底层C++,java是作用在虚拟机上的,无法直接操作硬件
并发:多线程操作同一个资源,cpu单核模拟多个线程
并行:多个人一起行走,cpu多核,多个线程可以同时执行,线程池
//获取cpu的核数
//cpu密集型,IO密集型
Runtime.getRuntime().availableProcessors();
并发的本质:充分利用cpu的资源
线程状态:
public enum State {
//线程新生
NEW,
//运行
RUNNABLE,
//阻塞
BLOCKED,
//等待,死等
WAITING,
//超时等待
TIMED_WAITING,
//终止
TERMINATED;
}
wait/sleep区别:
1.来自不同的类,wait来自obj类,sleep来自Thread类
2.wait会释放锁,sleep不会释放锁
3.使用范围不同:wait必须在同步代码块中,sleep无限制
Synchronized同步锁,关键字,本质:排队,锁
并发:多线程操作同一个资源类,把资源类丢进线程里,Runnable函数式接口可以new被成为匿名内部类,但繁琐,所以用lambda表达式来代替()->{}
new Thread(()->{},"name").start;
Interface Lock:
实现类:ReentrantLock可重入锁,ReentrantReadWriteLock.ReadLock读锁,eentrantReadWriteLock.WriteLock写锁
公平锁:十分公平,先来后到
非公平锁:十分不公平,可以插队(默认)
Lock:手动加锁,手动释放锁
Synchronized和Lock区别
锁是什么,如何判断锁的是谁!
步骤:判断等待,业务,通知
面试:单例模式,排序算法,生产者消费者,死锁
线程也可以唤醒,而不会通知,中断或者超时即所谓的虚假唤醒,等待应该总是出现在while循环中
Lock替代synchronized方法和语句的使用,Condition取代了对象
Condition方法:
condition.await();
condition.signalAll();
package com.liang.lesson1;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/*
生产者和消费者问题
用JUC lock()实现
*/
public class PC1 {
public static void main(String[] args) {
Product1 product1 = new Product1();
new Thread(()->{
for (int i = 0; i < 10; i++) {
product1.add();
}
},"生产者A").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
product1.delete();
}
},"消费者A").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
product1.add();
}
},"生产者B").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
product1.delete();
}
},"消费者B").start();
}
}
// 判断等待,业务,通知
class Product1{
private int number = 0;
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
public void add(){
try {
lock.lock();
while (number!=0){
condition.await();
}
number++;
System.out.println(Thread.currentThread().getName()+"=>"+number);
condition.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void delete(){
try {
lock.lock();
while (number==0){
condition.await();
}
number--;
System.out.println(Thread.currentThread().getName()+"=>"+number);
condition.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
Condition精准通知和唤醒线程
package com.liang.lesson1;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
@SuppressWarnings("all")
/*
Condition实现精准通知唤醒
A执行完调用B,B执行完调用C,C执行完调用A.实现依次执行.
*/
public class PC2 {
public static void main(String[] args) {
Data data = new Data();
new Thread(()->{
for (int i = 0; i < 10; i++) {
data.printA();
}
},"A").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
data.printB();
}
},"B").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
data.printC();
}
},"C").start();
}
}
class Data {
private Lock lock = new ReentrantLock();
Condition condition1 = lock.newCondition();
Condition condition2 = lock.newCondition();
Condition condition3 = lock.newCondition();
private int num = 1;
public void printA() {
lock.lock();
try {
if (num != 1) {
condition1.await();
}
num = 2;
System.out.println(Thread.currentThread().getName()+"=>AAAAAAAAAAAAAAAAAA");
condition2.signal();
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
public void printB(){
lock.lock();
try {
if (num != 2) {
condition2.await();
}
num = 3;
System.out.println(Thread.currentThread().getName()+"=>BBBBBBBBBBBBBBB");
condition3.signal();
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
public void printC(){
lock.lock();
try {
if (num != 3) {
condition3.await();
}
num = 1;
System.out.println(Thread.currentThread().getName()+"=>CCCCCCCCCCCCCCCC");
condition1.signal();
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
}
zpackage com.liang.lesson1;
import java.util.concurrent.TimeUnit;
@SuppressWarnings("all")
/*
八锁现象
1.标准情况下,两个线程操作同一对象的1.标准情况下sychronized方法,谁先调用这个方法,谁就先拿到调用这个方法的对象的这把锁
2.当两个线程同时操作同一个对象时,没加同步锁的方法不受锁的影响,按照代码的实际情况输出
3.当两个线程同时操作两个不同的对象时,这两个线程互不受影响,按照代码实际情况输出
4.当sychronize修饰的时静态static方法时,锁的对象是class对象,class对象有且只有一个,即使是两个线程操作不同的对象,谁先调用sychronized方法,谁就先获得这把锁
5.一个静态同步方法,一个普通同步方法,一个对象,两个线程分别调用不同的方法,这时两个线程锁的是不同的对象,互不影响
6.同理两个对象也是一样的
总结:当调用sychronized所修饰的方法时,调用这个方法的对象就先属于谁(即谁拿到这个对象的锁),所以分析两个线程先后顺序时,要看这两个线程
是否是同一把锁,如果是的话,谁先获得锁,谁就先执行。如果是不同的锁,就是按照代码实际情况处理
*/
public class eightLock {
public static void main(String[] args) throws InterruptedException {
Phone phone = new Phone();
new Thread(()->{
for (int i = 0; i < 10; i++) {
phone.call();
}
},"A").start();
TimeUnit.SECONDS.sleep(1);
new Thread(()->{
for (int i = 0; i < 10; i++) {
phone.sendMessage();
}
},"B").start();
}
}
class Phone{
public synchronized void call(){
System.out.println("打电话");
}
public synchronized void sendMessage(){
System.out.println("发短信");
}
}
并发下的ArrayList不安全,会报错:java.util.ConcurrentModificationException,解决方法:
package com.liang.lesson2;
import com.sun.xml.internal.ws.policy.privateutil.PolicyUtils;
import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
public class Demo01 {
public static void main(String[] args) {
// 并发下,ArrayList是不安全的.
// 解决方法:
//1.Vector vector = new Vector<>();
//2. List list = Collections.synchronizedList(new ArrayList<>());
//3.CopyOnWriteArrayList list = new CopyOnWriteArrayList<>();
ArrayList<String> list = new ArrayList<>();
for (int i = 0; i < 30; i++) {
new Thread(()->{
list.add(UUID.randomUUID().toString().substring(0,5));
System.out.println(list);
},String.valueOf(i)).start();
}
}
}
Set不安全
HashSet底层实现原理
public HashSet() {
map = new HashMap<>();
}
// add方法
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
// Dummy value to associate with an Object in the backing Map
private static final Object PRESENT = new Object();
HashMap不安全
HashSet底层实现原理
//initialCapacity容量用户自定义,初始容量默认为16,DEFAULT_LOAD_FACTOR默认负载因子为0.75f
public HashMap(int initialCapacity) {
this(initialCapacity, DEFAULT_LOAD_FACTOR);
}
// put
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
代码测试
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
/*
测试callable开启多线程
*/
public class TestDemo01 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
MyCallable myCallable = new MyCallable();
// callable线程适配器,负责将callable和thread结合起来
FutureTask futureTask = new FutureTask<String>(myCallable);
new Thread(futureTask,"a").start();
// callable会有返回值
Object o = futureTask.get();
System.out.println(o);
}
}
// 自定义类去实现Callable接口
class MyCallable implements Callable<String>{
@Override
public String call() throws Exception {
return "我是一个线程";
}
}
package com.liang.lesson4;
import java.util.concurrent.CountDownLatch;
/*
JUC常用辅组类之CountDownLatch(减法计数器)
*/
public class TestCountDown {
public static void main(String[] args) throws InterruptedException {
CountDownLatch countDownLatch = new CountDownLatch(6);
for (int i = 0; i < 10; i++) {
new Thread(()->{
countDownLatch.countDown();//-1
System.out.println("线程"+Thread.currentThread().getName()+"出去了");
},String.valueOf(i)).start();
}
countDownLatch.await();// 计算器CountDownLatch等待,当为0时才会执行后面的代码
System.out.println("线程全部都执行完了");
}
}
package com.liang.lesson4;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
/*
UC常用辅组类之CyclicBarrier(加法计数器)
*/
public class TestCyclicBarrier {
public static void main(String[] args) {
CyclicBarrier cyclicBarrier = new CyclicBarrier(17,()->{
System.out.println("降龙十八掌已经学会");
});
for (int i = 0; i < 18; i++) {
int temp = i;
new Thread(()->{
try {
System.out.println(Thread.currentThread().getName()+"已经学会了第"+temp+"掌");
cyclicBarrier.await();// 等待线程执行完毕
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
},String.valueOf(i)).start();
}
}
}
package com.liang.lesson4;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
/*
JUC常用辅组类之Semaphore(一个计数信号量)
*/
public class TestSemaphore {
public static void main(String[] args) {
Semaphore semaphore = new Semaphore(3);// 线程最大数量
for (int i = 1; i <= 10; i++) {
int temp = i;
new Thread(()->{
try {
semaphore.acquire();// 获取
System.out.println(Thread.currentThread().getName()+"抢到了第"+temp+"个车位");
TimeUnit.SECONDS.sleep(1);
System.out.println(Thread.currentThread().getName()+"离开了车位");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
semaphore.release();//释放
}
},String.valueOf(i)).start();
}
}
}
原理:
Semaphore.acquire(),获得,如果已经,满了,等待,等待被释放为止
Semaphore.release(),释放,会将当前的信号量释放
JUC类ReadWriteLock
package com.liang.lesson5;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/*
读写锁测试
*/
public class TestReadWriteLock {
public static void main(String[] args) {
MyCache myCache = new MyCache();
for (int i = 0; i < 10; i++) {
int temp = i;
new Thread(()->{
myCache.put(temp+"",temp+"");
},String.valueOf(i)).start();
}
for (int i = 0; i < 10; i++) {
int temp = i;
new Thread(()->{
myCache.get(temp+"");
},String.valueOf(i)).start();
}
}
}
class MyCache{
private volatile Map<String,Object> map=new HashMap<>();
private volatile ReadWriteLock lock = new ReentrantReadWriteLock();
// 存,写
public void put(String key,Object value){
try {
lock.writeLock().lock();
System.out.println(Thread.currentThread().getName()+"写入"+key);
map.put(key,value);
System.out.println(Thread.currentThread().getName()+"写入ok");
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.writeLock().unlock();
}
}
// 取,读
public void get(String key){
try {
lock.readLock().lock();
System.out.println(Thread.currentThread().getName()+"读取"+key);
Object o = map.get(key);
System.out.println(Thread.currentThread().getName()+"读取ok");
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.readLock().unlock();
}
}
}
BlockingQueue是一个接口,继承于Queue.常见的实现类有ArrayBlockingQueue、LinkedBlockingQueue和SynchronousQueue等
什么情况下我们会使用阻塞队列:多线程并发处理,线程池!
学会使用队列
四组API
方式 | 抛出异常 | 不会抛出异常 | 阻塞 等待 | 超时等待 |
---|---|---|---|---|
添加 | add() | offer() | put() | offer() |
移除 | remove() | poll() | take() | poll |
判断队列首部 | element() | peek | — | — |
package com.liang.lesson5;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.TimeUnit;
/*
测试BlockingQueue
BlockingQueue是一个接口继承于Queue接口,常用的实现接口类有ArrayBlockingQueue和LinkedBlockingQueue
四种API讲解
*/
public class TestBlockingQueue {
public static void main(String[] args) throws InterruptedException {
//test1();
//test2();
//test3();
test4();
}
// 第一种API add()添加元素和remove()移除元素,这一对都会抛出异常
public static void test1(){
ArrayBlockingQueue arrayBlockingQueue = new ArrayBlockingQueue<>(3);
System.out.println(arrayBlockingQueue.add("a"));
System.out.println(arrayBlockingQueue.add("b"));
System.out.println(arrayBlockingQueue.add("c"));
System.out.println(arrayBlockingQueue.remove());
System.out.println(arrayBlockingQueue.remove());
System.out.println(arrayBlockingQueue.remove());
System.out.println(arrayBlockingQueue.remove());
}
// 第二种API offer()添加元素和poll()弹出元素
public static void test2(){
ArrayBlockingQueue arrayBlockingQueue2 = new ArrayBlockingQueue<>(3);
System.out.println(arrayBlockingQueue2.offer("a"));
System.out.println(arrayBlockingQueue2.offer("b"));
System.out.println(arrayBlockingQueue2.offer("c"));
System.out.println(arrayBlockingQueue2.offer("d"));
System.out.println(arrayBlockingQueue2.poll());
System.out.println(arrayBlockingQueue2.poll());
System.out.println(arrayBlockingQueue2.poll());
System.out.println(arrayBlockingQueue2.poll());
}
// 第三种API 阻塞等待 put()放元素和take()取元素,当放元素位置满了和取元素为空时时会照成阻塞
public static void test3() throws InterruptedException {
ArrayBlockingQueue arrayBlockingQueue = new ArrayBlockingQueue<>(3);
arrayBlockingQueue.put("a");
arrayBlockingQueue.put("b");
arrayBlockingQueue.put("c");
//arrayBlockingQueue.put("d");
System.out.println(arrayBlockingQueue.take());
System.out.println(arrayBlockingQueue.take());
System.out.println(arrayBlockingQueue.take());
System.out.println(arrayBlockingQueue.take());// 一直超时等待
}
// 第四种API 超时等待
public static void test4() throws InterruptedException {
ArrayBlockingQueue arrayBlockingQueue = new ArrayBlockingQueue<>(3);
arrayBlockingQueue.offer("a");
arrayBlockingQueue.offer("b");
arrayBlockingQueue.offer("c");
arrayBlockingQueue.offer("d",2, TimeUnit.SECONDS);// 超时等待
arrayBlockingQueue.poll();
arrayBlockingQueue.poll();
arrayBlockingQueue.poll();
}
}
SynchronousQueue同步队列
package com.liang.lesson5;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.TimeUnit;
/*
同步队列
*/
public class TestSynchronousQueue {
public static void main(String[] args) {
SynchronousQueue<Object> synchronousQueue = new SynchronousQueue<>();
new Thread(()->{
try {
System.out.println(Thread.currentThread().getName()+"put 1");
synchronousQueue.put("1");
System.out.println(Thread.currentThread().getName()+"put 2");
synchronousQueue.put("2");
System.out.println(Thread.currentThread().getName()+"put 3");
synchronousQueue.put("3");
} catch (InterruptedException e) {
e.printStackTrace();
}
},"T1").start();
new Thread(()->{
try {
TimeUnit.SECONDS.sleep(1);
System.out.println(Thread.currentThread().getName()+"=>"+synchronousQueue.take());
TimeUnit.SECONDS.sleep(1);
System.out.println(Thread.currentThread().getName()+"=>"+synchronousQueue.take());
TimeUnit.SECONDS.sleep(1);
System.out.println(Thread.currentThread().getName()+"=>"+synchronousQueue.take());
} catch (InterruptedException e) {
e.printStackTrace();
}
},"T2").start();
}
}
没有容量
进去一个元素,必须等待取出来之后,才能再往里面放一个元素!
put、take
线程池:三大方法、7大参数、4种拒绝策略
池化技术
程序的运行,本质:占用系统的资源!优化资源的使用!=>池化技术
线程池、连接池、内存池、对象池
池化技术:事先准备好一些资源,有人要用,就来我这里拿,用完之后再还回来
线程池的好处
线程复用,可以控制最大并发数、管理线程
线程池:三大方法
package com.liang.lesson6;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/*
线程池三大方法
*/
public class Demo01 {
public static void main(String[] args) {
// 创建单个线程的线程池
// ExecutorService service1 = Executors.newSingleThreadExecutor();
// 创建一个固定大小线程的线程池
//ExecutorService service2 = Executors.newFixedThreadPool(50);
// 创建一个自动伸缩的线程池
ExecutorService service3 = Executors.newCachedThreadPool();
try {
for (int i = 1; i < 10; i++) {
service3.execute(()->{
System.out.println(Thread.currentThread().getName()+"=>OK");
});
}
} catch (Exception e) {
e.printStackTrace();
} finally {
service3.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,
60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>());}
//以上三种创建线程池的方法均是new ThreadPoolExecutor(),所以用户在自定义线程池也是通过new ThreadPoolExecutor()
public ThreadPoolExecutor(int corePoolSize,// 核心线程数量
int maximumPoolSize,// 最大线程数量
long keepAliveTime,// 超时了没人调用就会释放
TimeUnit unit,// 超时单位
BlockingQueue<Runnable> workQueue,//阻塞队列
ThreadFactory threadFactory,// 线程工厂
RejectedExecutionHandler handler)// 拒绝策略
自定义线程池
package com.liang.lesson6;
import java.util.concurrent.*;
/*
线程池七大参数和自定义线程池
*/
public class Demo02 {
public static void main(String[] args) {
ThreadPoolExecutor executor = new ThreadPoolExecutor(
2,
5,
3,
TimeUnit.SECONDS,
new LinkedBlockingDeque<>(3),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy());// 默认拒绝策略,当大于最大线程量与队列数量时,不处理,抛出异常
try{
for (int i = 1; i < 10; i++) {
executor.execute(()->{
System.out.println(Thread.currentThread().getName()+"=>OK");
});// 10大于8,抛出异常
}
} catch (Exception e) {
e.printStackTrace();
} finally {
executor.shutdown();
}
}
}
四种拒绝策略
AbortPolicy()//当线程池大于最大并发数,抛出异常
CallerRunsPolicy()// 哪来的回哪去,最常见的时main线程去执行
DiscardPolicy()// 队列满了,但是不会抛出异常
DiscardOldestPolicy()//队列满了,尝试和最早的竞争队列资源
小结和拓展
了解:IO密集型,CPU密集型
// 最大线程到底该如何定义
// 1.CPU密集型,几核,就是几,可以保持CPU的效率最高
// 2.IO密集型>判断你程序中十分耗IO的线程
// 程序 15个大型任务 io十分占用资源
新时代的程序员:lambda表达式、链式编程、函数式接口、Stream流式接口
四大接口
package com.liang.lesson7;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
/*
函数型接口
有一个输入,并有一个输出
*/
public class Demo1 {
public static void main(String[] args) {
//test1();
//test2();
//test3();
test4();
}
/*
函数型接口,一个输入,一个输出,并且输入和输出为同类型
*/
public static void test1(){
// Function function = new Function() {
// @Override
// public String apply(String o) {
// return o;
// }
// };
Function function = (str)->{return str;};
System.out.println(function.apply("fabe"));
}
/*
断定型接口,有输入,输出为boolean类型
*/
public static void test2(){
// Predicate predicate = new Predicate() {
// @Override
// public boolean test(String o) {
// return o.isEmpty();
// }
// };
Predicate<String> predicate = (str)->{
return str.isEmpty();
};
System.out.println(predicate.test("你好啊"));
}
/*
消费者接口
只负责接收,没有返回值
*/
public static void test3(){
Consumer<String> consumer = new Consumer<String>() {
@Override
public void accept(String o) {
System.out.println(o);
}
};
consumer.accept("你真是傻逼");
}
/*
供给型接口
没有参数值,只有返回值
*/
public static void test4(){
Supplier supplier = new Supplier<Integer>() {
@Override
public Integer get() {
System.out.println("get()");
return 1024;
}
};
System.out.println(supplier.get());
}
}
什么是Stream流式计算
大数据:存储+计算
集合、mysql本质就是存储东西的
计算都应该交给流来操作
package com.liang.lesson7;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/*
Stream流式计算
题目要求:一分钟内完成此题,只能用一行代码实现
有6个用户,筛选
1.id必须是整数
2.年龄必须大于23岁
3.用户名转化为大写
4.用户名字倒着排序
5.只输出一个用户
*/
public class Demo2 {
public static void main(String[] args) {
User user1 = new User(1,"a",21);
User user2 = new User(2,"b",22);
User user3 = new User(3,"c",23);
User user4 = new User(4,"d",24);
User user5 = new User(5,"e",25);
User user6 = new User(6,"f",26);
//集合就是存储
List<User> list = Arrays.asList(user1, user2, user3, user4, user5, user6);
// 计算交给Stream
list.stream().filter(u->{return u.getId()%2==0;}).filter(user ->{return user.getAge()>23;}).map(user -> user.getName().toUpperCase()).sorted((uu1,uu2)->{return uu2.compareTo(uu1);}).forEach(System.out::println);
}
}
class User{
private int id;
private String name;
private int age;
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
'}';
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public User(int id, String name, int age) {
this.id = id;
this.name = name;
this.age = age;
}
}
什么是ForkJoin
ForkJoin在JDK1.7,并行执行认为!提高效率,大数据量
大数据:Map Raduce(把大任务拆分为小任务)
package com.liang.lesson7;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
import java.util.concurrent.RecursiveTask;
import java.util.stream.LongStream;
@SuppressWarnings("all")
public class TestForkJoin {
public static void main(String[] args) throws ExecutionException, InterruptedException {
//test1();
//test2();
test3();
}
// 普通程序员
public static void test1(){
long sum = 0L;
long start = System.currentTimeMillis();
for(long i = 1L; i <= 10_0000_0000L; i++) {
sum +=i;
}
long end = System.currentTimeMillis();
System.out.println("sum="+sum+"时间:"+(end-start));
}
// 会使用ForkJoin
public static void test2() throws ExecutionException, InterruptedException {
long start = System.currentTimeMillis();
ForkJoinPool pool = new ForkJoinPool();
MyForkJoin myForkJoin = new MyForkJoin(1L, 10_0000_0000);
ForkJoinTask<Long> submit = pool.submit(myForkJoin);
long sum = submit.get();
long end = System.currentTimeMillis();
//
System.out.println("sum="+sum+"时间:"+(end-start));
}
// Stream并行流计算
public static void test3(){
long start = System.currentTimeMillis();
long sum = LongStream.rangeClosed(0L, 10_0000_0000).parallel().reduce(0, Long::sum);
long end = System.currentTimeMillis();
System.out.println("sum="+sum+"时间:"+(end-start));
}
}
class MyForkJoin extends RecursiveTask<Long> {
private long start;
private long end;
private long temp = 1000L;
public MyForkJoin(long start, long end) {
this.start = start;
this.end = end;
}
@Override
protected Long compute() {
// 一个任务拆分为两个任务,当数量大于temp时,通过forkjoin拆分为两个任务
if ((end-start)>temp){
long mid = (start+end)/2;
MyForkJoin forkJoin1 = new MyForkJoin(start, mid);
forkJoin1.fork();
MyForkJoin forkJoin2 = new MyForkJoin(mid, end);
forkJoin2.fork();
return forkJoin1.join()+forkJoin2.join();
}else {
long sum = 0;
for(long i = start; i < end;i++) {
sum +=i;
}
return sum;
}
}
}
Future设计的初衷:对将来的某个事件的结果进行建模
package com.liang.lesson7;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
/*
异步调用:Ajax
异步执行
成功回调
失败回调
*/
public class Demo3 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
// // 没有返回值的runAsync回调
// CompletableFuture future = CompletableFuture.runAsync(()->{
// try {
// TimeUnit.SECONDS.sleep(2);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
// System.out.println(Thread.currentThread().getName()+"runAsync=>Void");
// });
// System.out.println("11111");
// future.get();
// 有返回值的supplyAsync的异步回调
// Ajax,成功和失败的回调
// 返回的是错误信息
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(()->{
System.out.println(Thread.currentThread().getName()+"supplyAsync=>Integer");
return 1024;
});
future.whenComplete((t,u)->{
System.out.println("t=>"+t);// 正常的返回结果
System.out.println("u=>"+u);// 错误信息
}).exceptionally((e)->{
System.out.println(e.getMessage());
return 233;// 可以获取到错误的返回结果
});
}
}
请你谈谈你对Volatile的理解
Volatile是Java虚拟机提供轻量级的同步机制
什么是JMM
主内存与工作内存直接的具体交互协议,即一个变量如何从主内存拷贝到工作内存,如何从工作内存同步到主内存之间的实现细节,Java 内存模型定义了以下八种同步操作:
保证可见性
package com.liang.lesson8;
import java.util.concurrent.TimeUnit;
/*
测试Volatile关键字,可见性
*/
public class Demo1 {
// 加volatile关键字保证可见性
public static volatile int num = 0;
public static void main(String[] args) throws InterruptedException {
new Thread(()->{
while (num==0){
}
},"a").start();
TimeUnit.SECONDS.sleep(1);
num +=1;
System.out.println(num);
}
}
不保证原子性
原子性:不可分割、
线程A在执行任务的时候,不能被打扰的,也不能被分割,要么同时成功,要么同时失败
package com.liang.lesson8;
import java.util.concurrent.atomic.AtomicInteger;
/*
测试Volatile关键字,原子性
*/
public class Demo2 {
// Volatile不能保证原子性
//public static int num = 0;
// 在不使用lock()和synchronized前提下的解决方法:通过原子类来解决实现原子性问题
// 而原子类底层实现原理是通过java中的unsafe类来调用native接口实现原子性
public static AtomicInteger num = new AtomicInteger();
public static void add(){
num.getAndIncrement();
}
public static void main(String[] args) {
for(int i = 1; i <= 20 ; i++) {
new Thread(()->{
for (int j = 1; j <= 100 ; j++) {
add();
}
},String.valueOf(i)).start();
}
while (Thread.activeCount()>2){
Thread.yield();
}
System.out.println(num);
}
}
静止指令重排
Volatile如何保证有序性(禁止指令重排)
1.首先我们要了解计算机在执行程序时,为了提高性能,编译器和处理器一般都会对指令做重排,一般分为以下三种:
单线程环境里面确保程序最终执行的结果和代码顺序执行的结果一致。
处理器在进行指令重排时必须要考虑指令之间的数据依赖性。
多线程环境中线程交替执行,由于编译器优化重排的存在,两个或多个线程中使用的变量能否保证一致是不能确定的,最后执行的结果也是无法预测的。
2.volatile实现禁止指令重排优化,从而避免在多线程环境下程序出现乱排序执行的现象。
(1).首先要了解一个概念,就是内存屏障,也称为内存栅栏,他是一个cpu的指令。作用有两个:
一是保证特定操作的执行顺序;
二是保证某些变量的内存可见性(volatile的内存可见性是利用该特性实现的)
3.由于编译器和处理器都能执行指令重排优化,如果在指令之间插入一条内存屏障则会告诉编译器和cup不管在任何情况下,无论任何指令都不能和这条内存屏障进行指令重排,也就是说通过插入内存屏障禁止在内存屏障前后的指令执行重排序优化。内存屏障的另外一个作用就是强制刷出各种CPU的缓存数据,因此在任何CPU上的线程都能读取到这些数据的最新值。
单例模式:只创建一个实例对象
饿汉式 DCL懒汉式,深究
饿汉式类一加载进来就创建,浪费内存资源
package com.liang.lesson8;
/*
单例模式之饿汉式
*/
public class Demo3 {
private Demo3() {
}
private final static Demo3 bao = new Demo3();
public static Demo3 getInstance(){
return bao;
}
}
单例模式之懒汉式
通过反射来破坏单例模式
=======================================================================
package com.liang.lesson8;
/*
单例模式之懒汉式
*/
public class Demo4 {
private Demo4() {
System.out.println(Thread.currentThread().getName()+"=>OK");
}
private static volatile Demo4 er;
// 双重检测锁模式的懒汉式单例 DCL懒汉式
public static Demo4 getInstance(){
if (er==null){
synchronized(Demo4.class){
if (er==null){
er = new Demo4();
}
}
}
return er;
}
// 多线程并发
public static void main(String[] args) {
for (int i = 1; i <= 10; i++) {
new Thread(()->{
Demo4.getInstance();
},String.valueOf(i)).start();
}
}
}
=======================================================================
// 通过反射来破坏单例模式
package com.liang.lesson8;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
/*
饿汉式:单例模式之利用反射破坏单例
*/
public class Demo5 {
private static boolean flag = false;
private Demo5() {
if (flag==false){
flag = true;
}else {
throw new RuntimeException("不要试图使用反射破坏单例");
}
}
private static volatile Demo5 er;
// 双重检测锁模式的懒汉式单例 DCL懒汉式
public static Demo5 getInstance(){
if (er==null){
synchronized(Demo4.class){
if (er==null){
er = new Demo5();
}
}
}
return er;
}
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
//Demo5 instance = Demo5.getInstance();
Constructor<Demo5> constructors = Demo5.class.getDeclaredConstructor();
constructors.setAccessible(true);
Demo5 instance = constructors.newInstance();
Demo5 instance1 = constructors.newInstance();
System.out.println(instance);
System.out.println(instance1);
}
}
枚举类的最终反编译源码,是带有两个参数的有参构造器
什么是CAS
CAS:compareAndSet
package com.liang.lesson8;
import java.util.concurrent.atomic.AtomicInteger;
/*
CAS:compareAndSet 比较并更新值
*/
public class Demo6 {
public static void main(String[] args) {
// 设置原子类初始值
AtomicInteger atomicInteger = new AtomicInteger(2020);
// 期望,更新,如果是就返回true并更新值.如果不是则返回false
System.out.println(atomicInteger.compareAndSet(2020, 2021));
System.out.println(atomicInteger.get());
System.out.println(atomicInteger.compareAndSet(2020, 2022));
System.out.println(atomicInteger.get());
}
}
缺点:
CAS:ABA问题(狸猫换太子)
解决ABA问题的方法:原子引用
带版本号的原子操作
使用AtomicStampedReference类
解决ABA问题
package com.liang.lesson8;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicStampedReference;
/*
使用原子引用类AtomicStampedReference,解决CAS中的ABA问题
通过增加附属条件版本号来确定是否被修改过
*/
public class Demo7 {
// 注意:Integer包装类范围是-127到127.
public static AtomicStampedReference<Integer> atomic = new AtomicStampedReference<>(2,1);
public static void main(String[] args) {
new Thread(()->{
System.out.println("A1=>"+atomic.getStamp());
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(atomic.compareAndSet(2, 5, atomic.getStamp(), atomic.getStamp() + 1));
System.out.println("A2=>"+atomic.getStamp());
System.out.println(atomic.compareAndSet(5, 2, atomic.getStamp(), atomic.getStamp() + 1));
System.out.println("A3=>"+atomic.getStamp());
},"A").start();
new Thread(()->{
int stamp = atomic.getStamp();
System.out.println("B1=>"+stamp);
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(atomic.compareAndSet(2, 6, stamp, stamp+1));
System.out.println("B2=>"+atomic.getStamp());
},"B").start();
}
}
公平锁和非公平锁
公平锁:非常公平,不能插队,必须先来后到!
非公平锁:非常不公平,可以插队(默认是非公平的)
public ReentrantLock() {
sync = new NonfairSync();
}
/**
* Creates an instance of {@code ReentrantLock} with the
* given fairness policy.
*
* @param fair {@code true} if this lock should use a fair ordering policy
*/
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
可重入锁
Synchronized和Lock都是可重入锁
synchronized和ReentrantLock都称为可重入锁,也称递归锁. 指的同一个线程在外层方法获得锁时,进入内层方法会自动获取锁。也就是说,线程可以进入任何一个它已经拥有锁的代码块。比如get方法里面有set方法,两个方法都有同一把锁,得到了get的锁,就自动得到了set的锁。就像有了家门的锁,厕所、书房、厨房就为你敞开了一样。可重入锁可以避免死锁的问题。
自旋锁
自旋锁(spinlock):是指当一个线程在获取锁的时候,如果锁已经被其它线程获取,那么该线程将循环等待,然后不断的判断锁是否能够被成功获取,直到获取到锁才会退出循环。
获取锁的线程一直处于活跃状态,但是并没有执行任何有效的任务,使用这种锁会造成busy-waiting
它是为实现保护共享资源而提出一种锁机制。其实,自旋锁与互斥锁比较类似,它们都是为了解决对某项资源的互斥使用。无论是互斥锁,还是自旋锁,在任何时刻,最多只能有一个保持者,也就说,在任何时刻最多只能有一个执行单元获得锁。但是两者在调度机制上略有不同。对于互斥锁,如果资源已经被占用,资源申请者只能进入睡眠状态。但是自旋锁不会引起调用者睡眠,如果自旋锁已经被别的执行单元保持,调用者就一直循环在那里看是否该自旋锁的保持者已经释放了锁,”自旋”一词就是因此而得名。
Java如何获得自旋锁
下面是个简单的例子:
public class SpinLock {
private AtomicReference<Thread> cas = new AtomicReference<Thread>();
public void lock() {
Thread current = Thread.currentThread();
// 利用CAS
while (!cas.compareAndSet(null, current)) {
// DO nothing
}
}
public void unlock() {
Thread current = Thread.currentThread();
cas.compareAndSet(current, null);
}
}
1234567891011121314
lock()方法利用的CAS,当第一个线程A获取锁的时候,能够成功获取到,不会进入while循环,如果此时线程A没有释放锁,另一个线程B又来获取锁,此时由于不满足CAS,所以就会进入while循环,不断判断是否满足CAS,直到A线程调用unlock方法释放了该锁。
自旋锁存在的问题
自旋锁的优点
自旋锁不会使线程状态发生切换,一直处于用户态,即线程一直都是active的;不会使线程进入阻塞状态,减少了不必要的上下文切换,执行速度快
非自旋锁在获取不到锁的时候会进入阻塞状态,从而进入内核态,当获取到锁的时候需要从内核态恢复,需要线程上下文切换。 (线程被阻塞后便进入内核(Linux)调度状态,这个会导致系统在用户态与内核态之间来回切换,严重影响锁的性能)
自定义自旋锁
package com.liang.lesson9;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
@SuppressWarnings("all")
/*
自旋锁之自定义自旋锁
*/
public class Demo3 {
AtomicReference<Thread> atomic = new AtomicReference<>();
//加锁
public void mylock(){
Thread thread = Thread.currentThread();
System.out.println(Thread.currentThread().getName()+"===>mylock()");
// 自旋锁
while (!atomic.compareAndSet(null,thread)){
}
}
// 解锁
public void myunlock(){
Thread thread = Thread.currentThread();
System.out.println(Thread.currentThread().getName()+"===>myunlock()");
atomic.compareAndSet(thread,null);
}
public static void main(String[] args) throws InterruptedException {
Demo3 demo3 = new Demo3();
new Thread(()->{
demo3.mylock();
try {
TimeUnit.SECONDS.sleep(3);
} catch (Exception e) {
e.printStackTrace();
} finally {
demo3.myunlock();
}
},"A").start();
TimeUnit.SECONDS.sleep(1);
new Thread(()->{
demo3.mylock();
try {
TimeUnit.SECONDS.sleep(1);
} catch (Exception e) {
e.printStackTrace();
} finally {
demo3.myunlock();
}
},"B").start();
}
}
死锁
所谓死锁,是指多个进程在运行过程中因争夺资源而造成的一种僵局,当进程处于这种僵持状态时,若无外力作用,它们都将无法再向前推进。
产生死锁的四个必要条件
预防死锁的方法:
t.println(Thread.currentThread().getName()+“===>mylock()”);
// 自旋锁
while (!atomic.compareAndSet(null,thread)){
}
}
// 解锁
public void myunlock(){
Thread thread = Thread.currentThread();
System.out.println(Thread.currentThread().getName()+"===>myunlock()");
atomic.compareAndSet(thread,null);
}
public static void main(String[] args) throws InterruptedException {
Demo3 demo3 = new Demo3();
new Thread(()->{
demo3.mylock();
try {
TimeUnit.SECONDS.sleep(3);
} catch (Exception e) {
e.printStackTrace();
} finally {
demo3.myunlock();
}
},"A").start();
TimeUnit.SECONDS.sleep(1);
new Thread(()->{
demo3.mylock();
try {
TimeUnit.SECONDS.sleep(1);
} catch (Exception e) {
e.printStackTrace();
} finally {
demo3.myunlock();
}
},"B").start();
}
}
>**死锁**
**所谓死锁,是指多个进程在运行过程中因争夺资源而造成的一种僵局,当进程处于这种僵持状态时,若无外力作用,它们都将无法再向前推进。**
**产生死锁的四个必要条件**
* **互斥条件:进程要求对所分配的资源进行排它性控制,即在一段时间内某资源仅为一进程所占用**
* **请求和保持条件:当进程因请求资源而阻塞时,对已获得的资源保持不放**
* **不剥夺条件:进程已获得的资源在未使用完之前,不能剥夺,只能在使用时由自己释放**
* **环路等待条件:在发生死锁时,必然存在一个进程——资源的环形链**
**预防死锁的方法:**
* **资源一次性分配:一次性分配所有资源,这样就不会再有请求了:(破坏请求条件)**
* **只要有一个资源得不到分配,也不给这个进程分配其他的资源:(破坏请保持条件)**
* **可剥夺资源:即当某进程获得了部分资源,但得不到其它资源,则释放已占有的资源(破坏不可剥夺条件)**
* **资源有序分配法:系统给每类资源赋予一个编号,每一个进程按编号递增的顺序请求资源,释放则相反(破坏环路等待条件)**