业务:普通的线程代码 Thread
Runnable 没有返回值、效率相比于Callable较低!
2.线程与进程
线程进程如果不能用一句话说出来的技术,不扎实!
进程:一个程序,QQexe Music.exe 程序的集合;
一个进程往往可以包含多个线程,至少包含一个!
java默认有几个线程?2个 main、GC
线程:开了一个进程Typora,写字,自动保存(线程负责的)
Thread、Runnable/Callable
Java真的可以开启线程吗?
/* 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();
开发、并行
开发编程:并发、并行
开发(多线程操作同一个资源)
- cpu一核,模拟出来多条线程,快速交替
并行(多个人一起走)
- cpu多核,多个线程可以同时执行
public class Test1 {
public static void main(String[] args) {
//获取cpu的核数
//cpu密集型,IO密集型
System.out.println(Runtime.getRuntime().availableProcessors());
}
}
并发编程的本质:充分利用CPU的资源
所有的公司都很看重效率!
线程有几个状态?
//新生
NEW
//运行
RUNNABLE
//阻塞
BLOCKED
//等待
WAITING
//超时等待
TIMED_WAITING,
//终止
TERMINATED;
}
wait/sleep区别
1.来自不同的类
wait--->Object
sleep--->Thread
2.关于锁的释放
wait会释放锁,sleep不释放锁。
3.使用的范围是不同的
wait须在同步代码块中
4.是否需要捕获异常
wait不需要捕获异常
sleep必须捕获异常
3.Lock锁(重点)
Lock接口
公平锁:十分公平:先来后到
非公平锁:十分不公平:可以插队(默认)
synchronized与lock的区别
1.synchronized内置的java关键字,Lock是一个java类。
2.synchronized无法判断获取锁的状态,Lock可以判断是否获取到了锁。
3.synchronized会自定释放锁,lock必须要手动释放锁。如果不释放锁,会出现"死锁"
4.synchronized线程1(获得锁、阻塞)、线程2(等待傻傻的等)Lock锁就不一定会一直等下去
5.synchronized可重入锁,不可以中断的,非公平;Lock可重入锁,可以判断锁,非公平锁(自己设置)
6.synchronized适合锁少量的代码同步问题,Lock锁适合锁大量的同步代码
锁是什么,如何判断锁的是谁?
4.生产者和消费者问题
面试:单例模式、排序算法、生产者消费者、死锁
生产者和消费者问题synchronized
/**
* 线程之间的通信问题:生产者和消费者问题
* 线程交替执行 A B 操作同一个变量 num=0
* A num+1
* B num-1
*/
public class A {
public static void main(String[] args) {
thread thread = new thread();
new Thread(()->{for(int i=0;i<20;i++) {thread.add();}},"A").start();
new Thread(()->{for(int i=0;i<20;i++) {thread.decrement();}},"B").start();}
}
class thread{
private int i=0;
public synchronized void add(){
if (i<1){
//当i<1时加1
i+=1;
System.out.println(Thread.currentThread().getName()+"--->"+i);
//通知其他线程我处理完毕了
this.notifyAll();
}else {
try {
//当不满足i<1时,进程进入等待
this.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
public synchronized void decrement(){
if (i>0) {
i -= 1;
System.out.println(Thread.currentThread().getName() + "--->" + i);
//处理完毕,通知其他线程
this.notifyAll();
}else {
try {
//当不满足i>0时,进程进入等待
this.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
问:上面这种写法,当存在四个线程时,是否能不出错?答:会出现虚假唤醒问题!
将if改为while判断 if只会判断一次,两个线程有可能会同时进入方法
public class A {
public static void main(String[] args) {
thread thread = new thread();
new Thread(()->{for(int i=0;i<50;i++) {thread.add();}},"A").start();
new Thread(()->{for(int i=0;i<50;i++) {thread.decrement();}},"B").start();
new Thread(()->{for(int i=0;i<50;i++) {thread.add();}},"C").start();
new Thread(()->{for(int i=0;i<50;i++) {thread.decrement();}},"D").start();
}
}
class thread{
private int i=0;
public synchronized void add(){
while (i!=0){
try {
this.wait();
} catch (InterruptedException e) {
}
}
//当i<1时加1
i+=1;
System.out.println(Thread.currentThread().getName()+"--->"+i);
//通知其他线程我处理完毕了
this.notifyAll();
}
public synchronized void decrement(){
while (i!=1) {
try {
this.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
i -= 1;
System.out.println(Thread.currentThread().getName() + "--->" + i);
//处理完毕,通知其他线程
this.notifyAll();
}
}
JUC版本的生产者消费者
通过Lock找到Condition
代码实现:
public class B {
public static void main(String[] args) {
thrad thrad = new thrad();
new Thread(()->{for(int i=0;i<50;i++){thrad.add();}},"A").start();
new Thread(()->{for(int i=0;i<50;i++){thrad.decrement();}},"B").start();
}
}
class thrad {
int i=0;
Lock lock=new ReentrantLock();
Condition condition=lock.newCondition();
public void add(){
try {
lock.lock();
while(i!=0){
condition.await();
}
i+=1;
condition.signalAll();
System.out.println(Thread.currentThread().getName()+"---i--->"+i);
} catch (InterruptedException e) {
throw new RuntimeException(e);
} finally {
lock.unlock();
}
}
public void decrement(){
lock.lock();
try {
while (i!=1){
condition.await();
}
i-=1;
condition.signalAll();
System.out.println(Thread.currentThread().getName()+"---i--->"+i);
} catch (InterruptedException e) {
throw new RuntimeException(e);
} finally {
lock.unlock();
}
}
}
任何一个新的技术,绝对不是仅仅只是覆盖了原来的技术,一定会有优势和补充!
Condition精准的通知和唤醒线程
代码测试:
public class C {
public static void main(String[] args) {
Data data = new Data();
new Thread(()->{for(int i=0;i<20;i++)data.printA();},"A").start();
new Thread(()->{for(int i=0;i<20;i++)data.printB();},"B").start();
new Thread(()->{for(int i=0;i<20;i++)data.printC();},"C").start();
}
}
class Data{//资源类
private Lock lock=new ReentrantLock();
private Condition condition1 = lock.newCondition();
private Condition condition2 = lock.newCondition();
private Condition condition3 = lock.newCondition();
private int number=1;
public void printA(){
lock.lock();
try {
while (number!=1){
condition1.await();
}
number=2;
condition2.signal();
System.out.println("--->AAA");
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
lock.unlock();
}
}
public void printB(){
lock.lock();
try {
while (number!=2){
condition2.await();
}
number=3;
condition3.signal();
System.out.println("--->BBB");
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
lock.unlock();
}
}
public void printC(){
lock.lock();
try {
while (number!=3){
condition3.await();
}
number=1;
condition1.signal();
System.out.println("--->CCC");
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
lock.unlock();
}
}
}
5、8锁现象
如何判断锁的是谁?
/**
* 8锁就是关于锁的8个问题
* 1.标准情况下,两个线程先打印发短信还是打电话? 1.sendSms 2.call
* 2.sendSms等待4秒,两个线程先打印发短信还是打电话? 1.sendSms 2.call
*/
public class Test1 {
public static void main(String[] args) {
Phone phone = new Phone();
new Thread(()->{phone.sendSms();}).start();
//捕获
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
new Thread(()->{phone.call();}).start();
}
}
class Phone{
//synchronized锁的对象是方法的调用者!
//两个方法用的是同一把锁,谁先拿到谁先执行
public synchronized void sendSms(){
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("sendSms");
}
public synchronized void call(){
System.out.println("call");
}
}
/**
* 3.增加一个普通方法并调用,两个线程先打印发短信还是hello? 1.hello 2.sendSms
* 4.两个对象,两个同步方法,两个线程先打印发短信还是打电话? 1.call 2.sendSms
*/
public class Test2 {
public static void main(String[] args) {
//两个对象,两把锁
Phone2 phone = new Phone2();
Phone2 phone2 = new Phone2();
new Thread(()->{phone.sendSms();}).start();
//捕获
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
new Thread(()->{phone2.call();}).start();
}
}
class Phone2{
//synchronized锁的对象是方法的调用者!
//两个方法用的是同一把锁,谁先拿到谁先执行
public synchronized void sendSms(){
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("sendSms");
}
public synchronized void call(){
System.out.println("call");
}
public void hello(){
System.out.println("hello");
}
}
/**
* 5.增加两个静态同步方法,只有一个对象,先打印哪个? 1.sendSms 2.call
* 6.两个对象,两个方法均为静态同步方法,先打印哪个? 1.sendSms 2.call
*/
public class Test3 {
public static void main(String[] args) {
//两个对象的class类模板只有一个,static锁的是Class类本身
Phone3 phone = new Phone3();
Phone3 phone2 = new Phone3();
new Thread(()->{phone.sendSms();}).start();
//捕获
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
new Thread(()->{phone2.call();}).start();
}
}
//唯一的Class对象
class Phone3{
//synchronized锁的对象是方法的调用者!
//static 随着类的加载而加载,锁的是Class
public static synchronized void sendSms(){
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("sendSms");
}
public static synchronized void call(){
System.out.println("call");
}
}
/**
* 7.调用一个静态同步方法一个普通同步方法,先调用谁? 1.call 2.sendSms
* 8.两个对象,分别调用一个静态同步方法一个普通同步方法,先调用谁? 1.call 2.sendSms
*/
public class Test4 {
public static void main(String[] args) {
//两个对象的class类模板只有一个,static锁的是Class类本身
Phone4 phone = new Phone4();
Phone4 phone2 = new Phone4();
new Thread(()->{phone.sendSms();}).start();
//捕获
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
new Thread(()->{phone2.call();}).start();
}
}
//唯一的Class对象
class Phone4{
//static 随着类的加载而加载,锁的是Class
public static synchronized void sendSms(){
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("sendSms");
}
//synchronized锁的对象是方法的调用者!
public synchronized void call(){
System.out.println("call");
}
}
小结
new 当调用的方法为普通方法时,锁锁的是new出来的对象
static 当调用的方法为静态同步方法时,锁锁的是Class 类本身
6.集合的安全性
List不是线程安全的,同理Set也不是同样建议使用CopyOnWriteArraySet<>()
/**
* java.util.ConcurrentModificationException 并发修改异常
*/
public class ListTest {
public static void main(String[] args) {
//并发下ArrayList不安全,synchronized
/**
* 解决方案:
* 1.List<String> list1 = new Vector<>();
* 2.List<String> list1 = Collections.synchronizedList(list);
* 3.List<String> list1 =new CopyOnWriteArrayList<>();
*/
//CopyOnWrite写入时复制 COW 计算机设计领域的一种优化策略;
//多个线程调用的时候,list,读取的时候,固定的,写入(覆盖)
//在写入的时候避免覆盖,造成数据问题!
//读写分离
//CopyOnWriteArrayList 比 Vector 厉害在哪里?效率更高
List<String> list1 =new CopyOnWriteArrayList<>();
for (int i=0;i<10;i++){
new Thread(()->{list1.add(UUID.randomUUID().toString().substring(1,8));
System.out.println(list1);},String.valueOf(i)).start();
}
}
}
HashSet的底层是什么?
public HashSet() {
map = new HashMap<>();
}
//add Set 本质就是 map key是无法重复的!
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
private static final Object PRESENT = new Object();//常量
HashMap
/**
* java.util.ConcurrentModificationException 并发修改异常
*/
public class MapTest {
public static void main(String[] args) {
//map是这样用的吗? 工作中不用HashMap
//默认等价于什么? 影响因子 0.75 默认容量16
ConcurrentHashMap<String, String> map1 = new ConcurrentHashMap<>();
for (int i=0;i<60;i++){
new Thread(()->{
map1.put(Thread.currentThread().getName(),UUID.randomUUID().toString().substring(1,6));
System.out.println(map1.toString());
},String.valueOf(i)).start();
}
}
}
7、Callable()
1.可以有返回值
2.可以抛出异常
3.方法不同,run{}/call{}
代码测试
public class CallableTest {
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();
//new Thread().start();//怎么启动Callable
MyThread thread = new MyThread();
FutureTask futuretask = new FutureTask(thread);
new Thread(futuretask,"A").start();
new Thread(futuretask,"B").start();//结果会被缓存,效率高
Integer o = (Integer) futuretask.get();//get方法可能会产生阻塞!把他放到最后或者通过异步通信来处理
System.out.println(o);
}
}
class MyThread implements Callable<Integer> {
@Override
public Integer call() throws Exception {
System.out.println("--->call");//会打印几个call
return 1024;
}
}
细节:
1.有缓存
2.结果可能需要等待,会阻塞!
8、常用的辅助类(必会)
8.1CountDownLatch
//计数器
public class countDownLatchDemo {
public static void main(String[] args) throws InterruptedException {
//倒计时,必须要执行任务的时候,再使用!
CountDownLatch countDownLatch = new CountDownLatch(6);
for (int i=0;i<6;i++){
new Thread(()->{
System.out.println(Thread.currentThread().getName()+" Go Out");
countDownLatch.countDown();//计数器-1
}, String.valueOf(i)).start();
}
countDownLatch.await();//等待计数器归零,继续执行
System.out.println("关门");
}
}
原理:
countDownLatch.countDawn();//数量-1
countDownLatch.await();//等待计数器归零,技续执行
每次有线程调用countDown()数量-1,假设计数器变为0,countDownLatch.await()就会被唤醒,继续执行。
8.2CycliBarrier
public class CyclicBarrierDemo {
public static void main(String[] args) throws BrokenBarrierException, InterruptedException {
/**
* 加法计数器
*/
CyclicBarrier cyclicBarrier = new CyclicBarrier(6,()->{
System.out.println("guanmen");
});
for (int i = 1; i < 7; i++) {
final int temp=i;
new Thread(()->{
System.out.println(Thread.currentThread().getName()+" go room "+temp);
try {
cyclicBarrier.await();
} catch (InterruptedException e) {
throw new RuntimeException(e);
} catch (BrokenBarrierException e) {
throw new RuntimeException(e);
}
}).start();
}
}
}
8.3、Semaphore
信号量
抢车位!
6辆车---3个车位
public class SemaphoreDemo {
public static void main(String[] args) {
//线程数量:限流
Semaphore semaphore = new Semaphore(3);
for (int i = 0; i < 6; i++) {
final int temp1=i;
new Thread(()->{
//acquire()得到
//release()释放
try {
semaphore.acquire();
System.out.println(Thread.currentThread().getName()+" P ");
TimeUnit.SECONDS.sleep(2);
System.out.println(Thread.currentThread().getName()+" go out");
} catch (InterruptedException e) {
throw new RuntimeException(e);
}finally{
//release()释放
semaphore.release();
}
}, String.valueOf(i)).start();
}
}
}
原理:
semaphore.acquire() 获得,假设已经满了,等待,等到被释放为止!
semaphore.release() 释放,会将当前的信号量释放+1,然后唤醒等待的线程!
作用:多个共享资源互斥的使用!并发限流的最大线程数。
9、读写锁
ReadWriteLock
读的时候可以多个线程读,写的时候只能由一个线程写。
测试代码:
public class ReadWriteLockDemo {
public static void main(String[] args) {
MyCacheLock myCache = new MyCacheLock();
for (int i = 0; i <5; i++) {
final int temp=i;
new Thread(()->{myCache.put(temp+"",temp+"");}, String.valueOf(i)).start();
}
for (int i = 0; i < 5; i++) {
final int temp=i;
new Thread(()->{myCache.get(temp+"");}, String.valueOf(i)).start();
}
}
}
/**
* 自定义带锁的缓存
* ReadWriteLock
* 读-读 可以共存! 独占锁
* 读-写 不能共存! 共享锁
* 写-写 不能共存! 共享锁
*/
class MyCacheLock {
private volatile Map<String, Object> map = new HashMap<>();
//读写锁:更加细粒度的操作
private ReadWriteLock lock=new ReentrantReadWriteLock();
//存,写入的时候只希望同时只有一个线程
public void put(String key,Object value) {
//写锁上锁
lock.writeLock().lock();
try {
System.out.println(Thread.currentThread().getName()+" Write");
map.put(key,value);
System.out.println("Write ok "+value);
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
//写锁解锁
lock.writeLock().unlock();
}
}
//取,读的时候所有人都可以读!
public void get(String key) {
System.out.println(Thread.currentThread().getName()+" Read");
Object o = map.get(key);
System.out.println(o+" Read ok");
}
}
10、阻塞队列
阻塞队列:
BlockQueue BlockingQueue 不是新东西,是数组的一种
什么情况下我们会使用 阻塞队列:多线程并发处理,线程池
学会使用队列
四组API
方式 | 抛出异常 | 不会抛出异常 | 阻塞等待 | 超时等待 |
添加 | add() | offer(Object) | put() | offer(Object,int,TimeUnit) |
移除 | remove() | poll() | take() | poll(int,TimeUnit) |
判断 | element() | peek() |
public class Test {
public static void main(String[] args) {
test1();
}
/**
* 抛出异常
*/
public static void test1(){
//队列大小
Queue abq = new ArrayBlockingQueue<>(3);
System.out.println(abq.add("a"));
System.out.println(abq.add("b"));
System.out.println(abq.add("c"));
//java.lang.IllegalStateException: Queue full 抛出异常
// System.out.println(abq.add("d"));
System.out.println(abq.remove());
System.out.println(abq.remove());
System.out.println(abq.remove());
//java.util.NoSuchElementException
// System.out.println(abq.remove());
}
}
/**
* 不抛出异常 offer/poll
*/
public static void test2() {
//队列大小
Queue abq = new ArrayBlockingQueue<>(3);
System.out.println(abq.offer("a"));
System.out.println(abq.offer("b"));
System.out.println(abq.offer("C"));
//false
System.out.println(abq.offer("d"));
System.out.println(abq.poll());
System.out.println(abq.poll());
System.out.println(abq.poll());
//null
System.out.println(abq.poll());
}
/**
* 等待,阻塞(死等)
*/
public static void test3() throws InterruptedException {
ArrayBlockingQueue abq = new ArrayBlockingQueue<>(3);
abq.put("a");
abq.put("b");
abq.put("c");
System.out.println(abq.toString());
// abq.put("D"); 队列已满,一直阻塞
System.out.println(abq.take());
System.out.println(abq.take());
System.out.println(abq.take());
// System.out.println(abq.take()); 没有这个元素,一直阻塞
}
/**
* 等待,阻塞(超时等待)
*/
public static void test4() throws InterruptedException {
ArrayBlockingQueue<Object> abq = new ArrayBlockingQueue<>(3);
abq.offer("a");
abq.offer("b");
abq.offer("c");
abq.offer("d",2, TimeUnit.SECONDS);//超时两秒将退出
abq.poll();
abq.poll();
abq.poll();
abq.poll(2,TimeUnit.SECONDS);//超时两秒将退出
}
SynchronousQueue同步队列
没有容量,进去一个元素,必须等待取出来之后,才能再往里面放一个元素!
put、take
/**
* 同步队列
* 和其他BlockQueue不一样,SynchronousQue 不存储元素
* put了一个元素,必须从里面先take取出来,否则不能再put进去值!
*/
public class SynchronousQueDemo {
public static void main(String[] args) {
SynchronousQueue<String> sq = new SynchronousQueue<>();
new Thread(() -> {
try {
sq.put("1");
System.out.println(sq);
sq.put("2");
System.out.println(sq);
sq.put("3");
System.out.println(sq);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}, String.valueOf("放-->")).start();
new Thread(() -> {
try {
TimeUnit.SECONDS.sleep(2);
System.out.println(sq.take());
TimeUnit.SECONDS.sleep(2);
System.out.println(sq.take());
TimeUnit.SECONDS.sleep(2);
System.out.println(sq.take());
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}, String.valueOf("取-->")).start();
}
}
11.线程池(重点)
线程池:三大方法、7大参数、4种拒绝策略
池化技术
程序的运行的本质:占用系统的资源 !优化资源的使用!--->池化技术
线程池、连接池、内存池、对象池//.....
池化技术:事先准备好一些资源,有人要用,就来我这里拿,用完之后还给我
默认大小:2
max:
线程池的好处:
1.降低资源的消耗
2.提高响应的速度
3.方便管理
线程复用、可以控制最大并发数、管理线程
三大方法
//Executors 工具类、三大方法
public class Demo01 {
public static void main(String[] args) {
ExecutorService executor = Executors.newSingleThreadExecutor();//单个线程
ExecutorService executor1 = Executors.newFixedThreadPool(5);//创建一个固定的线程池的大小
ExecutorService executor2 = Executors.newCachedThreadPool();//遇强则强,遇弱则弱
try {
for (int i = 0; i < 100; i++) {
//使用了线程池后,使用线程池来创建线程
executor.execute(()->{
System.out.println(Thread.currentThread().getName()+" ok");;});
}
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
//线程池用完,程序结束,关闭线程池
executor.shutdown();
}
}
}
7大参数 默认线程池大小 最大线程池大小 超时等待时间 时间单位 阻塞队列 线程工厂、创建线程的,一般不用动 拒绝策略
源码分析
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亿,会导致内存溢出OOM
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.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
手动创建一个线程池
四种拒绝策略
/**
* new ThreadPoolExecutor.AbortPolicy());//请求>最大+队列,抛出异常 RejectedExecutionException
* new ThreadPoolExecutor.CallerRunsPolicy());//哪来的回哪去,这里将由mian处理
*new ThreadPoolExecutor.DiscardPolicy());//丢弃超额任务,不抛异常
*new ThreadPoolExecutor.DiscardOldestPolicy());//尝试与第一个线程竞争,失败丢则放弃超额任务
*/
小结和拓展:池的大小如何设置 性能调优
public class Demo01 {
public static void main(String[] args) {
//自定义线程池!工作中只会使用ThreadPoolExecutor
//最大线程到底该如何定义?
//1.CPU 密集型,几核就是几,可以保持Cpu的效率最高
// Runtime.getRuntime().availableProcessors();
//2.IO密集型,判断你程序中十分耗IO的线程,设置为耗时IO线程的2倍
ExecutorService threadPool=new ThreadPoolExecutor(
2,
Runtime.getRuntime().availableProcessors(),//获取逻辑CPU个数
3,
TimeUnit.SECONDS,
new LinkedBlockingQueue<>(3),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.DiscardOldestPolicy());//尝试与第一个线程竞争,失败丢则抛弃超额任务
try {
for (int i = 0; i < 100; i++) {
//使用了线程池后,使用线程池来创建线程
threadPool.execute(() -> {
System.out.println(Thread.currentThread().getName() + " ok");
;
});
}
} catch (
Exception e) {
throw new RuntimeException(e);
} finally {
//线程池用完,程序结束,关闭线程池
threadPool.shutdown();
}
}
}
12、四大函数式接口(必须掌握)
新时代的程序员:lambda表达式、链式编程、函数式接口、Stream流
函数式接口:只有一个方法的接口
@FunctionalInterface
public interface Runnable {
public abstract void run();
}
//超级多FunctionalInterface
//简化编程模型,在新版本的框架底层大量应用!
//foreach(消费者类的函数式接口)
代码测试:
Function函数式接口
import java.util.function.Function;
/**
*function 函数式接口,有一个输入参数,有一个输出结果
* 只要是函数式接口,就可以用lambda表达式简化
*/
public class Demo01 {
public static void main(String[] args) {
Function<String, Integer> functionT = new Function<String, Integer>() {
@Override
public Integer apply(String o) {
return Integer.parseInt(o);
}
};
Function function=(str)->{return str;};
System.out.println(functionT.apply(String.valueOf(123)));
}
}
断定型接口:有一个输入参数,返回值只能是 布尔值!
/**
* 断定型接口:有一个输入参数,返回值只能是 布尔值!
*/
public class Demo02 {
public static void main(String[] args) {
// Predicate<String> predicate = new Predicate<String>() {
// @Override
// public boolean test(String o) {
//
// return o.isEmpty();
// }
// };
Predicate<String> pre=(str)->{return str.isEmpty();};
boolean str = pre.test("");
System.out.println(str);
}
}
Consumer 消费型接口:
Supplier 供给型接口:
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);
// }
// };
//
// Supplier supplier = new Supplier<String>() {
// @Override
// public String get() {
// return 1024+"";
// }
// };
// consumer.accept("1");
Consumer con=(o)->{System.out.println(o);};
// System.out.println(supplier.get());
Supplier sup=()->{return 1024;};
con.accept("o");
System.out.println(sup.get());
}
}
13、Stream流式计算
什么是Stream流式计算
存储+计算
集合:MySQL本质就是存储东西打的
计算都应该交给流来操作!
/**
* 题目要求:一分钟内完成此题,只能用一行代码实现!
* 现在有五个用户,筛选:
* 1.列出id为2的倍数的对象
* 2.年龄必须大于23岁
* 3.用户名转为大写字母
* 4.用户名字母倒着排序
* 5.只输出一个用户
*/
public class Test {
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(6,"e",25);
//集合就是存储
List<User> list = Arrays.asList(u1, u2, u3, u4, u5);
//计算交给Stream流
//lambda表达式,链式编程、函数式接口、Stream链式计算
list.stream()
.filter(u->{return u.getId()%2==0;})
.filter(u->{return u.getAge()>23;})
.filter(u->{ u.setName(u.getName().toUpperCase());return true;})
.sorted((u,uu)->{return uu.getName().compareTo(u.getName());})
.limit(1)
.forEach(System.out::println);
}
}
14、ForkJoin
什么是ForkJoin
ForkJoin在JDK1.7中用于:大数据量时并行执行任务,,提高效率!
大数据:Map Reduce (把大任务拆分为小任务)
forkjoin工作特点:工作窃取
这里维护的都是双端队列
ForkJoin的操作
/**
* 求和计算的任务!
* <p>
* 3000 6000(ForkJoin) 9000(Stream并行流)
* 如何使用ForkJoinPool
* 1.ForkJoinPool
* 2.计算任务ForkJoinPool.execute(ForkJoinTask<?> task)
*/
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 middle = (start + end) / 2;//中间值
ForkJoinDemo task = new ForkJoinDemo(start, middle);
ForkJoinDemo task1 = new ForkJoinDemo(middle+1, end);
task.fork();
task1.fork();
return task.join()+task1.join();
} else {
long sum = 0L;
for (long i = start; i <= end; i++) {
sum += i;
}
return sum;
}
}
}
public class Test {
public static void main(String[] args) {
// test1();//用时:--->272ms
//
// test2();//用时:--->229ms
test3();//用时: --->137ms
}
public static void test1(){
//计算1-1_0000_0000的和
long sum = 0L;
long l = System.currentTimeMillis();
for (int i = 0; i < 10_0000_0000; i++) {
sum += i;
}
long l2 = System.currentTimeMillis();
System.out.println("用时:--->" + (l2 - l) + "ms" + " 结果为:" + sum);//用时:--->262ms 结果为:499999999500000000
}
public static void test2(){
ForkJoinDemo forkJoinDemo = new ForkJoinDemo(0, 10_0000_0000-1);
long l = System.currentTimeMillis();
Long compute = forkJoinDemo.compute();
System.out.println("结果为:--->"+compute+" 用时:--->"+ (System.currentTimeMillis()-l)+"ms");//结果为:--->499999999500000000 用时:--->227ms
}
public static void test3(){
long start = System.currentTimeMillis();
//start并行流() (]
long sum = LongStream.rangeClosed(0L, 10_0000_0000L-1).parallel().reduce(0, Long::sum);
long end = System.currentTimeMillis();
System.out.println(end-start+"ms--->"+sum);
}
}
15、异步回调
Future设计的初衷:对将来的某个事件的结果进行建模
/**
* 异步调用:CompletableFuture
* 异步执行
* 成功回调
* 失败回调
*/
public class Demo01 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
CompletableFuture<Void> completableFuture = CompletableFuture.runAsync(() -> {
//没有返回值的runAsync 异步回调
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(Thread.currentThread().getName() + "runAsync=>Void");
});
System.out.println("111");
completableFuture.get();//获取执行结果
//有返回值的supplyAsync 异步回调
//aj
CompletableFuture<String> runnable = CompletableFuture.supplyAsync(() -> {
int i = 10 / 0;
return "1024";
});
System.out.println(runnable.whenComplete((u, t) -> {
System.out.println("u--->" + u);//正常结果
System.out.println("t--->" + t);//错误信息
}).exceptionally((e) -> {
System.out.println(e.getMessage());
return String.valueOf(233);//可以获得错误的返回结果
}).get());
}
}
16.JMM
请你谈谈对Volatile的理解
Volatile是java虚拟机提供的轻量级的同步机制
1.保证可见性
2.不保证原子性
3.禁止指令重排
什么是JMM
JMM:java的内存模型,是一种概念、约定
关于JMM的一些同步的约定:
1.线程的解锁前,必须把共享变量立刻刷回主存。
2.线程加锁前,必须读取主存中的最新值到工作内存中!
3.加锁和解锁是同一把锁
线程 工作内存、主内存
17.Volatile
1、保证可见性
public class VolatileTest1 {
//保证可见性
private volatile static int num=0;
public static void main(String[] args) {
new Thread(()->{
while (num==0){
}
}).start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
num+=1;
System.out.println(num);
}
}
2、不保证原子性
public class Volatile {
//volatile不保证原子性
private volatile static int num=0;
public static void add(){
num++;
}
public static void main(String[] args) {
for (int i = 0; i < 20; i++) {
new Thread(()->{
for (int j = 0; j < 1000; j++) {
add();
}
}).start();
}
while (Thread.activeCount()>2){
Thread.yield();
}
System.out.println(num);
}
}
如果不加lock和synchronized,怎么样保证原子性
使用原子类,解决原子性
public class VolatileTest2 {
//volatile不保证原子性
//原子类的 Integer
private static AtomicInteger num=new AtomicInteger();
public static void add(){
num.getAndIncrement();//一个原子性操作,CAS
}
public static void main(String[] args) {
for (int i = 0; i < 20; i++) {
new Thread(()->{
for (int j = 0; j < 1000; j++) {
add();
}
}).start();
}
while (Thread.activeCount()>2){
Thread.yield();
}
System.out.println(num);
}
}
这些类的底层都直接和操作系统挂钩!在内存中修改值!Unsafe类是一个很特殊的存在。
指令重排
什么是指令重排:你写的程序,计算机并不是按照你写的那样去执行的。
源代码-->编译器优化的重排-->指令并行也可能会重排-->内存系统也会重排-->执行
Volatile是可以保持可见性。不能保证原子性,由于内存屏障,可以保证避免指令重排的现象产生!
18、彻底玩转单例模式
饿汉式单例模式
//饿汉式单例模式
public class Hunger {
//饿汉式可能会浪费空间
private byte[] b=new byte[1024*1024];
private byte[] y=new byte[1024*1024];
private byte[] t=new byte[1024*1024];
private byte[] e=new byte[1024*1024];
private byte[] s=new byte[1024*1024];
private Hunger() {
}
private final static Hunger HUNGER =new Hunger() {};
public static Hunger getInstance() {
return HUNGER;
}
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
new Thread(() -> {
System.out.println(getInstance() + "--->ok " + Thread.currentThread().getName());
}, String.valueOf(i)).start();
}
}
}
懒汉式单例模式
//懒汉式
//道高一尺魔高一丈,反射会破坏单例
public class lazyMan {
private static boolean qingjiang=false;
private lazyMan() {
synchronized (lazyMan.class){
if (qingjiang==false){
qingjiang=true;
}else
throw new RuntimeException("不要试图用反射破坏单例");
}
System.out.println(Thread.currentThread().getName() + "---0k");
}
private volatile static lazyMan lazy;
public static lazyMan getInstance() {
//双重检测锁的懒汉式单例 DCL懒汉式
if (lazy == null) {
synchronized (lazyMan.class) {
if (lazy == null) {
lazy = new lazyMan();//不是一个原子性操作
}
}
}
return lazy;
}
/**
* 1.分配内存空间
* 2.执行构造方法,初始化对象
* 3.把这个对象指向这个空间
*
* 123
* 132 A
* B此时LazyMan还没有完成构造
*/
//多线程并发
public static void main(String[] args) throws Exception {
// for (int i = 0; i < 10; i++) {
// new Thread(() -> {
// getInstance();
// }, String.valueOf(i)).start();
// }
Field qingjiang = lazyMan.class.getDeclaredField("qingjiang");
qingjiang.setAccessible(true);
Constructor<lazyMan> declaredConstructor = lazyMan.class.getDeclaredConstructor(null);
declaredConstructor.setAccessible(true);
lazyMan lazyMan = declaredConstructor.newInstance();
qingjiang.set(lazyMan,false);
lazyMan lazyMan2 = declaredConstructor.newInstance();
// com.atguigu.single.lazyMan lazyMan2 = com.atguigu.single.lazyMan.getInstance();
System.out.println(lazyMan.hashCode()+"--111");
System.out.println(lazyMan2.hashCode()+"--222");
System.out.println(lazyMan.equals(lazyMan2));
}
}
静态内部类
//静态内部类
public class Holder {
private Holder(){
};
public static Holder getInstance(){
return InnerClass.HOLDER;
}
public static class InnerClass{
private static final Holder HOLDER=new Holder();
}
}
反射能够破坏上面的单例模式,于是我们需要一个能不被反射破坏的单例模式:enum
//enum是一个什么?本身也是一个Class类
public enum EnumSingle {
INSTANCE;
public EnumSingle getInstance(){
return INSTANCE;
}
}
class Test{
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
EnumSingle instance = EnumSingle.INSTANCE;
EnumSingle instance2 = EnumSingle.INSTANCE;
EnumSingle instance3 = EnumSingle.INSTANCE;
System.out.println(instance.equals(instance2));
System.out.println(instance2.equals(instance3));
Constructor<EnumSingle> declaredConstructor = EnumSingle.class.getDeclaredConstructor(String.class,int.class);//Cannot reflectively create enum objects
declaredConstructor.setAccessible(true);
EnumSingle enumSingle = declaredConstructor.newInstance();//java.lang.NoSuchMethodException 没有找到空参构造器
EnumSingle enumSingle2 = declaredConstructor.newInstance();
System.out.println(enumSingle2.equals(enumSingle));
}
}
19、深入理解CAS
什么是CAS
Unsafe类
CAS:比较当前工作内存中的值,如果这个值是期望的,那么则执行 操作,如果不是则一直循环
缺点:
1.循环会耗时
2.一次性只能保证一个共享变量的原子性
3.ABA问题
CAS:ABA问题(狸猫换太子)
public class CASDemo {
//compareAndSet:比较并交换
public static void main(String[] args) {
AtomicInteger atomicInteger=new AtomicInteger(2020);
//对于我们平时写的SQL : 乐观锁!
//expect 期望 update 更新
// public final boolean compareAndSet(int expect, int update)
//如果我期望的值达到了,那么久更新,否则,就不更新
//捣乱的线程
atomicInteger.compareAndSet(2020,2021);
System.out.println(atomicInteger.get());
atomicInteger.compareAndSet(2021,2020);
System.out.println(atomicInteger.get());
//期望的线程
atomicInteger.compareAndSet(2020,2222);
System.out.println(atomicInteger.get());
}
}
20、原子引用
带版本号的原子操作!
public class VolatileABA {
//compareAndSet:比较并交换
public static void main(String[] args) {
// AtomicInteger atomicInteger=new AtomicInteger(2020);
AtomicStampedReference<String> atomicInteger = new AtomicStampedReference<>("2020",1);
//对于我们平时写的SQL : 乐观锁!
//expect 期望 update 更新
// public final boolean compareAndSet(V expect, V update)
//如果我期望的值达到了,那么久更新,否则,就不更新
//捣乱的线程
atomicInteger.compareAndSet("2020","2021", atomicInteger.getStamp(),atomicInteger.getStamp()+1);
System.out.println(atomicInteger.getStamp()+" "+atomicInteger.get(new int[]{atomicInteger.getStamp()}));
atomicInteger.compareAndSet("2021","2020", atomicInteger.getStamp(),atomicInteger.getStamp()+1);
System.out.println(atomicInteger.getStamp()+" "+atomicInteger.get(new int[]{atomicInteger.getStamp()}));
//期望的线程
atomicInteger.compareAndSet("2020","2222", atomicInteger.getStamp(),atomicInteger.getStamp()+1);
System.out.println(atomicInteger.getStamp()+" "+atomicInteger.get(new int[]{atomicInteger.getStamp()}));
}
}
21、各种锁的理解
1.公平锁、非公平锁
公平锁:不能插队 (ReentrantLock lock = new ReentrantLock(true);)
非公平锁:可以插队 (默认非公平锁 synchronized lock)
public ReentrantLock() {
sync = new NonfairSync();
}
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
2.可重入锁
可重入锁(递归锁)
public class Demo01 {
public static void main(String[] args) {
call();
}
public static synchronized void call(){
System.out.println("--->打电话");
message();//这里也有锁
}
public static void message(){
ReentrantLock lock = new ReentrantLock();
lock.lock();
try {
System.out.println("--->发短信");
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
lock.unlock();
}
}
}
3.自旋锁
/**
* 自旋锁
*/
public class SpinlockDemo {
AtomicReference<Thread> atomicReference = new AtomicReference<>();
//加锁
public void mylock() {
Thread thread = Thread.currentThread();
System.out.println(thread.currentThread().getName() + "===> mylock");
//自旋锁
while (!atomicReference.compareAndSet(null, thread)) {
}
}
//解锁
public void unlock() {
Thread thread = Thread.currentThread();
System.out.println(Thread.currentThread().getName() + "===> unlock");
atomicReference.compareAndSet(thread, null);
}
}
public class SpinlockDemoTest {
static SpinlockDemo spinlockDemo = new SpinlockDemo();
public static void main(String[] args) {
new Thread(()->{
call();
},"A").start();
new Thread(()->{
message();
},"B").start();
}
public static void call(){
spinlockDemo.mylock();
try {
System.out.println("打电话--->");
TimeUnit.SECONDS.sleep(3);
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
spinlockDemo.unlock();
}
}
public static void message(){
spinlockDemo.mylock();
try {
System.out.println("发短信--->");
TimeUnit.SECONDS.sleep(1);
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
spinlockDemo.unlock();
}
}
}
4、死锁
死锁是什么
死锁测试,怎么排除死锁
public class DeadLockDemo {
public static void main(String[] args) {
String lockA="lockA";
String lockB="lockB";
new Thread(new MyThread(lockA,lockB),"T1").start();
new Thread(new MyThread(lockB,lockA),"T2").start();
}
}
class MyThread implements Runnable{
private String lockA;
private String lockB;
public MyThread(String lockA, String lockB) {
this.lockA = lockA;
this.lockB = lockB;
}
@Override
public void run() {
synchronized (lockA){
System.out.println(Thread.currentThread().getName()+"--->lock:"+lockA+"->get->"+lockB);
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
synchronized (lockB){
System.out.println(Thread.currentThread().getName()+"--->lock:"+lockB+"->get->"+lockA);
}
}
}
}
解决问题
1、使用jsp -l定位进程号(若无法使用jps命令可以重新配置一下jre的路径,重启idea再试)
2.使用jstack 进程号 查看堆栈信息
面试:工作中排查问题
1.日志
2.堆栈