• JAVA多线程和JUC


    1.程序、进程、线程关系?

    程序(Process)是一个静态的概念,是指令和数据的有序集合。
    进程是一个动态的概念,程序运行就是一个进程。进程是操作系统分配资源的基本单位。
    线程(Thread)是CPU调度和执行的单位。
    进程和线程是包含与被包含的关系,一个进程可以有多个线程。JAVA中一个进程至少拥有两个线程(main/gc线程)。

    2.创建线程的多种方式

    1.继承Thread类
    缺点:java是单继承,使用继承方式后代码耦合

    public class CreatThread1 extends Thread{
    
        @Override
        public void run() {
            for(int i=0;i<200;i++){
                System.out.println("线程"+i);
            }
        }
    
        public static void main(String[] args) throws InterruptedException {
            CreatThread1 thread = new CreatThread1();
            thread.start();
            for(int i=0;i<1000;i++){
                System.out.println("主线程"+i);
            }
    
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    2.实现Runnable接口

    public class CreateThread2 implements  Runnable{
    
        @Override
        public void run() {
            for (int i = 0; i < 200; i++) {
                System.out.println("线程"+i);
            }
        }
    
        public static void main(String[] args) {
            CreateThread2 thread = new CreateThread2();
            new Thread(thread).start();
            for (int i = 0; i < 1000; i++) {
                System.out.println("主线程"+i);
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    3.实现Callable接口
    这种方式是对于Runnable的升级版,Runnable创建的线程没有返回值,而Callable有返回值,而且多了其他操作。

    4.基于四种线程池的创建方式

    public class TestThreadPoolExecutor {
        public static void main(String[] args) {
    //        ExecutorService threadPool = Executors.newSingleThreadExecutor();//一个线程,不建议使用
    //        ExecutorService threadPool = Executors.newFixedThreadPool(5);//指定线程个数,不建议使用
    //        ExecutorService threadPool = Executors.newCachedThreadPool();//根据内存而定线程个数,不建议使用
            ThreadPoolExecutor threadPool = new ThreadPoolExecutor(2,//一般线程池可以有几个
                    Runtime.getRuntime().availableProcessors(),//最大连接个数,一般是当前电脑的核数
                    3,//超时时间
                    TimeUnit.SECONDS,//超时单位
                    new LinkedBlockingDeque<>(3),//线程池已满,在外面最多可以等待进入线程池的个数
                    Executors.defaultThreadFactory(),//默认工程,一般设置这个就行,固定
                    new ThreadPoolExecutor.DiscardPolicy());//指定线程的拒绝策略
            try {
                for (int i = 0; i < 100; i++) {
                    threadPool.execute(() -> {
                        System.out.println(Thread.currentThread().getName() + "ok");
                    });
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                threadPool.shutdown();
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    3.怎么让线程停止

    不太推荐jdk中的stop方式,stop已经被弃用,如果先前受这些监视器保护的任何对象处于不一致的状态,则损坏的对象将变得对其他线程可见,可能导致任意行为。推荐使用自定义标志位方式来使线程停止。

    public class ThreadStop implements Runnable {
        //1.设置标志位
        private boolean flag = true;
        @Override
        public void run() {
            int i = 0;
            while (flag){
                System.out.println("run...Thread"+i++);
            }
        }
        //2.设置一个公开的方法停止线程
        public void stop(){
            this.flag = false;
        }
        public static void main(String[] args) {
            ThreadStop threadStop = new ThreadStop();
            new Thread(threadStop).start();
            //主线程
            for (int i = 0; i < 1000; i++) {
                System.out.println("main.."+i);
                if (i==900){//当主线程中的i=900时,run线程停止
                    threadStop.stop();
                    System.out.println("run线程停止了");
                }
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    4.notify()和notifyAll()有什么区别?

    1、notify()只能唤醒一个wait()线程,然而notifyAll()可以唤醒多个wait()线程;

    2、两个都必须在synchronized中使用,过程不释放锁;

    3、当每个线程都有特定锁的时候,只有等待这个锁的线程才能被唤醒,也就是线程2的notify()或notifyAll()不能唤醒线程1的wait();

    5.sleep()和wait()有什么区别?

    ​1、这两个方法来自不同的类分别是Thread和Object,sleep方法属于Thread类中的静态方法,wait属于Object的成员方法。​

    ​2、最主要是sleep方法没有释放锁,而wait方法释放了锁,使得其他线程可以使用同步控制块或者方法。​

    ​3、wait,notify和notifyAll只能在同步控制方法或者同步控制块里面使用,而sleep可以在任何地方使用(使用范围)。

    6.volatile是什么?如果不加lock和synchronized ,怎么样保证原子性?

    1)保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的,volatile关键字会强制将修改的值立即写入主存。

    2)禁止进行指令重排序。

    3)volatile 不是原子性操作

    4)可以使用原子类,如AtomicInteger

    7.Thread类中的start()和run()方法有什么区别?

    start()方法: 它会启动一个新线程,并将其添加到线程池中,待其获得CPU资源时会执行run()方法,start()不能被重复调用。

    run()方法:它和普通的方法调用一样,不会启动新线程。只有等到该方法执行完毕,其它线程才能获得CPU资源。

    8.为什么wait和notify方法要在同步块中调用?

    wait()方法强制当前线程释放对象锁。这意味着在调用某对象的wait()方法之前,当前线程必须已经获得该对象的锁。因此,线程必须在某个对象的同步方法或同步代码块中才能调用该对象的wait()方法。.

    9.简述一下你对线程池的理解。

    第一:降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。

    第二:提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。

    第三:提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。

    10.Semaphore信号量

    Semaphore 是一种基于计数的信号量。它可以设定一个阈值,基于此,多个线程竞争获取许可信号,做完自己的申请后归还,超过阈值后,线程申请许可信号将会被阻塞。 Semaphore 可以用来构建一些对象池,资源池之类的, 比如数据库连接池实现互斥锁(计数器为 1)我们也可以创建计数为 1 的 Semaphore,将其作为一种类似互斥锁的机制,这也叫二元信号量,表示两种互斥状态。

    11.ArrayBlockingQueue

    用数组实现的有界阻塞队列。此队列按照先进先出(FIFO)的原则对元素进行排序。 默认情况下不保证访问者公平的访问队列,所谓公平访问队列是指阻塞的所有生产者线程或消费者线程,当队列可用时,可以按照阻塞的先后顺序访问队列,即先阻塞的生产者线程,可以先往队列里插入元素,先阻塞的消费者线程,可以先从队列里获取元素。通常情况下为了保证公平性会降低吞吐量。我们可以使用以下代码创建一个公平的阻塞队列。

    12.什么是Callable和Future?

    Callable接口类似于Runnable,从名字就可以看出来了,但是Runnable不会返回结果,并且无法抛出返回结果的异常,而 Callable功能更强大一些,被线程执行后,可以返回值,这个返回值可以被Future拿到,也就是说,Future可以拿到异步执行任务的返回值。可以认为是带有回调的Runnable。

    Future接口表示异步任务,是还没有完成的任务给出的未来结果。所以说Callable用于产生结果,Future用于获取结果。

    13.并发和并行

    并发: 多线程操作同一个资源(并发编程—多个线程操作同一个资源类), CPU 只有一核,可以使用CPU快速交替, 模拟出来多条线程

    并行:CPU多核,多个线程可以同时执行

    14.Synchronized 与Lock

    • Synchronized 是内置的Java关键字,Lock是一个Java类
    • Synchronized 无法判断获取锁的状态,Lock可以判断
    • Synchronized 会自动释放锁,lock必须要手动加锁和手动释放锁!可能会遇到死锁
    • Synchronized 线程1(获得锁->阻塞)、线程2(等待);lock就不一定会一直等待下去,lock会有一个trylock去尝试获取锁,不会造成长久的等待。
    • Synchronized 是可重入锁,不可以中断的,非公平的;Lock,可重入的,可以判断锁,可以自己设置公平锁和非公平锁;
    • Synchronized 适合锁少量的代码同步问题,Lock适合锁大量的同步代码;

    15.CopyOnWriteArrayList

    使用ArrayList,当多个线程同时写入数据时会产生java.util.ConcurrentModificationException异常(并发修改异常)

    解决方案:使用vector代替arrylist,List list = new Vector<>();
    使用collections工具类,List list = Collections.synchronizedList(new ArrayList<>());
    CopyOnWrite,List list = new CopyOnWriteArrayList<>();

    16.CountDownLatch

    相当于减法器

    public class TestCountDownLatch {
        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()+"线程已完成");
                    countDownLatch.countDown();//数量-1
                },String.valueOf(i)).start();
            }
            countDownLatch.await();//等待计数器归零,然后再向下执行
            System.out.println("线程全部执行完毕");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    17.CyclicBarrier

    相当于加法器

    public class TestCyclicBarrier {
        public static void main(String[] args) {
            CyclicBarrier cyclicBarrier = new CyclicBarrier(7, () -> {
                System.out.println("7个线程都已完成");
            });
            for (int i = 0; i < 7; i++) {
                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();
    
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    18.ReadWriteLock

    读写锁允许访问共享数据时的并发性高于互斥锁所允许的并发性。 它利用了这样一个事实:一次只有一个线程( 写入线程)可以修改共享数据,在许多情况下,任何数量的线程都可以同时读取数据(因此读取器线程)。

    19.BlockingQueue

    BlockingQueue方法有四种形式,具有不同的操作方式,不能立即满足,但可能在将来的某个时间点满足:一个抛出异常,第二个返回一个特殊值( null或false ,具体取决于操作),第三个程序将无限期地阻止当前线程,直到操作成功为止,而第四个程序块在放弃之前只有给定的最大时限。
    在这里插入图片描述

    20.线程池(三大方法、7大参数、4种拒绝策略)

    池化技术包括:线程池、JDBC的连接池、内存池、对象池等

    池化技术:事先准备好一些资源,如果有人要用,就来我这里拿,用完之后再归还,以此来提高效率

    线程池的好处:降低资源的消耗;提高响应的速度;方便管理;线程复用、可以控制最大并发数、管理线程;

    public class TestThreadPoolExecutor {
        public static void main(String[] args) {
    //        ExecutorService threadPool = Executors.newSingleThreadExecutor();//一个线程,不建议使用
    //        ExecutorService threadPool = Executors.newFixedThreadPool(5);//指定线程个数,不建议使用
    //        ExecutorService threadPool = Executors.newCachedThreadPool();//根据内存而定线程个数,不建议使用
            ThreadPoolExecutor threadPool = new ThreadPoolExecutor(2,//一般线程池可以有几个
                    Runtime.getRuntime().availableProcessors(),//最大连接个数,一般是当前电脑的核数
                    3,//超时时间
                    TimeUnit.SECONDS,//超时单位
                    new LinkedBlockingDeque<>(3),//线程池已满,在外面最多可以等待进入线程池的个数
                    Executors.defaultThreadFactory(),//默认工程,一般设置这个就行,固定
                    new ThreadPoolExecutor.DiscardPolicy());//指定线程的拒绝策略
            try {
                for (int i = 0; i < 100; i++) {
                    threadPool.execute(() -> {
                        System.out.println(Thread.currentThread().getName() + "ok");
                    });
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                threadPool.shutdown();
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    21.四大函数式接口

    函数式接口、断定型接口、消费型接口、供给型接口

    22.流式计算

    public class TestStream {
        public static void main(String[] args) {
            User1 u1 = new User1(1, "a", 21);
            User1 u2 = new User1(2, "b", 22);
            User1 u3 = new User1(3, "c", 23);
            User1 u4 = new User1(4, "d", 24);
            User1 u5 = new User1(5, "e", 25);
            List<User1> list = Arrays.asList(u1, u2, u3, u4, u5);
            list.stream().filter(u->{return u.getId()%2==0;})
                    .map(u->{return u.getName().toUpperCase();})
                    .sorted((uu1,uu2)->{return uu2.compareTo(uu1);})
                    .limit(1)
                    .forEach(System.out::println);
            //计算0-1000000000的和
            System.out.println(LongStream.rangeClosed(0L,10_0000_0000L).parallel().reduce(0,Long::sum));
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    23.ForkJoin

    ForkJoin 工作特点:工作窃取!

    原理是双端队列!从上面和下面都可以去进行执行!,当B线程完成后,B线程回去执行A线程未完成的任务,提高效率。

    24.异步回调

    即Future,设计的初衷: 对将来的某个事件的结果进行建模,类似ajax
    1.没有返回值的runAsync异步回调

    public class FutureTest1 {
        public static void main(String[] args) throws ExecutionException, InterruptedException {
            CompletableFuture<Void> voidCompletableFuture = CompletableFuture.runAsync(() -> {
                try {
                    TimeUnit.SECONDS.sleep(2);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + "  runAsync--->void");
            });
            System.out.println("111111");
            System.out.println(voidCompletableFuture.get());   // 获取异步执行结果
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    2.有返回值的异步回调supplyAsync

    public class supplyAsyncTest {
        public static void main(String[] args) throws ExecutionException, InterruptedException {
            CompletableFuture<Integer> integerCompletableFuture = CompletableFuture.supplyAsync(() -> {
                System.out.println(Thread.currentThread().getName() + "supplyAsync=>Integer");
                int i = 10 / 0;
                return 1024;
            });
    
            //设置integerCompletableFuture的回调
            System.out.println(integerCompletableFuture.whenComplete((t, u) -> {
                System.out.println("t=>" + t);// 成功结果
                System.out.println("u=>" + u);// 错误信息:
            }).exceptionally((e) -> {
                System.out.println(e.getMessage());
                return 233;
            }).get());  //获取回调成功或者失败的返回结果
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    25.自旋锁

  • 相关阅读:
    数据通信——传输层TCP(超时时间选择)
    分布式Session登录
    现实与虚幻:人工智能的迷惑瞬间
    ArduPilot飞控AOCODARC-H7DUAL固件编译
    静态方法、类方法
    【附源码】Python计算机毕业设计汽车养护平台
    【Git】gitignore不生效场景2: 添加文件忽略 & .gitignore,整个文件夹都被忽略了
    八股文第九天
    一篇带你了解如何使用纯前端类Excel表格构建现金流量表
    Hadoop3:MapReduce源码解读之Map阶段的FileInputFormat的切片原理(2)
  • 原文地址:https://blog.csdn.net/qq_44954571/article/details/125613036