多线程


1.线程创建的三种方式

  • 继承Thread类
  • 实现Runnable接口
  • 实现Callable接口

1.1 创建线程方式① 继承Thread类

  1. 继承Thread类
  2. 重写run方法
  3. 创建线程对象
  4. 线程对象调用star()方法执行
/** 创建线程方式1 实现多线程图片下载 需要导入 commons-io-2.11.0.jar */
public class TestThread2 extends Thread {
    @Override
    public void run() {
        WebDownload webDownload = new WebDownload();
        webDownload.downLoader(url,fileName);
        System.out.println("下载图片:"+fileName);
    }
    private String url;
    private String fileName;

    public TestThread2(String url,String fileName){
        this.url = url;
        this.fileName = fileName;

    }
    public static void main(String[] args) {
        TestThread2 testThread1 = new TestThread2("https://img-pre.ivsky.com/img/tupian/pre/202103/07/xuejing-004.jpg","图片1.jpg");
        TestThread2 testThread2 = new TestThread2("https://img-pre.ivsky.com/img/tupian/pre/202103/07/xuejing-005.jpg","图片2.jpg");
        TestThread2 testThread3 = new TestThread2("https://img-pre.ivsky.com/img/tupian/pre/202103/07/xuejing-006.jpg","图片3.jpg");
        //线程对象调用star()方法执行
        testThread1.start();
        testThread2.start();
        testThread3.start();
    }
}
class WebDownload{
    public  void downLoader(String url, String fileName) {
        try {
            FileUtils.copyURLToFile(new URL(url),new File(fileName));
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("IO异常,downLoad方法出现问题!");
        }
    }
}

1.2 创建线程方式② Runnable接口

  1. 实现Runnable接口
  2. 重写run()方法
  3. 创建Thread对象
  4. 将线程对象传递给Thread对象代理执行
  5. 由Thread对象调用start()执行
/** 创建线程方式二 实现Runnable接口 龟兔赛跑 */
public class Race implements Runnable {
    private static String Winner;
    @Override
    public void run() {
        for (int i = 1; i <= 100; i++) {
            if (Thread.currentThread().getName().equals("兔子") && i%10==0){
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println(Thread.currentThread().getName()+"---->走了第"+i+"步");
            boolean flag = GameOver(i);
            if (flag){return;}
        }
    }

    public static void main(String[] args) {
        Race race = new Race();
        //将线程对象传递给Thread对象代理执行
        new Thread(race,"兔子").start();
        new Thread(race,"乌龟").start();
    }
    private boolean GameOver(int steps){
        if (Winner!=null){
            return true;
        }
        else {
            if (steps>=100){
                Winner = Thread.currentThread().getName();
                System.out.println(Winner+"is winner!");
                return true;
            }
        }
        return false;
    }
}

区别:通过继承Thread类创建线程,受到了单继承的制约。实现Runnable接口更加灵活

1.3 创建线程方式③ 实现Callable接口

  1. 实现Callable接口
  2. 重写call()方法
  3. 创建线程对象
  4. 创建执行服务(线程池)
  5. 提交执行(返回Future对象)
  6. 关闭服务
/** 线程创建方式3:实现callable接口 */
public class ThreadCallable implements Callable<Boolean> {
    private String url;
    private String fileName;

    public ThreadCallable(String url,String fileName){
        this.url = url;
        this.fileName = fileName;
    }
    @Override
    public Boolean call(){
        WebDownload webDownload = new WebDownload();
        webDownload.downLoader(url,fileName);
        System.out.println("下载图片:"+fileName);
        return true;
    }
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        ThreadCallable t1 = new ThreadCallable("https://img-pre.ivsky.com/img/tupian/pre/202103/07/xuejing-004.jpg","图片1.jpg");
        ThreadCallable t2 = new ThreadCallable("https://img-pre.ivsky.com/img/tupian/pre/202103/07/xuejing-005.jpg","图片2.jpg");
        ThreadCallable t3 = new ThreadCallable("https://img-pre.ivsky.com/img/tupian/pre/202103/07/xuejing-006.jpg","图片3.jpg");
        //创建执行服务
        ExecutorService ser = Executors.newFixedThreadPool(3);
        //提交执行
        Future<Boolean> r1 = ser.submit(t1);
        Future<Boolean> r2 = ser.submit(t2);
        Future<Boolean> r3 = ser.submit(t3);
        //获取结果
        boolean rs1 = r1.get();
        boolean rs2 = r1.get();
        boolean rs3 = r1.get();
        //关闭服务
        ser.shutdownNow();
    }
}
class WebDownload{
    public  void downLoader(String url, String fileName) {
        try {
            FileUtils.copyURLToFile(new URL(url),new File(fileName));
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("IO异常,downLoad方法出现问题!");
        }
    }
}

1.31 实现Runnable接口 和 Callable接口的区别:

Runnable 接口不会返回结果或抛出检查异常,但是Callable 接口可以。所以,如果任务不需要返回结果或抛出异常推荐使用 Runnable 接口

1.32 执行 execute()方法和 submit()方法的区别?

  1. execute()方法用于提交不需要返回值的任务,所以无法判断任务是否被线程池执行成功与否;
  2. submit()方法用于提交需要返回值的任务。线程池会返回一个 Future 类型的对象,通过这个 Future 对象可以判断任务是否执行成功,并且可以通过 Futureget()方法来获取返回值,get()方法会阻塞当前线程直到任务完成,而使用 get(long timeout,TimeUnit unit)方法则会阻塞当前线程一段时间后立即返回,这时候有可能任务没有执行完。

1.4 wait()和sleep()的区别?

  1. 所属的类不同:wait —> Object sleep —>Thread
  2. 释放锁: wait —> 释放锁(最好的等待是说放开) sleep —> 不释放锁(抱着锁睡)
  3. 使用的范围不同:wait —> 只能在同步代码块内 sleep —> 哪儿都能用
  4. 是否需要捕获异常: wait —> 不需要捕获异常 sleep —> 需要捕获异常

1.5 Lock锁和synchronied锁有什么区别?

  1. synchronied是Java内置关键字,Lock是Java类
  2. synchronied不能获取锁的状态,Lock可以获取锁的状态
  3. synchronied会自动释放锁,Lock需要手动释放锁,如果不释放—>死锁
  4. synchronied可重入锁,不可中断,非公平,Lock可重入锁,可判断所哦哦,非公平(可设置)
  5. synchronied 线程1(获取,阻塞)—->线程2(等待,傻傻的等),Lock锁不会一直等—>Lock.try();
  6. synchronied适合锁少量的同步代码,Lock适合锁大量的同步代码

1.6 锁的虚假唤醒

等待没有写到while里面,例如wait()语句写在了if语句里面,这样假设存在,A、B、C、D线程就会导致虚假唤醒。避免虚假唤醒—>使用while代替if;因为当其他线程调用notifyAll的时候,由于在while语句内,所以它还会在进行判断是否满足条件。而如果使用的if则当其他线程调用notifyAll的时候其他线程会继续往下执行,因为if只判断一次

// synchronized + wait() + notifyAll();
public class A {
    public static void main(String[] args) {
        Data data = new Data();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    data.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"A").start();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    data.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"D").start();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    data.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"B").start();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    data.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"C").start();
    }
}
class  Data{
    //判断等待--》业务--》通知
    private  int number = 0;
    public synchronized void  increment() throws InterruptedException {
        //等待
        while (number!=0){
            this.wait();
        }
        number++;
        System.out.println(Thread.currentThread().getName()+"===>"+number);
        //通知
        this.notifyAll();
    }
    public synchronized void  decrement() throws InterruptedException {
        //等待
        while (number==0){
            this.wait();
        }
        number--;
        System.out.println(Thread.currentThread().getName()+"===>"+number);
        //通知
        this.notifyAll();
    }
}

JUC版 生产者和消费者问题

// JUC ---- Lock + await() + signalAll();
public class B {
    public static void main(String[] args) {
        Data2 data = new Data2();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                data.increment();
            }
        },"A").start();

        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                data.increment();
            }
        },"B").start();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                data.decrement();
            }
        },"C").start();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                data.decrement();
            }
        },"D").start();

    }
}

class  Data2{
    //判断等待--》业务--》通知
    private  int number = 0;
    Lock lock = new ReentrantLock();
    Condition condition = lock.newCondition();
    public  void  increment()  {
        lock.lock();
        try {
            //等待
            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  decrement() {
        lock.lock();
        try {
            //等待
            while (number==0){
                condition.await();
            }
            number--;
            System.out.println(Thread.currentThread().getName()+"===>"+number);
            //通知
            condition.signalAll();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }

    }
}

image-20211008144455834

解决—>指定唤醒:

public class C {
    public static void main(String[] args) {
        Data3 data = new Data3();
        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  Data3{
    //判断等待--》业务--》通知
    private  int number = 1;
    Lock lock = new ReentrantLock();
    Condition condition = lock.newCondition();
    Condition condition1 = lock.newCondition();
    Condition condition2 = lock.newCondition();
    public void printA(){
        lock.lock();
        try {
            while (number!=1){
                 //该方法拥有的监视器为condition
                condition.await();
            }
            System.out.println(Thread.currentThread().getName()+"正在执行-----》AAAAAA");
            number = 2;
             //指定唤醒监视器
            condition1.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }

    }
    public void printB(){
        lock.lock();
        try {
            while (number!=2){
                //该方法拥有的监视器为condition1
                condition1.await();
            }
            System.out.println(Thread.currentThread().getName()+"正在执行-----》BBBBBB");
            number = 3;
             //指定唤醒监视器
            condition2.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }

    }
    public void printC(){
        lock.lock();
        try {
            while (number!=3){
                //该方法拥有的监视器为condition2
                condition2.await();
            }
            System.out.println(Thread.currentThread().getName()+"正在执行-----》CCCCCC");
            number = 1;
             //指定唤醒监视器
            condition.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }

    }
}

1.7 8锁现象

  • new/this 锁的是用的对象
  • Static 锁的是Class模板

顺序执行:谁先调用先执行谁

随机执行:没有规律,与计算机硬件资源有关,哪个线程先得到资源就先执行,各个线程之间互不干扰。

对此锁的问题,我们先判断是不是同一把锁,如果是同一把锁,那么就是谁先拿到锁,先执行哪个直到它结束—>顺序执行。如果不是同一把锁两者之间不存在竞争—>随机执行,输出的结果就看谁没有延迟,谁的结果先显示,如果都没有延迟则运行结果是随机的;不加关键字的普通方法跟锁没有关系。

1.8 并发编程下ArrayList和HashSet是不安全的,如何解决呢?

  1. 使用Collections.synchronizedSet()

    List<Object> list = Collections.synchronizedSet(new ArrayList<>());//ArrayList
    Set<Object> set = Collections.synchronizedSet(new HashSet<>());//HashSet

2.使用JUC包下提供的类

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

        }
Set<Object> set = new CopyOnWriteArraySet<>();
        for (int i = 0; i < 10; i++) {
            new Thread(()->{
                set.add(UUID.randomUUID().toString().substring(0,5));
                System.out.println(set);
            },String.valueOf(i)).start();
        }

1.82 Callable —> FutureTask —> Thread

public class G {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        TestCallable Thread = new TestCallable();
        FutureTask futureTask = new  FutureTask(Thread);
        //两个线程只会输出一个Call()---》缓存提高效率
        new Thread(futureTask,"A").start();
        new Thread(futureTask,"B").start();
        //因为他需要获取返回的结果(如果遇到耗时的操作)--->可能会发生阻塞-->所以放在最后面
        System.out.println(futureTask.get());
    }
}
class TestCallable implements Callable<String> {
    @Override
    public String call() throws Exception {
        System.out.println("Call()");
        return "Hello World!";
    }
}

1.9 CountDownLatch(减法计数器)

public class DownLatch {
    public static void main(String[] args) throws InterruptedException {
        CountDownLatch countDownLatch = new java.util.concurrent.CountDownLatch(10);
        for (int i = 0; i < 10; i++) {
            new Thread(()->{
                System.out.println(Thread.currentThread().getName()+" Already Go Out!");
                countDownLatch.countDown();
            },String.valueOf(i)).start();
        }
        //等待计数器归零后唤醒向下执行
        countDownLatch.await();
        System.out.println("大门锁死!");
    }
}

2.0 CyclicBarrier(加法计数器)

public class cbarry {
    public static void main(String[] args) {
        CyclicBarrier barrier = new CyclicBarrier(7,()->{
            System.out.println("召唤神龙!!!");
        });
            for (int i = 1; i <=7; i++) {
                final int temp = i;
                new Thread(()->{
                    System.out.println(Thread.currentThread().getName()+"收集了第"+temp+"颗龙珠");
                    try {
                        //等待执行7次后唤醒---->执行上面的代码--->召唤神龙
                        barrier.await();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } catch (BrokenBarrierException e) {
                        e.printStackTrace();
                    }
                }).start();
            }
    }
}

2.1 Semaphore —- 信号量

public class semaphore {
    //抢车位例子
    public static void main(String[] args) {
        //类似抢车位的位置
        Semaphore semaphore = new Semaphore(3);
        for (int i = 0; i < 6; i++) {
            new Thread(()->{
                try {
                    //阻塞--->停车位被占据
                    semaphore.acquire();
                    System.out.println(Thread.currentThread().getName()+"获得了车位");
                    TimeUnit.SECONDS.sleep(2);
                    System.out.println(Thread.currentThread().getName()+"离开了车位");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }finally {
                    //release()释放
                    semaphore.release();
                }
            },String.valueOf(i)).start();
        }
    }
}

2.2 ReadWriteLock — 读写锁

写锁是独占锁:有且仅有一个线程可以获得写模式下的锁,即当一个线程获取了写模式的锁,其他的线程不能对其以写模式或者读模式在加锁;

读锁是共享锁:当多个线程同时以读模式加锁的时候,都将被允许。即允许多个线程同时进行读操作,但是如果想以写模式进行加锁,那么将会在所有读模式锁都释放的情况下被允许。但是在系统当中一般会阻塞接下来的读操作,让写操作进行,为了避免写操作阻塞时间过长

public class readwrightLock {
    public static void main(String[] args) {
        TestRwLock MyTest = new TestRwLock();
        //数据存储
        for (int i = 1; i < 5; i++) {
            final int temp = i;
            new Thread(()->{
                MyTest.put(temp+ "",temp);
            },String.valueOf(i)).start();
        }
        //数据读取
        for (int i = 1; i < 5; i++) {
            final int temp = i;
            new Thread(()->{
                MyTest.get(String.valueOf(temp));
            },String.valueOf(i)).start();
        }
    }
}
class TestRwLock{
    Map<String,Object> map = new HashMap<>();
    ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
    public synchronized void put(String key,Object value){
        //加写锁
        readWriteLock.writeLock().lock();
        try {
            map.put(key,value);
            System.out.println(key+"正在写入。。。");
            System.out.println(key+"写入成功!");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //解锁
            readWriteLock.writeLock().unlock();
        }
    }
    public void get(String key){
        //加读锁
        readWriteLock.readLock().lock();
        try {
            Object o = map.get(key);
            System.out.println("读取 "+key+" 的Value值为:"+o);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //解锁
            readWriteLock.readLock().unlock();
        }
    }
}

2.3 阻塞队列 BlockingQueue

四组API

方式 抛出异常 有返回值不抛出异常 阻塞 等待 超时 等待
添加 add() offer() put() offer(等待时间,TimeUtil.Second)
移除 remove() poll() take() poll(等待时间,TimeUtil.Second)
检查队首元素 element() peek()
//声明可以容量3个元素的阻塞队列
ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);

//================add()与remove()===============
System.out.println(blockingQueue.add("a"));
System.out.println(blockingQueue.add("b"));
System.out.println(blockingQueue.add("c"));
//抛出异常---超出容量
//System.out.println(blockingQueue.add("d"));

System.out.println(blockingQueue.remove());
System.out.println(blockingQueue.remove());
System.out.println(blockingQueue.remove());
//抛出异常---队列为空无法继续移除
//System.out.println(blockingQueue.remove());

//输出队首元素---a  element()无元素抛出异常 peek()无元素返回 null
System.out.println(blockingQueue.element());
System.out.println(blockingQueue.peek());

//================offer()poll()===============
System.out.println(blockingQueue.offer("a"));
System.out.println(blockingQueue.offer("b"));
System.out.println(blockingQueue.offer("c"));
//不会抛出异常会返回false----成功返回true
System.out.println(blockingQueue.offer("d"));

System.out.println(blockingQueue.poll());
System.out.println(blockingQueue.poll());
System.out.println(blockingQueue.poll());
//不会抛出异常失败返回null----成功取出相应的值
System.out.println(blockingQueue.poll());

//================put()take()===============
blockingQueue.put("a");
blockingQueue.put("b");
blockingQueue.put("c");
//操作阻塞,程序一直等待直到可以把"d"加入队列才往下执行
blockingQueue.put("d");

blockingQueue.take();
blockingQueue.take();
blockingQueue.take();
//操作阻塞,程序一直等待直到可以拿出元素才往下执行
blockingQueue.take();

//================offer(,)和poll(,)===============
blockingQueue.offer("a");
blockingQueue.offer("b");
blockingQueue.offer("c");
//当2s后依然无法加入队列则停止加入队列,往下执行
blockingQueue.offer("d",2,TimeUnit.SECONDS);

blockingQueue.poll();
blockingQueue.poll();
blockingQueue.poll();
//当2s后依然无法获取元素则停止获取,往下执行
blockingQueue.poll(2,TimeUnit.SECONDS);

2.5 同步队列 SynchronizedQueue

public class SynchrniedQueue {
    public static void main(String[] args) {
        SynchronousQueue<String> syq = new SynchronousQueue<>();
        new Thread(()->{
            try {
                syq.put("a");
                System.out.println(Thread.currentThread().getName()+"Put===》a");
                syq.put("b");
                System.out.println(Thread.currentThread().getName()+"Put===》b");
                syq.put("c");
                System.out.println(Thread.currentThread().getName()+"Put===》c");
                syq.put("d");
                System.out.println(Thread.currentThread().getName()+"Put===》d");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"线程1").start();

        new Thread(()->{
            try {
                TimeUnit.SECONDS.sleep(3);
                syq.take();
                System.out.println(Thread.currentThread().getName()+"Take===》a");
                TimeUnit.SECONDS.sleep(3);
                syq.take();
                System.out.println(Thread.currentThread().getName()+"Take===》b");
                TimeUnit.SECONDS.sleep(3);
                syq.take();
                System.out.println(Thread.currentThread().getName()+"Take===》c");
                TimeUnit.SECONDS.sleep(3);
                syq.take();
                System.out.println(Thread.currentThread().getName()+"Take===》d");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"线程2").start();
    }
}

只能放一个取一个,如果将线程2注释掉,会出现线程1被阻塞的状态。放一个a进去,另外一个线程取出a,在放b进去,另外一个线程取出b。。。。以此类推。同步队列—只能放一个元素进去

2.6 线程池创建线程

public class DemonThreadPool {
    public static void main(String[] args) {
        //创建单个线程
        ExecutorService threadPool = Executors.newSingleThreadExecutor();
        //创建一个固定的线程池大小
        //ExecutorService threadPool = Executors.newFixedThreadPool(5);
        //可伸缩,遇强则强,遇弱则弱
        //ExecutorService threadPool = Executors.newCachedThreadPool();
        try {
            for (int i = 0; i < 10; i++) {
                threadPool.execute(()->{
                    System.out.println(Thread.currentThread().getName()+"====>OK baby!");
                });
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //关闭线程池
            threadPool.shutdown();
        }
    }
}

2.7 ThreadExcutor创建线程

public class ThreadPool {
    public static void main(String[] args) {
        ThreadPoolExecutor threadPool = new ThreadPoolExecutor(
                2,
                5,
                3,
                TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(3),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy()
                );
        //最大承载量 maxMumPoolSize+队列长度 = 5+3
        try {
            for (int i = 0; i < 7; i++) {
                threadPool.execute(()->{
                    System.out.println(Thread.currentThread().getName()+"====>OK baby!");
                });
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            threadPool.shutdown();
        }
    }
}

image-20211009165012501

//默认拒绝策略---超出最大处理数量--->不处理并抛出异常
new ThreadPoolExecutor.AbortPolicy()
//超出最大处理数量(队列满了)--->交由其原线程执行(main)
new ThreadPoolExecutor.CallerRunsPolicy()
//队列满了,不会抛出异常,丢掉任务
new ThreadPoolExecutor.DiscardPolicy()
// 队列满了,也不会抛出异常,但是会尝试丢弃最旧的任务把新任务添加进去
new ThreadPoolExecutor.DiscardOldestPolicy()

2.8 四种函数型接口

/** Consumer消费型接口 */
//Consumer<String> consumer = new Consumer() {
//    @Override
//    public void accept(Object o) {
//        System.out.println(o);
//    }
//};
//
Consumer<String> consumer = (str)->{
    System.out.println(str);
};
consumer.accept("hello world");

/** supplier供给型接口 */
//Supplier supplier = new Supplier() {
//    @Override
//    public Object get() {
//        System.out.println("你已经进入Get()");
//        return 1024;
//    }
//};
Supplier supplier = ()->{
    System.out.println("你已经进入Get()");
    return 1024;
};
supplier.get();

/** Function 函数型接口 */
//Function<String, String > function = new Function<String, String>() {
//    @Override
//    public String apply(String o) {
//        return o;
//    }
//};
Function<String, String > function = (str)->{
    return str;
};
System.out.println(function.apply("Fengfeng"));

/** Predicate 断定型接口---返回值只有False或者True */
//Predicate<String> predicate = new Predicate<String>() {
//    @Override
//    public boolean test(String s) {
//        Boolean flag = false;
//        if (!s.isEmpty()){flag=true;}
//        return flag;
//    }
//};
Predicate<String> predicate = (s)->{
    Boolean flag = false;
    if (!s.isEmpty()){flag=true;}
    return flag;
};
System.out.println(predicate.test("Hello world"));

2.9 JMM

关于JMM同步的一些约定:

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

3.0 什么是指令重排?

就是你写的程序,计算并不是按照你写的那样去执行

源代码—>编译器优化重排—>指令并行也会重排—>内存系统也会重排—>执行

3.1 可重入锁

某个线程获得了锁之后可以再次获取锁(套娃)而不会产生死锁,通俗点讲就是一扇门里面还有一扇门,你拿到第一扇门的钥匙后会自动获取第二扇门的钥匙。

3.11 ABA问题—->解决—>原子引用

例子:桌子上又一个装满水的杯子,你路过之后把杯中的水喝完,又重新倒满了杯中的水,这时候杯子的主人回来,他看到杯子的水是满的,以为杯子的水没人动过

3.2 自旋锁

public class SpinLock {
    //原子类
    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 myunlock(){
        Thread thread = Thread.currentThread();
        atomicReference.compareAndSet(thread,null);
        System.out.println(Thread.currentThread().getName()+"解锁成功!");
    }
}
public class TestSpinLock {
    public static void main(String[] args) throws Exception {
        SpinLock spinLock = new SpinLock();

        new Thread(()->{
            spinLock.mylock();
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                spinLock.myunlock();
            }
        },"线程A").start();

        TimeUnit.SECONDS.sleep(1);
        new Thread(()->{
            spinLock.mylock();
            try {
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                spinLock.myunlock();
            }
        },"线程B").start();
    }
}

image-20211010195849893

线程A获取了锁,线程B无法获取锁只能被迫进入自旋状态卡在了这行代码上,

while (!atomicReference.compareAndSet(null,thread)){}

直到线程A解锁后将atomicReference的值重置为null后,线程B才可以获得锁

3.3 排查死锁

  1. jps -l

image-20211010201424152

  1. jstack+进程号 —->获取当前进程堆栈信息—–>查看是否产生死锁问题

文章作者: Z.Wfeng
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Z.Wfeng !
  目录