• 多线程与高并发(一)


    【前言】:

    多线程、JVM、操作系统。

    【概述】:

    基础概念
    JUC同步工具
    同步容器
    Disruptor //一个MQ框架,公认的单机环境下效率最高。
    线程池

    【线程的概念】:

    【纤程】:在这里插入图片描述

    【 run和start的区别 】:

            //new T1().run();    //方法调用。
            new T1().start();    //Thread类里面有start方法。
    
    • 1
    • 2

    run方法还是依次顺序执行;( 先run后main , 相当于只有一条执行路径 )
    但start方法是分支执行。( 有分支路径 )

    【 线程的启动方式 】:

    package Ten_Class.t01;
    
    import java.util.concurrent.*;
    
    public class T02_HowToCreateThread {
        static class MyThread extends Thread {
            @Override
            public void run() {
                System.out.println("Hello MyThread!");
            }
        }
    
        static class MyRun implements Runnable {
            @Override
            public void run() {
                System.out.println("Hello MyRun!");
            }
        }
    
        static class MyCall implements Callable<String> {
            @Override
            public String call() {
                System.out.println("Hello MyCall");
                return "success";
            }
        }
    
        //启动线程的5种方式
        public static void main(String[] args) throws Exception {
            new MyThread().start();
            new Thread(new MyRun()).start();
            new Thread(() -> {
                System.out.println("Hello Lambda!");
            }).start();
    
            FutureTask<String> task = new FutureTask<>(new MyCall());
            Thread t = new Thread(task);
            t.start();
            System.out.println(task.get());
    
            ExecutorService service = Executors.newCachedThreadPool();
            service.execute(() -> {
                System.out.println("Hello ThreadPool");
            });
    
            Future<String> f = service.submit(new MyCall());
            String s = f.get();
            System.out.println(s);
            service.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
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52

    【 线程的方法 】:

    【 sleep 】:

    睡眠 ,当前线程暂停一段时间, 让给别的线程去运行。

    【 yield 】:

    我从CPU上先离开 , 进入到一个等待队列里。当然有可能刚进去就被拽出去执行 , 但更大的可能是——将等待队列中其他的拽出去执行。
    我让出一下CPU , 然后从等待队列里拽出一个来执行。

    【 Join 】:

    在这里插入图片描述
    两个线程 t1 、 t2 , 如果在t1线程的某个点上调用 t2.join , 这个点的意思是——立即跑到t2去运行,t1先等着,什么时候t2运行完了,t1再运行 。
    在这里插入图片描述
    //Join常用来用于等待另一个线程的结束。

    【如何保证三个线程按照顺序执行完呢?】:

    main{
    	t1.join();
    	t2.join();
    	t3.join();
    }
    
    或者
    
    t1{
    	t2.join();
    }
    
    t2{
    	t3.join();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    【 线程的状态 】:

    【NEW】:

    在这里插入图片描述
    //当我们新建了一个线程,new了一个,还没有调用start( ) 方法之前的线程的状态。

    【 Runnable 】:

    //当你调用start方法之后 , 它会被线程调度器来执行,也就是交给操作系统来执行了。交给操作系统来执行的话,整个的状态叫做——Runnable。Runnable内部又有两个状态( Ready就绪状态 和 Running运行状态 ) , Ready就绪状态是指我们扔到CPU的等待队列里去了。真正的扔到CPU上去运行了——这个状态叫做Running。
    Teminated状态后,不能再回到NEW状态再调用start()了,这样是不行的。
    在这里插入图片描述
    【什么时候进入阻塞?】:
    加synchronized进入同步代码块,我这段代码写了synchronized , 但是我还没有得到那把锁 。
    在这里插入图片描述
    【 哪些是操作系统管理的?哪些是JVM管理的? 】:
    全是JVM管理的 ,因为JVM管理这些状态的时候也要通过操作系统。JVM 和 操作系统 它俩分不开 , JVM是跑在操作系统上的程序。

    【锁的概念】:

    package Ten_Class.t01;
    
    public class T_114_01 {
        private int count = 10;
        private Object o = new Object();
        
        public void m(){
            synchronized ( o ){
                count--;
                System.out.println( Thread.currentThread().getName() + " count = " +count );
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    【 锁的特性 】:

    如果我们每一次都定义一个锁的对象( Object o )的话,这样的加锁方式太麻烦,每次都得New一个新的对象出来。
    //所以有一个最简单的方式:

            synchronized (this){  //锁定当前方法。
    			。。。。。。
            }
    
    • 1
    • 2
    • 3

    【Class文件】:
    每一个class文件load到内存之后,它会专门生成一个class类的对象——和load到内存的那一段代码相对应;

    【 方法上加sync 】:

    package Ten_Class.t01;
    
    public class T_115_02 {
        private static int count = 10;
    
        public synchronized static void m(){   //这里等同于synchronized (T_115_02.class)
            count--;
            System.out.println( Thread.currentThread().getName() + " count = " + count );
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    【 设计小程序验证锁的问题 】:

    【规则】:
    加了synchronized就没有必要再加volatile了,因为synchronized既保证了原子性又保证了可见性。

    【同步方法和非同步方法是否可以同时调用?】:
    //下面程序段中m1是同步方法,m2是非同步方法。

    /**
     * 对比前一个小程序,分析一下这个程序的输出
     */
     
    package Ten_Class.t01;
    
    public class T_116_02 {
    
        private int count = 10;
    
        public synchronized void m1() {
            System.out.println( Thread.currentThread().getName() + " m1 start...... " );
            try{
                Thread.sleep(10000 );
            }catch( InterruptedException e ){
                e.printStackTrace();
            }
            System.out.println( Thread.currentThread().getName() + " m1 end 。。。。。" );
        }
    
        public void m2(){
            try{
                Thread.sleep(5000 );
            }catch (InterruptedException e){
                e.printStackTrace();
            }
            System.out.println( Thread.currentThread().getName() + " m2 " );
        }
    
        public static void main(String[] args) {
            T_116_02 t = new T_116_02();
    
            new Thread( t::m1 , "t1" ).start();
            new Thread( t::m2 , "t2" ).start();
        }
    }
    
    • 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
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    在这里插入图片描述
    //说明是允许被同时调用的。

    【模拟银行账户 】:

    //是否允许客户读取那些不好的数据。
    【允许客户读到不好数据】:
    写方法加锁,读方法可以不加锁。
    【不允许客户读取不好的数据】:
    写、读 方法都加上锁。

    /**
     * 面试题:模拟银行账户
     * 对业务写方法加锁
     * 对业务读方法不加锁
     * 这样行不行?
     * 

    * 容易产生脏读问题(dirtyRead) */ package Ten_Class.t01; import java.util.concurrent.TimeUnit; /* * 【面试题】————模拟银行账户 * 对业务写方法加锁; * 对业务读方法不加锁; * 这样行不行? * * 容易产生脏读问题( dirtyRead ) * */ public class T_116_03_Account { String name; double balance; public synchronized void set(String name, double balance) { this.name = name; try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } this.balance = balance; } public /*synchronized*/ double getBalance(String name) { return this.balance; } public static void main(String[] args) { T_116_03_Account a = new T_116_03_Account(); new Thread(() -> a.set("zhangsan", 100.0)).start(); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(a.getBalance("zhangsan")); try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(a.getBalance("zhangsan")); } }

    • 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
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63

    【 加锁与否 】:

    能不加锁就不加锁 , 加完锁后的效率要低100倍。

    【锁的可重入属性】:

    synchronized必须是可重入锁

    【例一】:

    package Ten_Class.t01;
    
    import java.util.concurrent.TimeUnit;
    
    /*
    * 一个同步方法可以调用另外一个同步方法,一个线程已经拥有某个对象的锁,再次申请的时候仍然会得到该对象的锁。
    * 也就是说synchronized获得的锁是可重入的。
    * */
    
    public class T_117_01 {
        synchronized void m1(){
            System.out.println("m1 start");
            try{
                TimeUnit.SECONDS.sleep(1);
            }catch (InterruptedException e ){
                e.printStackTrace();
            }
            m2();
            System.out.println("m1 end");
        }
    
        synchronized void m2(){
            try{
                TimeUnit.SECONDS.sleep(2);
            }catch (InterruptedException e ){
                e.printStackTrace();
            }
            System.out.println("----m2----");
        }
    
        public static void main(String[] args) {
            new T_117_01().m1();
        }
    }
    
    • 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
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    【例二】:

    package Ten_Class.t01;
    
    import java.util.concurrent.TimeUnit;
    
    public class T_117_02 {
        synchronized void m(){
            System.out.println("m start");
    
            try{
                TimeUnit.SECONDS.sleep(1);
            }catch(InterruptedException e){
                e.printStackTrace();
            }
    
            System.out.println("m end");
        }
    }
    
    class TT extends T_117_02{
    
        synchronized void m(){
            System.out.println("child m start");
            super.m();
            System.out.println("child m end");
        }
    }
    
    • 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

    【异常】:

    //抛出异常,释放锁,其它想要拿到这把锁的程序会进来。

    package Ten_Class.t01;
    
    import java.util.concurrent.TimeUnit;
    
    /*
    * 程序在执行过程中,如果出现异常,默认情况锁会被释放;
    * 所以,在并发处理的过程中,有异常要多加小心,不然可能会发生不一致的情况;
    * 比如,在一个 web app 处理过程中 , 多个servlet线程共同访问一个资源,这时如果异常处理不合适,
    * 在第一个线程中抛出异常,其它线程就会进入同步代码区,有可能会访问到异常产生时的数据。
    * 因此要非常小心的处理同步业务逻辑中的异常。
    * */
    public class T_118_01 {
        int count = 0;
        synchronized void m(){
            System.out.println( Thread.currentThread().getName() + " start " );
    
            while (true){
                count++;
                System.out.println( Thread.currentThread().getName() + " count = " + count );
                try{
                    TimeUnit.SECONDS.sleep(1);
                }catch ( InterruptedException e ){
                    e.printStackTrace();
                }
    
                if (count==5){
                    int i = 1/0;  //此处抛出异常 , 锁将要被释放,要想不被释放,可以在这里进行catch , 然后让循环继续。
                    System.out.println( i );
                }
            }
        }
    
        public static void main(String[] args) {
            T_118_01 t = new T_118_01();
            Runnable r = new Runnable() {
                @Override
                public void run() {
                    t.m();
                }
            };
    
            new Thread( r,"t1").start();
    
            try{
                TimeUnit.SECONDS.sleep(3);
            }catch (InterruptedException e){
                e.printStackTrace();
            }
    
            new Thread(r,"t2").start();
        }
    }
    
    • 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
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52

    在这里插入图片描述

  • 相关阅读:
    systemd 服务脚本编写与管理
    JOSEF约瑟 JJKY-30(JY82)检漏继电器 面板安装 0.1-50A 380V配零序互感器
    python opencv 读取mp4,上一帧,下一帧
    MySQL进阶实战1,数据类型与三范式
    0.96寸OLED屏显示测试和代码详细分析SPI通信
    【Vue3.0】watch监听事件
    【中间件系列】Kafka 与 RocketMQ几件事
    手把手APP抓包检测实战 - 某汽车APP
    MFC CDockablePane类,常见配置
    【AI赋能医学】基于深度学习和HRV特征的多类别心电图分类
  • 原文地址:https://blog.csdn.net/fuyuanduan/article/details/128128616