首页 > 编程语言 > 详细

Java线程进阶知识-面试必备

时间:2020-09-29 13:16:07      阅读:24      评论:0      收藏:0      [点我收藏+]

多线程进阶JUC

1.什么是JUC

java.util 工具包,包,分类

业务:普通的线程代码 Thread

Runnable:没有返回值,相对效率较低

我们使用Callable

2.线程和进程

进程: 一个程序

一个进程可以包含多个线程,至少包含一个

Java真的可以开启线程吗?不可以

public synchronized void start() {
    /**
     * This method is not invoked for the main method thread or "system"
     * group threads created/set up by the VM. Any new functionality added
     * to this method in the future may have to also be added to the VM.
     *
     * A zero status value corresponds to state "NEW".
     */
    if (threadStatus != 0)
        throw new IllegalThreadStateException();
/* Notify the group that this thread is about to be started
 * so that it can be added to the group's list of threads
 * and the group's unstarted count can be decremented. */
group.add(this);

boolean started = false;
try {
    start0();
    started = true;
} finally {
    try {
        if (!started) {
            group.threadStartFailed(this);
        }
    } catch (Throwable ignore) {
        /* do nothing. If start0 threw a Throwable then
          it will be passed up the call stack */
    }
}

}

并发和并行

并发编程:(多线程操作同一个资源)

  • CPU

  • public class Test1 {
        public static void main(String[] args) {
            // 获取CPU的核数
            // CPU密集型,IO密集型
            System.out.println(Runtime.getRuntime().availableProcessors());
        }
    }
    

    并发编程的本质: 充分利用CPU的资源

并行: (多个人一起行走)

线程有几个状态:

public enum State {
       //新生
        NEW,
    //运行
    RUNNABLE,

   //阻塞
    BLOCKED,

    //等待
    WAITING,

  //超时等待
    TIMED_WAITING,

   //终止
    TERMINATED;
}

wait和sleep的区别

1.来自不同的类

wait---object

sleep--Thread

2.关于锁的释放

wait会释放锁,sleep不会释放

3.使用的范围不同

wait 必须在同步代码块中使用

sleep 可以在任何地方使用

4.是否需要捕获异常

wait 不是必须捕获异常

sleep 必须捕获异常

3.Lock锁(重点)

传统方式 synchronized

lock接口

技术分享图片

Lock锁

技术分享图片

公平锁:十分公平,先来后到

非公平锁:十分不公平:可以插队(默认)

package com.lei.demo1;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**

  • @ClassName SaleTicketDemo2
  • @Description: TODO
  • @Author 1689169874@qq.com
  • @Date 2020/7/23/023 16:51
  • @Version 1.0
    **/
    public class SaleTicketDemo2 {
    public static void main(String[] args) {
    //并发:多线程操作 ,把资源丢入线程
    final Ticket ticket = new Ticket();
    new Thread(()->{
    for (int i = 0; i < 60; i++) {
    ticket.sale();
    }
    }, "A").start();
    new Thread(()->{
    for (int i = 0; i < 60; i++) {
    ticket.sale();
    }
    }, "B").start();new Thread(()->{
    for (int i = 0; i < 60; i++) {
    ticket.sale();
    }
    }, "C").start();
    }
    }

//lock锁
//三部曲 1.新建锁 2.加锁 3.解锁
class Ticket2 {
private int number = 50;
Lock lock = new ReentrantLock();

public void sale() {
    lock.lock(); //加锁

    try {
        //业务代码
        if (number &gt; 0) {
            System.out.println(Thread.currentThread().getName() + &quot; 卖出了第&quot; + (number--) + &quot;票,剩余&quot; + number);
        }
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        //解锁
        lock.unlock();
    }
}

}

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 wait notify

package com.lei.PC;

/**

  • @ClassName A
  • @Description: TODO
  • @Author 1689169874@qq.com
  • @Date 2020/7/23/023 17:08
  • @Version 1.0
    **/

import jdk.nashorn.internal.ir.IfNode;

import javax.xml.crypto.Data;

/**

  • 线程之间的通信问题:生产者和消费者之间的问题: 等待唤醒和通知唤醒
  • 线程交替进行 A B 操作同一个变量 num=0
  • A num+1
  • B num-1
    */
    public class A {
    public static void main(String[] args) {
    MyData myData = new MyData();
    new Thread(()->{
    for (int i = 0; i < 10; i++) {
    try {
    myData.increment();
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    }
    },"A").start();
    new Thread(()->{
    for (int i = 0; i < 10; i++) {
    try {
    myData.decrement();
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    }
    },"B").start();
    }
    }

//判断等待,业务,通知
class MyData{
private int num = 0;
//+1
public synchronized void increment() throws InterruptedException {
if (num!=0){
//等待
this.wait();
}
num++;
this.notifyAll();
System.out.println(Thread.currentThread().getName()+"-->"+num);
}

//-1
public synchronized void decrement() throws InterruptedException {
    if (num==0){
        // 等待
        this.wait();
    }
    num--;
    //通知
    this.notifyAll();
    System.out.println(Thread.currentThread().getName()+&quot;--&gt;&quot;+num);
}

}

问题存在===>更多的线程安全吗? 因为我们使用了notifAll()

技术分享图片

if 改为 while

JUC版本的生产者和消费者

技术分享图片

通过Lock找到 Condition

技术分享图片

代码实现:

package com.lei.PC;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**

  • @ClassName B

  • @Description: TODO

  • @Author 1689169874@qq.com

  • @Date 2020/7/23/023 17:26

  • @Version 1.0
    **/
    public class B {
    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(()-&gt;{
         for (int i = 0; i &lt; 10; i++) {
             try {
                 data.increment();
             } catch (InterruptedException e) {
                 e.printStackTrace();
             }
         }
     },&quot;C&quot;).start();
     new Thread(()-&gt;{
         for (int i = 0; i &lt; 10; i++) {
             try {
                 data.decrement();
             } catch (InterruptedException e) {
                 e.printStackTrace();
             }
         }
     },&quot;D&quot;).start();
    

    }

    static class Data {
    private int num = 0;
    Lock lock = new ReentrantLock();
    Condition condition = lock.newCondition();

     //+1
    

// condition.await(); //等待
// condition.signalAll(); //唤醒
public void increment() throws InterruptedException {
lock.lock();
try {
//业务代码
while (num != 0) {
condition.await();
}
num++;
System.out.println(Thread.currentThread().getName() + "-->" + num);
condition.signalAll();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}

    //-1
    public void decrement() throws InterruptedException {
        lock.lock();
        try {
            while (num == 0) {
                // 等待
                condition.await();
            }
            num--;
            //通知
            condition.signalAll();
            System.out.println(Thread.currentThread().getName() + &quot;--&gt;&quot; + num);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}

}

Condition 精准的通知和唤醒线程

技术分享图片

代码测试:

package com.lei.PC;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**

  • @ClassName C

  • @Description: TODO

  • @Author 1689169874@qq.com

  • @Date 2020/7/23/023 20:53

  • @Version 1.0
    **/
    public class C {
    public static void main(String[] args) {
    Data2 data2 = new Data2();
    new Thread(() -> {
    for (int i = 0; i < 10; i++) {
    data2.printA();
    }
    }, "A").start();
    new Thread(() -> {
    for (int i = 0; i < 10; i++) {
    data2.printB();
    }
    }, "B").start();
    new Thread(() -> {
    for (int i = 0; i < 10; i++) {
    data2.printC();
    }
    }, "C").start();
    }
    }
    //A-> B -> C
    class Data2 {
    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 {
    //业务代码, 判断 ,执行,通知
    while (num!=1){
    //等待
    condition1.await();
    }
    System.out.println(Thread.currentThread().getName()+"=>AAAA");
    //唤醒指定的人 B
    num=2;
    condition2.signal();
    } catch (Exception e) {
    e.printStackTrace();
    } finally {
    lock.unlock();
    }
    }

    public void printB() {
    lock.lock();
    try {
    while (num!=2){
    condition2.await();
    }
    System.out.println(Thread.currentThread().getName()+"=>BBBB");
    num=3;
    condition3.signal();
    } catch (Exception e) {
    e.printStackTrace();
    } finally {
    lock.unlock();
    }
    }

    public void printC() {
    lock.lock();
    try {
    while (num!=3){
    condition3.await();
    }
    System.out.println(Thread.currentThread().getName()+"=>CCCC");
    num=1;
    condition1.signal();
    } catch (Exception e) {
    e.printStackTrace();
    } finally {
    lock.unlock();
    }
    }
    }

5.8锁现象

如何判断锁的是谁? 永远知道是什么锁,锁到底锁的是谁?

深刻理解我们的锁

package com.lei.lock8;

/**

  • @ClassName Test1
  • @Description: TODO
  • @Author 1689169874@qq.com
  • @Date 2020/7/23/023 22:56
  • @Version 1.0
    **/

import java.util.concurrent.TimeUnit;

/**

  • 8锁,就是关于锁的8个问题
  • 1、标准情况下,两个线程发短信和打电话谁先打印?
  • 2、发短信(内部延迟4秒)情况下,两个线程发短信和打电话谁先打印?
    */
    public class Test1 {
    public static void main(String[] args) {
    Phone phone = new Phone();
    //锁的存在
    new Thread(()->{
    phone.sendMsg();
    },"A").start();
    try {
    TimeUnit.SECONDS.sleep(1);
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    new Thread(() ->{
    phone.call();
    },"B").start();
    }
    }
    class Phone{
    //synchronized 锁的对象是锁的调用者
    // 两个方法用的是同一个锁,谁先拿到谁执行
    public synchronized void sendMsg(){
    try {
    TimeUnit.SECONDS.sleep(2);
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    System.out.println("发短信");
    }
    public synchronized void call(){
    System.out.println("打电话");
    }
    }

 

package com.lei.lock8;

import java.util.concurrent.TimeUnit;

/**

  • @ClassName Test2
  • @Description: TODO
  • @Author 1689169874@qq.com
  • @Date 2020/7/23/023 23:42
  • @Version 1.0
    **/

/**

  • 3.增加了一个普通方法后,是先执行hello还是先发短信?

  • 4.两个对象,两个同步方法,发短信还是打电话
    */
    public class Test2 {
    public static void main(String[] args) {
    //两个对象,两个调用者,两把锁
    Phone2 phone1 = new Phone2();
    Phone2 phone2 = new Phone2();
    //锁的存在
    new Thread(() -> {
    phone1.sendMsg();
    }, "A").start();
    try {
    TimeUnit.SECONDS.sleep(1);
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    new Thread(() -> {
    phone2.call();
    }, "B").start();
    }
    }

    class Phone2 {
    //synchronized 锁的对象是锁的调用者
    // 两个方法用的是同一个锁,谁先拿到谁执行
    public synchronized void sendMsg() {
    try {
    TimeUnit.SECONDS.sleep(2);
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    System.out.println("发短信");
    }

     public synchronized void call() {
         System.out.println(&quot;打电话&quot;);
     }
    

// 这里没有锁,不受锁的影响
public void hello(){
System.out.println("hello");
}
}

package com.lei.lock8;

import java.util.concurrent.TimeUnit;

/**
 * @ClassName Test3
 * @Description: TODO
 * @Author 1689169874@qq.com
 * @Date 2020/7/23/023 23:49
 * @Version 1.0
 **/

/**
 * 5.增加两个静态的同步方法,只有一个对象,发短信和打电话?
 * 6.两个对象,增加两个同步的教态方法,先发短信还是打电话?
 */
public class Test3 {
    public static void main(String[] args) {
        //两个对象,两个调用者,两把锁
        //两个对象的类都是同一个,static,锁的是Class
        Phone3 phone1 = new Phone3();
        Phone3 phone2 = new Phone3();

        //锁的存在
        new Thread(() -> {
            Phone3.sendMsg();
        }, "A").start();
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(() -> {
            Phone3.call();
        }, "B").start();
    }
}
class Phone3 {
    //synchronized 锁的对象是锁的调用者
//    两个方法用的是同一个锁,谁先拿到谁执行
//    static 静态方法
//    类一加载就有了,锁的是Class
    public static synchronized void sendMsg() {
        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("发短信");
    }

    public static synchronized void call() {
        System.out.println("打电话");
    }

}
package com.lei.lock8;

import java.util.concurrent.TimeUnit;

/**
 * @ClassName Test4
 * @Description: TODO
 * @Author 1689169874@qq.com
 * @Date 2020/7/23/023 23:56
 * @Version 1.0
 **/

/**
 * 7.一个静态同步方法,一个普通同步方法,先打印 发短信,打电话?
 * 8.两个对象,一个静态同步方法,一个普通同步方法,先打印 发短信,打电话?
 *
 */
public class Test4 {

    public static void main(String[] args) {
        //两个对象,两个调用者,两把锁
        //两个对象的类都是同一个,static,锁的是Class
        Phone4 phone1 = new Phone4();
        Phone4 phone2 = new Phone4();

        //锁的存在
        new Thread(() -> {
            Phone4.sendMsg();
        }, "A").start();
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(() -> {
            phone2.call();
        }, "B").start();
    }
}
class Phone4 {

//    静态的同步方法
    public static synchronized void sendMsg() {
        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("发短信");
    }
    //普通的同步方法
    public  synchronized void call() {
        System.out.println("打电话");
    }

}

小结

new this 具体的一个属性

static Class 唯一的一个模板

6.集合类不安全

List不安全

package com.lei.unsafe;

import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
//java.util.ConcurrentModificationException 并发修改异常!

/**

  • @ClassName ListTest

  • @Description: TODO

  • @Author 1689169874@qq.com

  • @Date 2020/7/24/024 0:04

  • @Version 1.0
    /
    public class ListTest {
    public static void main(String[] args) {
    //并发下,ArrayList 不是安全的
    /

    * 解决方案:
    * 1.List<String> list = new Vector<>();
    * 2.List<String> list = Collections.synchronizedList(new ArrayList<>());
    * 3.List<String> list = new CopyOnWriteArrayList<>();
    */
    //CopyOnWrite 写入是复刻, COW 计算机程序设计领域的一种优化策略
    // 多个线程调用的时候,list,读取的时候,固定的,写入(覆盖)
    //避免在写入的时候覆盖, 造成数据问题!
    // 读写分离 MyCat
    // CopyOnWriteArrayList比Vector好在哪里?

     List&lt;String&gt; list = new CopyOnWriteArrayList&lt;&gt;();
    
     for (int i = 0; i &lt; 10; i++) {
         new Thread(() -&gt; {
             list.add(UUID.randomUUID().toString().substring(0, 5));
             System.out.println(list);
         }, String.valueOf(i)).start();
     }
    

    }
    }

学习方法:1.先会用 2,寻找其他解决方案 3.分析源码

set不安全

package com.lei.unsafe;

import java.util.*;
import java.util.concurrent.CopyOnWriteArraySet;

/**

  • @ClassName SetTest
  • @Description: TODO
  • @Author 1689169874@qq.com
  • @Date 2020/7/24/024 0:22
  • @Version 1.0
    **/

/**

  • 同理可证: java.util.ConcurrentModificationException
  • 1.Set<String> set = Collections.synchronizedSet(new HashSet<>());
  • 2.Set<String> set = new CopyOnWriteArraySet<>();
    */
    public class SetTest {
    public static void main(String[] args) {
    // Set<String > set = new HashSet<>();
    // Set<String> set = Collections.synchronizedSet(new HashSet<>());
    Set<String> set = new CopyOnWriteArraySet<>();
    for (int i = 0; i < 100; i++) {
    new Thread(() -> {
    set.add(UUID.randomUUID().toString().substring(0,4));
    System.out.println(set);
    },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;
    }

// PRESENT是一个不变的值
private static final Object PRESENT = new Object();

HashMap

package com.lei.unsafe;

import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;

/**

  • @ClassName MapTest
  • @Description: TODO
  • @Author 1689169874@qq.com
  • @Date 2020/7/24/024 0:34
  • @Version 1.0
    **/

/**

  • java.util.ConcurrentModificationException

*/
public class MapTest {
public static void main(String[] args) {

// Map<String ,String> map = new HashMap<>();
Map<String ,String> map = new ConcurrentHashMap<>();

    //加载因子,初始化容量
    for (int i = 0; i &lt; 40; i++) {
        new Thread(() -&gt; {
            map.put(Thread.currentThread().getName(), UUID.randomUUID().toString().substring(0,4));
            System.out.println(map);
        }).start();
    }
}

}

7.Callable( 简单 )

类似于Runnable

1.可以有返回值

2.可以抛出异常

3.方法不同 run() /call()

代码测试

package com.lei.callable;

import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;

/**

  • @ClassName CallableTest
  • @Description: TODO
  • @Author 1689169874@qq.com
  • @Date 2020/7/24/024 9:53
  • @Version 1.0
    **/
    public class CallableTest {
    public static void main(String[] args) throws Exception {

// new Thread(new Runnable()).start(); //怎么启动Callable
// new Thread(new FutureTask<V>()).start(); //怎么启动Callable
// new Thread(new FutureTask<V>(Callable)).start(); //怎么启动Callable
// new Thread().start(); //怎么启动Callable
MyThread myThread = new MyThread();
FutureTask<Integer> futureTask = new FutureTask<>(myThread);
new Thread(futureTask,"A").start();
Integer o = futureTask.get();
System.out.println(o);

}

}
class MyThread implements Callable<Integer> {

@Override
public Integer call() {
    System.out.println(&quot;call&quot;);
    return 123;
}

}

细节

1.有缓存

2.结果可能需要等待,会阻塞

8.常用的辅助类

8.1、CountDownLatch

技术分享图片

代码:

package com.lei.add;

/**

  • @ClassName CountDownLatchDemo
  • @Description: TODO
  • @Author 1689169874@qq.com
  • @Date 2020/7/24/024 14:03
  • @Version 1.0
    **/

import java.util.concurrent.CountDownLatch;

/**

  • 计数器
    */
    public class CountDownLatchDemo {
    public static void main(String[] args) throws InterruptedException {
    //总数是6,必须是执行任务的时候使用
    CountDownLatch countDownLatch = new CountDownLatch(6);

     for (int i = 0; i &lt; 6; i++) {
         new Thread(() -&gt; {
             System.out.println(Thread.currentThread().getName()+&quot;Go out&quot;);
             countDownLatch.countDown();
         },String.valueOf(i)).start();
     }
     countDownLatch.await();// 等待计数器归零,然后再向下执行
    
     System.out.println(&quot;Close Door&quot;);
    

// countDownLatch.countDown(); // -1
}
}

原理:减法计数器

countDownLatch.countDown(); //数量-1

countDownLatch.await();// 等待计数器归零,然后再向下执行

每次有线程调用countDown()数量-1,假设计数器变为0,countDownLatch.await()就会被唤醒,继续执行

8.2、CyclicBarrier

加法计数器

技术分享图片代码:

package com.lei.add;

import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;

/**

  • @ClassName CyclicBarrierDemo
  • @Description: TODO
  • @Author 1689169874@qq.com
  • @Date 2020/7/24/024 14:17
  • @Version 1.0
    /
    public class CyclicBarrierDemo {
    public static void main(String[] args) {
    /

    * 集齐七颗龙珠
    */
    CyclicBarrier cyclicBarrier = new CyclicBarrier(7, () -> {
    System.out.println("召唤神龙成功");
    });
    for (int i = 0; i < 7; i++) {
    //lambda
    final int temp = i;
    new Thread(() -> {
    System.out.println(Thread.currentThread().getName()+"收集" +temp + "个龙珠");
    try {
    cyclicBarrier.await();
    } catch (InterruptedException e) {
    e.printStackTrace();
    } catch (BrokenBarrierException e) {
    e.printStackTrace();
    }
    }).start();
    }
    }
    }

 

8.3、Semaphore

Semaphore:信号量

技术分享图片

代码

package com.lei.add;

import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;

/**

  • @ClassName SemaphoreDemo

  • @Description: TODO

  • @Author 1689169874@qq.com

  • @Date 2020/7/24/024 14:23

  • @Version 1.0
    **/
    public class SemaphoreDemo {
    public static void main(String[] args) {
    //线程数量: 停车位
    Semaphore semaphore = new Semaphore(3);
    for (int i = 0; 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 {
                 semaphore.release();
             }
             //release() 释放
    
         },String.valueOf(i)).start();
     }
    

    }

}

原理:

技术分享图片

9.读写锁

ReadWriteLock

技术分享图片

测试:

package com.lei.rw;

/**

  • @ClassName ReadWriteLockDemo
  • @Description: TODO
  • @Author 1689169874@qq.com
  • @Date 2020/7/24/024 14:30
  • @Version 1.0
    **/

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**

  • ReadWriteLock
    */
    public class ReadWriteLockDemo {
    public static void main(String[] args) {
    MyCache2 myCache = new MyCache2();
    //写入
    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 &lt; 5; i++) {
         final int temp = i;
         new Thread(() -&gt; myCache.get(temp+&quot;&quot;),String.valueOf(i)).start();
     }
    

    }
    }

/**

  • 自定义缓存
    */
    class MyCache {
    private volatile Map<String, Object> map = new HashMap<>();

    //存,写
    public void put(String key, Object value) {
    System.out.println(Thread.currentThread().getName() + "写入" + key);
    map.put(key, value);
    System.out.println(Thread.currentThread().getName() + "写入ok");

    }
    // 取,写

    public void get(String key) {
    System.out.println(Thread.currentThread().getName() + "读取" + key);
    Object o = map.get(key);
    System.out.println(Thread.currentThread().getName() + "读取ok");
    }
    }
    /**

  • 自定义缓存 加锁
    */
    class MyCache2 {
    private volatile Map<String, Object> map = new HashMap<>();
    //读写锁,更加细腻的控制
    ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
    //写入的时候只希望同时只有一个线程写
    //存,写
    public void put(String key, Object value) {
    readWriteLock.writeLock().lock();
    System.out.println(Thread.currentThread().getName() + "写入" + key);
    map.put(key, value);
    System.out.println(Thread.currentThread().getName() + "写入ok");
    readWriteLock.writeLock().unlock();
    }
    // 取,写

    public void get(String key) {
    readWriteLock.readLock().lock();
    System.out.println(Thread.currentThread().getName() + "读取" + key);
    Object o = map.get(key);
    System.out.println(Thread.currentThread().getName() + "读取ok");
    readWriteLock.readLock().unlock();
    }
    }

10、阻塞队列

技术分享图片

BlockingQueue 不是新的东西

什么情况下我们会使用阻塞队列:多线程,线程池

技术分享图片

学会使用队列

添加,移除

四组API

方式抛出异常不会抛出异常,有返回值阻塞 等待超时等待
添加addoffer()put()offer( , )
移除removepoll()take()poll( , )
检测队首元素element()peek  
package com.lei.bq;

import com.lei.lock8.Test1;

import java.util.ArrayList;
import java.util.Collection;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.TimeUnit;

/**

  • @ClassName BlockingQueueDemo

  • @Description: TODO

  • @Author 1689169874@qq.com

  • @Date 2020/7/24/024 15:00

  • @Version 1.0
    **/
    public class BlockingQueueDemo {
    public static void main(String[] args) throws InterruptedException {
    // test1();
    // test2();
    // test3();
    test4();
    }

    /**

    • 抛出异常
      */
      public static void test1() {
      //队列的大小
      ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);
      System.out.println(blockingQueue.add("q"));
      System.out.println(blockingQueue.add("b"));
      System.out.println(blockingQueue.add("c"));
      System.out.println("===========");
      System.out.println(blockingQueue.remove());
      System.out.println(blockingQueue.remove());
      System.out.println(blockingQueue.remove());
      }

    /**

    • 不抛出异常,有返回值
      */
      public static void test2() {
      ArrayBlockingQueue arrayBlockingQueue = new ArrayBlockingQueue<>(3);
      System.out.println(arrayBlockingQueue.offer("a"));
      System.out.println(arrayBlockingQueue.offer("b"));
      System.out.println(arrayBlockingQueue.offer("c"));
      System.out.println(arrayBlockingQueue.offer("e")); //false 不抛出异常
      System.out.println("=============");
      System.out.println(arrayBlockingQueue.poll());
      System.out.println(arrayBlockingQueue.element());
      System.out.println(arrayBlockingQueue.poll());
      System.out.println(arrayBlockingQueue.poll());
      System.out.println(arrayBlockingQueue.poll()); // null 不抛出异常
      }

    /**

    • 等待,阻塞 (一直)

    */
    public static void test3() throws InterruptedException {
    ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);
    //一直阻塞
    blockingQueue.put("a");
    blockingQueue.put("b");
    blockingQueue.put("c");

     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("a");
    blockingQueue.offer("a");
    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));

    }
    }

SynchronizeQueue 同步队列>

没有容量,

进去一个元素,必须等待取出来后,才能再往里面放一个元素

put take

package com.lei.bq;

/**

  • @ClassName SynchronizeQueueDemo
  • @Description: TODO
  • @Author 1689169874@qq.com
  • @Date 2020/7/24/024 16:29
  • @Version 1.0
    **/

import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.TimeUnit;

/**

  • 同步队列
    */
    public class SynchronizeQueueDemo {
    public static void main(String[] args) {
    SynchronousQueue<String> 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(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();
         }
     },&quot;T2&quot;).start();
    

    }
    }

11.线程池(重点)

线程池:三大方法,七大参数,四种拒绝策略

程序运行的本质: 占用系统的资源,优化资源的使用 =》池化技术

线程池:内存池,对象池

池化技术: 事先准备好资源,有人要用,就来我这里拿,用完之后还给我。

线程池的好处:

  1. 降低资源的消耗
  2. 提高响应的速度
  3. 方便管理

==线程可以复用,可以控制最大并发数,管理线程==

线程池必会技术,

技术分享图片

三大方法

package com.lei.pool;

import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**

  • @ClassName Demo1
  • @Description: TODO
  • @Author 1689169874@qq.com
  • @Date 2020/7/24/024 16:46
  • @Version 1.0
    **/
    public class Demo1 {
    public static void main(String[] args) {
    // ExecutorService executorService = Executors.newSingleThreadExecutor();//单个线程
    //// Executors.newFixedThreadPool(5); //单个线程
    //// Executors.newCachedThreadPool(); //单个线程
    // for (int i = 0; i < 10; i++) {
    // new Thread().start();
    // }
    ExecutorService service = Executors.newSingleThreadExecutor();
    for (int i = 0; i < 10; i++) {
    service.execute(
    () -> {
    // ... do something inside runnable task
    System.out.println(Thread.currentThread().getName()+" ok");
    });
    }
    service.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>());
}
//本质 :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;
}

技术分享图片

手动创建一个线程池

拒绝策略:

技术分享图片

四种拒绝策略

package com.lei.pool;

import java.util.concurrent.*;

/**

  • @ClassName Demo1
  • @Description: TODO
  • @Author 1689169874@qq.com
  • @Date 2020/7/24/024 16:46
  • @Version 1.0
    **/
    public class Demo1 {
    public static void main(String[] args) {
    //自定义线程池 ! 工作中常用
    ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
    2,
    5,
    3,
    TimeUnit.SECONDS,
    new LinkedBlockingQueue<>(3),
    Executors.defaultThreadFactory(),
    // new ThreadPoolExecutor.AbortPolicy()); //队列满了,抛出异常
    // new ThreadPoolExecutor.CallerRunsPolicy()); //哪里来的回哪
    // new ThreadPoolExecutor.DiscardPolicy());//队列满了,丢掉任务,不会抛出异常
    new ThreadPoolExecutor.DiscardOldestPolicy());//队列满了,尝试竞争,不会抛出异常
    //线程满了,还有人进来,不处理这个人的业务,抛出异常
    //最大承载:Deque + max
    //java.util.concurrent.RejectedExecutionException 超出
    for (int i = 1; i <= 9 ; i++) {
    threadPoolExecutor.execute(() -> {
    System.out.println(Thread.currentThread().getName()+"ok");
    });
    }
    threadPoolExecutor.shutdown();
    }

}

小结和拓展

了解:CPU 密集型 IO密集型

//最大线程该如何定义
        //1.CPU 密集型  几核,就是几,可以保持CPU的效率更高
//        2.IO 密集型
//            程序 15个大型任务线程,io十分占用资源
        //获取CPU的核数
//        System.out.println(Runtime.getRuntime().availableProcessors());

 

12、四大函数式接口(必须掌握)

1.函数式接口:只有一个方法的接口

@FunctionInterface

代码测试:

技术分享图片

代码:

package com.lei.function;

import com.sun.org.apache.bcel.internal.generic.RET;

import java.util.function.Function;

/**

  • @ClassName Demo01
  • @Description: TODO
  • @Author 1689169874@qq.com
  • @Date 2020/7/24/024 20:02
  • @Version 1.0
    **/
    public class Demo01 {
    public static void main(String[] args) {
    //工具类,输出输入的值
    // Function function = new Function<String,String>() {
    // @Override
    // public String apply(String str) {
    // return str;
    // }
    // };
    Function function = (str) -> {return str;};
    System.out.println(function.apply("123"));
    }
    }

断定型接口

package com.lei.function;

import java.util.function.Predicate;

/**

  • @ClassName Demo02
  • @Description: TODO
  • @Author 1689169874@qq.com
  • @Date 2020/7/24/024 20:25
  • @Version 1.0
    **/
    public class Demo02 {
    public static void main(String[] args) {
    //判断字符串是否为空
    // Predicate<String> predicate = new Predicate<String>() {
    // @Override
    // public boolean test(String s) {
    // return s.isEmpty();
    // }
    // };
    Predicate<String > predicate = (str) -> str.isEmpty();
    System.out.println(predicate.test("123"));;
    }
    }

Consumer 消费型接口

package com.lei.function;

import java.sql.Connection;
import java.util.function.Consumer;

/**

  • @ClassName Demo03
  • @Description: TODO
  • @Author 1689169874@qq.com
  • @Date 2020/7/24/024 21:27
  • @Version 1.0
    **/
    //消费性接口,只有输入,没有返回值
    public class Demo03 {
    public static void main(String[] args) {
    // Consumer<String > consumer = new Consumer<String>() {
    // @Override
    // public void accept(String str) {
    // System.out.println(str);
    // }
    // };
    Consumer<String > consumer = (str) -> System.out.println(str);
    consumer.accept("1234");
    }

}

Supplier 供给型接口

package com.lei.function;

import java.util.function.Supplier;

/**

  • @ClassName Demo04
  • @Description: TODO
  • @Author 1689169874@qq.com
  • @Date 2020/7/24/024 21:57
  • @Version 1.0
    **/
    public class Demo04 {
    public static void main(String[] args) {
    // Supplier supplier = new Supplier<Integer>() {
    // @Override
    // public Integer get() {
    // System.out.println("1234");
    // return 1234;
    // }
    // };
    Supplier supplier = () -> {return 12345;};
    System.out.println(supplier.get());
    }
    }

13、Stream流式计算

什么是Stream流式计算

大数据: 存储+计算

集合、MySQL本质就是存储东西的

计算都应该交给流来操作!

package com.lei.stream;

import java.util.Arrays;
import java.util.List;

/**

  • @ClassName Test
  • @Description: TODO
  • @Author 1689169874@qq.com
  • @Date 2020/7/24/024 22:41
  • @Version 1.0
    **/
    public class Test {
    public static void main(String[] args) {
    User user1 = new User(1,"a",19);
    User user2 = new User(2,"b",20);
    User user3 = new User(3,"dsag",22);
    User user4 = new User(4,"adsf",21);
    //集合就是存储
    List<User> userList = Arrays.asList(user1, user2, user3, user4);
    //计算交给Stream流
    userList.stream().filter(u -> {return u.getId()%2==0;}).
    filter(u -> {return u.getAge()>20;}).
    map(user->{return user.getName().toUpperCase();})
    .sorted((uu1,uu2) -> {return uu2.compareTo(uu1);})
    .limit(1).
    forEach(System.out::println);
    }
    }

14、Forkjoin

分支合并

什么是ForkJoin?

ForkJoin在JDK1.7,并行执行任务,提高效率,大数据量!

大数据: Map Reduce(把大任务拆分为小任务)

技术分享图片

ForkJoin特点:工作窃取

技术分享图片

技术分享图片

ForkJoin

package com.lei.forkjoin;

/**

  • @ClassName ForkJoinDemo
  • @Description: TODO
  • @Author 1689169874@qq.com
  • @Date 2020/7/27/027 23:43
  • @Version 1.0
    **/

import java.util.concurrent.RecursiveTask;

/**

  • 求和计算的任务:

  • 3000 6000(ForkJoin) 9000(Stream并行流水)

  • 如何使用forkjoin

  • 1、forkjoinpool

  • 2、计算任务forkjoinpool.execute(ForkJoinTask <task>)

  • 3、计算类要继承我们的ForkJoinTask
    */
    public class ForkJoinDemo extends RecursiveTask<Long> {
    private Long start;
    private Long end;

    public ForkJoinDemo(Long start, Long end) {
    this.start = start;
    this.end = end;
    }

    //临界值
    private Long temp = 10000L;

    //计算方法
    @Override
    protected Long compute() {

     if ((end - start) &gt; temp) {
         //分支合并计算
         Long sum = 0L;
         for (Long i = start; i &lt; end; i++) {
             sum += i;
         }
         return sum;
     } else { //ForkJoin
         Long middle = (start - end) / 2;  //中间值
         ForkJoinDemo forkJoinDemo = new ForkJoinDemo(start, middle);
         forkJoinDemo.fork();  //拆分任务,把任务压入线程队列
         ForkJoinDemo forkJoinDemo1 = new ForkJoinDemo(middle + 1, end);
         forkJoinDemo1.fork();    //拆分任务,把任务压入线程队列
         return forkJoinDemo1.join() + forkJoinDemo.join();
    
     }
    

    }
    }

 

 

 

测试:

package com.lei.forkjoin;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
import java.util.stream.LongStream;

/**

  • @ClassName Test

  • @Description: TODO

  • @Author 1689169874@qq.com

  • @Date 2020/7/28/028 12:55

  • @Version 1.0
    **/
    public class Test {
    public static void main(String[] args) throws Exception {
    // test1(); //7.463s
    // test2(); //
    test3();

    }
    //普通程序员
    public static void test1(){
    long start = System.currentTimeMillis();
    Long sum = 0L;
    for (Long i = 0L; i < 10_0000_0000; i++) {
    sum+=i;
    }
    long end = System.currentTimeMillis();
    System.out.println(sum);
    System.out.println("耗时:"+((end-start) * 0.001) + "s");
    }

    //会使用ForkJoin
    public static void test2() throws Exception{

     long start = System.currentTimeMillis();
     ForkJoinPool forkJoinPool = new ForkJoinPool();
     ForkJoinDemo task = new ForkJoinDemo(0L, 10_0000_0000L);
     ForkJoinTask&lt;Long&gt; submit = forkJoinPool.submit(task);// 提交任务
     Long sum = submit.get();
    
    
    
     long end = System.currentTimeMillis();
     System.out.println(sum);
     System.out.println(&quot;耗时:&quot;+((end-start) * 0.001) + &quot;s&quot;);
    

    }

    public static void test3(){
    long start = System.currentTimeMillis();
    //使用stream流
    long sum = LongStream.rangeClosed(0L, 10_0000_0000L).parallel().reduce(0, Long::sum);
    long end = System.currentTimeMillis();
    System.out.println(sum);
    System.out.println("耗时:"+(end-start) * 0.001 + "s");
    }
    }

15、异步回调

Future 设计的初衷: 对将来某个事件的结果进行建模

技术分享图片

package com.lei.future;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

/**

  • @ClassName Demo01

  • @Description: 异步调用

  • // 异步执行

  • // 成功回调

  • // 失败回调

  • @Author 1689169874@qq.com

  • @Date 2020/7/28/028 13:16

  • @Version 1.0
    **/
    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());
    // });
    // System.out.println("111");
    // completableFuture.get();// 获取阻塞执行结果

     //有返回值的supplyAsync 异步回调
     //ajax,成功和失败的回调
     // 返回的是错误信息
     CompletableFuture&lt;Integer&gt; completableFuture = CompletableFuture.supplyAsync(() -&gt; {
         System.out.println(&quot;completableFuture&quot;);
         int i = 10/0;
         return  1024;
     });
     completableFuture.whenComplete((t,u) -&gt; {
         System.out.println(&quot;t-&gt;&quot;+t);  //正常的返回结果
    
         System.out.println(&quot;u-&gt;&quot;+u); // 失败的返回结果
     }).exceptionally((e) -&gt; {
         System.out.println(e.getMessage());
         return 1234;
     }).get();
    

    }
    }

16、JMM

请你谈谈你对Volatile的理解

Volatile是Java虚拟机提供的轻量级的同步机制

  1. 保证可见性
  2. ==不保证原子性==
  3. 禁止指令重排

什么是JMM

JMM : Java内存模型,不存在的东西,只是一个概念和约定

关于JMM的一些同步的约定

  1. 线程解锁前,必须把共享变量==立刻==刷回主存
  2. 线程加锁前,必须读取主存中的最新值到工作内存中
  3. 加锁和解锁是通一把锁

内存交互操作有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操作之前,必须把此变量同步回主内存

问题:程序不知道主内存的值被修改了

17、Volatile

1、保证可见性

package com.lei.jmm;

import java.util.concurrent.TimeUnit;

/**

  • @ClassName JMMDemo

  • @Description: TODO

  • @Author 1689169874@qq.com

  • @Date 2020/7/28/028 19:54

  • @Version 1.0
    **/
    public class JMMDemo {
    //不加 Volatile 程序就会死循环!
    // 加 Volatile 可以保证可见性
    private volatile static int num = 0;

    public static void main(String[] args) {
    new Thread(() -> { // 线程1对主内存的变化是不知道的
    while (num == 0){

         }
     }).start();
     try {
         TimeUnit.SECONDS.sleep(1);
     } catch (InterruptedException e) {
         e.printStackTrace();
     }
     num = 1;
     System.out.println(num);
    

    }

}

2、不保证原子性

原子性: 不可分割

线程A在执行任务时,是不能被打扰的,也不能被分割,要么同时成功,要么同时失败

package com.lei.tvolatile;

/**

  • @ClassName volatileDemo

  • @Description: TODO

  • @Author 1689169874@qq.com

  • @Date 2020/7/28/028 20:06

  • @Version 1.0
    **/
    public class volatileDemo {
    // volatile不保证原子性
    private volatile static int num = 0;
    public static void add() {
    num++;
    }

    public static void main(String[] args) {
    //理论上 num 的结果应该为 2万
    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(Thread.currentThread().getName() + " " + num);
    }
    }

如果不加lock和synchronized ,怎么样保证原子性

技术分享图片

使用原子类,解决原子性问题

package com.lei.future;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

/**

  • @ClassName Demo01

  • @Description: 异步调用

  • // 异步执行

  • // 成功回调

  • // 失败回调

  • @Author 1689169874@qq.com

  • @Date 2020/7/28/028 13:16

  • @Version 1.0
    **/
    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());
    // });
    // System.out.println("111");
    // completableFuture.get();// 获取阻塞执行结果

     //有返回值的supplyAsync 异步回调
     //ajax,成功和失败的回调
     // 返回的是错误信息
     CompletableFuture&lt;Integer&gt; completableFuture = CompletableFuture.supplyAsync(() -&gt; {
         System.out.println(&quot;completableFuture&quot;);
         int i = 10/0;
         return  1024;
     });
     completableFuture.whenComplete((t,u) -&gt; {
         System.out.println(&quot;t-&gt;&quot;+t);  //正常的返回结果
    
         System.out.println(&quot;u-&gt;&quot;+u); // 失败的返回结果
     }).exceptionally((e) -&gt; {
         System.out.println(e.getMessage());
         return 1234;
     }).get();
    

    }
    }

 

18、彻底玩转单例模式

??饿汉式

package com.lei.single;

/**

  • @ClassName Hungry

  • @Description: 饿汉式单例

  • @Author 1689169874@qq.com

  • @Date 2020/7/31/031 13:57

  • @Version 1.0
    **/
    public class Hungry {
    //可能会命令浪费空间
    private byte[] data1 = new byte[10241024];
    private byte[] data2 = new byte[1024
    1024];
    private byte[] data3 = new byte[10241024];
    private byte[] data4 = new byte[1024
    1024];
    private Hungry(){

    }
    private final static Hungry HUNGRY = new Hungry();
    public static Hungry getInstance(){
    return HUNGRY;
    }
    }

??DCL懒汉式

package com.lei.single;

import java.lang.reflect.Constructor;

/**

  • @ClassName LazyMan

  • @Description: TODO

  • @Author 1689169874@qq.com

  • @Date 2020/7/31/031 14:05

  • @Version 1.0
    /
    public class LazyMan {
    private LazyMan(){
    System.out.println(Thread.currentThread().getName() + "ok!");
    }
    private volatile static LazyMan lazyMan;
    public static LazyMan getInstance(){
    if (lazyMannull){
    synchronized (LazyMan.class){
    if (lazyMan
    null){
    lazyMan = new LazyMan(); //不是原子性操作
    /

    * 1、分配内存空间
    * 2、执行构造方法,初始化对象
    * 3、把这个对象指向这个空间
    */
    }
    }
    }

     return lazyMan;
    

    }
    //以上代码单线程下可以
    //多线程并发
    // public static void main(String[] args) {
    // for (int i = 0; i < 10; i++) {
    // new Thread(() -> {
    // LazyMan.getInstance();
    // }).start();
    // }
    // }
    public static void main(String[] args) throws Exception {
    LazyMan instance = LazyMan.getInstance();
    Constructor<LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor(null);
    LazyMan lazyMan = declaredConstructor.newInstance();
    System.out.println(instance);
    System.out.println(lazyMan);
    }
    }

??静态内部类

package com.lei.single;

/**

  • @ClassName Holder
  • @Description: TODO
  • @Author 1689169874@qq.com
  • @Date 2020/7/31/031 14:17
  • @Version 1.0
    **/

import com.sun.org.apache.bcel.internal.classfile.InnerClass;

/**

  • 静态内部类懒汉式
    */
    public class Holder {
    private Holder() {

    }

    public static Holder getInstance() {
    return InnerClass.HOLDER;
    }

    public static class InnerClass {
    private static final Holder HOLDER = new Holder();
    }
    }

单例不安全,因为有反射

枚举:

package com.lei.single;

import javax.sql.ConnectionPoolDataSource;
import java.lang.reflect.Constructor;

/**

  • @ClassName EnumSingle
  • @Description: TODO
  • @Author 1689169874@qq.com
  • @Date 2020/7/31/031 14:27
  • @Version 1.0
    **/
    // emum是什么? 本身也是一个class类
    public enum EnumSingle {
    INSTANCE;
    public EnumSingle getInstance(){
    return INSTANCE;
    }

}
class Test {
public static void main(String[] args) throws Exception{
EnumSingle ins1 = EnumSingle.INSTANCE;;
Constructor<EnumSingle> constructor = EnumSingle.class.getDeclaredConstructor(String.class,int.class);
constructor.setAccessible(true);
EnumSingle enumSingle = constructor.newInstance();

    System.out.println(ins1);
    System.out.println(enumSingle);
}

}

19、深入理解CAS

什么是CAS

大厂必须深入研究底层

package com.lei.CAS;

import java.util.concurrent.atomic.AtomicInteger;

/**

  • @ClassName Demo

  • @Description: TODO

  • @Author 1689169874@qq.com

  • @Date 2020/7/31/031 14:41

  • @Version 1.0
    **/
    public class Demo {
    //CAS compareAndSet : 比较并交换
    public static void main(String[] args) {
    AtomicInteger au = new AtomicInteger(2020);
    au.compareAndSet(2020,2021);
    System.out.println(au.get());

    }
    }

Unsafe类

技术分享图片

技术分享图片

技术分享图片

CAS:比较当前工作中的值和主内存中的值,如果这个值是期望的,那么执行操作,如果不是就一直循环

缺点

  1. 循环耗时
  2. 一次性只能保证一个共享变量的原子性
  3. ABA问题

 

CAS: ABA问题(狸猫换太子)

技术分享图片

package com.lei.CAS;

import java.util.concurrent.atomic.AtomicInteger;

/**

  • @ClassName Demo

  • @Description: TODO

  • @Author 1689169874@qq.com

  • @Date 2020/7/31/031 14:41

  • @Version 1.0
    **/
    public class Demo {
    //CAS compareAndSet : 比较并交换
    public static void main(String[] args) {
    AtomicInteger au = new AtomicInteger(2020);
    boolean set = au.compareAndSet(2020, 2021);
    System.out.println(au.get());

     boolean set1 = au.compareAndSet(2021, 2020);
     System.out.println(au.get());
    
     boolean set2 = au.compareAndSet(2020, 9999);
     System.out.println(au.get());
    

    }
    }

20、原子引用

技术分享图片

package com.lei.CAS;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicStampedReference;

/**

  • @ClassName Demo

  • @Description: TODO

  • @Author 1689169874@qq.com

  • @Date 2020/7/31/031 14:41

  • @Version 1.0
    **/
    public class Demo {
    //CAS compareAndSet : 比较并交换
    public static void main(String[] args) {
    // AtomicInteger au = new AtomicInteger(2020);
    AtomicStampedReference<Integer> au = new AtomicStampedReference<>(1, 1);
    new Thread(() -> {
    int stamp = au.getStamp(); // 获得版本号
    System.out.println("a1-> "+ stamp);
    try {
    TimeUnit.SECONDS.sleep(2);
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    au.compareAndSet(1,2,au.getStamp(),au.getStamp()+1);
    System.out.println("a2-> "+ au.getStamp());

       au.compareAndSet(2,1,au.getStamp(),au.getStamp()+1);
       System.out.println(&quot;a3-&gt; &quot;+ au.getStamp());
    

    },"a").start();

     new Thread(() -&gt; {
         int stamp = au.getStamp(); // 获得版本号
         System.out.println(&quot;b1-&gt;&quot; + stamp);
         try {
             TimeUnit.SECONDS.sleep(2);
         } catch (InterruptedException e) {
             e.printStackTrace();
         }
         au.compareAndSet(2,3,stamp, stamp+1);
     },&quot;b&quot;).start();  //此处修改失败
     System.out.println(&quot;b2-&gt;&quot; + au.getStamp());
    

    }
    }

21、各种锁的理解

1、公平锁、非公平锁

公平锁: 不能插队,公平

不公平锁: 可以插队,不公平 (默认都是非公平)

技术分享图片

 

2、可重入锁

synchronize

package com.lei.lock;

import sun.awt.geom.AreaOp;

import java.time.LocalDate;

/**

  • @ClassName Demo

  • @Description: TODO

  • @Author 1689169874@qq.com

  • @Date 2020/7/31/031 15:18

  • @Version 1.0
    **/
    public class Demo {
    public static void main(String[] args) {
    Phone phone = new Phone();
    new Thread(()->{
    phone.sms();
    },"A").start();

     new Thread(() -&gt; {
         phone.sms();
     },&quot;B&quot;).start();
    

    }
    }
    class Phone{

    public synchronized void sms(){
    System.out.println(Thread.currentThread().getName() + "sms");
    call();
    }

    public void call(){
    System.out.println(Thread.currentThread().getName() + "call");
    }
    }

Lock版

package com.lei.lock;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**

  • @ClassName Demo2

  • @Description: TODO

  • @Author 1689169874@qq.com

  • @Date 2020/7/31/031 15:22

  • @Version 1.0
    **/
    public class Demo2 {
    public static void main(String[] args) {
    Phone2 phone = new Phone2();
    new Thread(() -> {
    phone.sms();
    }, "A").start();

     new Thread(() -&gt; {
         phone.sms();
     }, &quot;B&quot;).start();
    

    }
    }

class Phone2 {
Lock lock;

{
    lock = new ReentrantLock();
}

public synchronized void sms() {
    lock.lock();
    try {
        System.out.println(Thread.currentThread().getName() + &quot;sms&quot;);
        call();  //第二把锁
        //lock 锁必须配对,不然会死锁
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        lock.unlock();
    }
}

public void call() {
    lock.lock();
    try {
        System.out.println(Thread.currentThread().getName() + &quot;call&quot;);
        call();
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        lock.unlock();
    }
}

}

3、自旋锁

技术分享图片

package com.lei.lock;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;

/**

  • @ClassName TestSpinLock

  • @Description: TODO

  • @Author 1689169874@qq.com

  • @Date 2020/7/31/031 15:35

  • @Version 1.0
    **/
    public class TestSpinLock {
    public static void main(String[] args) throws InterruptedException {
    // ReentrantLock lock = new ReentrantLock();
    // lock.lock();
    // lock.unlock();

     //底层使用自旋锁CAS
     SpinlockDemo spinlockDemo = new SpinlockDemo();
     new Thread(() -&gt; {
         spinlockDemo.myLock();
         try {
             TimeUnit.SECONDS.sleep(3);
         } catch (Exception e) {
             e.printStackTrace();
         } finally {
             spinlockDemo.myUnlock();
         }
     },&quot;a1&quot;).start();
     TimeUnit.SECONDS.sleep(1);
     new Thread(() -&gt; {
         //线程2开始自旋
         spinlockDemo.myLock();
         try {
             TimeUnit.SECONDS.sleep(3);
         } catch (Exception e) {
             e.printStackTrace();
         } finally {
             spinlockDemo.myUnlock();
         }
     },&quot;a2&quot;).start();
    

    }

}

4、死锁

技术分享图片

解决问题

1、JPS定位进程号

技术分享图片

2、查看进程信息 jstack 找到死锁问题

 

 

Java线程进阶知识-面试必备

原文:https://www.cnblogs.com/flashdiko/p/13749064.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!