• 多线程与高并发编程


    多线程与高并发编程一

    线程

    什么是线程

    程序的一条执行路径

    public class ThreadDemo {
        public static void main(String[] args) {
            new T1().start();
            for (int i = 0; i<10; i++){
                try {
                    TimeUnit.MICROSECONDS.sleep(1);
                }catch (InterruptedException e){
                    e.printStackTrace();
                }
                System.out.println("main");
            }
        }
        private static class T1 extends Thread{
            @Override
            public void run(){
                for (int i = 0; i<10; i++){
                    try {
                        TimeUnit.MICROSECONDS.sleep(1);
                    }catch (InterruptedException e){
                        e.printStackTrace();
                    }
                    System.out.println("T1");
                }
            }
        }
    }
    
    • 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

    image-20220712225136894

    新建一个线程

    public class NewThread {
        //继承thread
        static class MyThread extends Thread{
            @Override
            public void run(){
                System.out.println("HELLO MyThrwad");
            }
    
        }
        //实现Runnabe
        static class MyRun implements Runnable{
    
            @Override
            public void run() {
                System.out.println("HELLO MyRun");
            }
        }
        //@Function
        public static void main(String[] args) {
            new MyThread().start();
            new Thread(new MyRun()).start();
            new Thread(()->{
                System.out.println("Hello Lambda");
            }).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

    启动一个线程

    //启动线程的方式 1:Thread 2.Runable 3.Executors.newCachedThrad(线程池)实际上也是1、2的一种
    
    • 1

    image-20220714210145237

    状态新建一个线程是 NEW状态(没屌用start),调用start之后之后线程 会被线程调度器来执行整体的状态是Runable状态(内部有ready和Runing俩状态),其中ready是就绪状态, 继续状态就是将线程放到cpu的等待队列当中,真正的放到cpu中运行的是Runing状态,处于Runing状态的线程可以通过yield方法重新回到Ready,直到线程被调度器选中执行重新到Runing状态,执行结束后Teminated状态,到达Teminated不能再次start,也不能回到runable状态

    image-20220720233600146

    阻塞状态:Blocked 进入synchronized代码块没有获得没有获得锁之前是阻塞状态

    等待:waiting 在runable状态是调用了o.wait(time)、t.join(time)、LockSupport.park()进入waiting状态,再调用o.nitify()、o.nitifyAll()、LockSupport.unpark(),又回到Runable状态

    时间等待:Timewaiting(时间结束后会自动回去) 在runable状态是调用了Thread.sleep(time)
    o.wait(time)、t.join(time)、LockSupport.parkNanos()、LockSupport.parkUntill()时间结束之后自动回到Runable状态

    stop()废除不使用

    interrupt 底层会使用没有使用它控制业务逻辑的

    synchronized

    1.在同一个classloader中的classLoader都是单例模式。

    2.一个object只能锁住一个对象

    什么时候用到多线程

    1.起一个线程 加载图片下载文件

    1. MQ
    2. 小程序体现 防止用户脚本刷

    模拟单例模式产生的脏数据

    import java.util.concurrent.TimeUnit;
    
    /**
     * @Author:
     * @Date: 2022/08/08/22:09
     * @Description:
     **/
    //singletonMode 单例模式
    public class 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) {
            Account account = new Account();
            new Thread(() -> account.set("zkj", 100.0)).start();
            try{
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(account.getBalance("zkj"));
            try{
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(account.getBalance("zkj"));
        }
    
    }
    
    • 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

    image-20220808235405477

    new Account的时候就设置了balance这样1s后能读到正确的值100.0,再过2s后能读到正确的值100.0

    import java.util.concurrent.TimeUnit;
    
    /**
     * @Author: 
     * @Date: 2022/08/08/22:09
     * @Description:
     **/
    //singletonMode 单例模式
    public class 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) {
            Account account = new Account();
            new Thread(() -> account.set("zkj", 100.0)).start();
            try{
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(account.getBalance("zkj"));
            try{
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(account.getBalance("zkj"));
        }
    
    }
    
    • 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

    image-20220808235629037

    new Account的时候就没有设置balance,而是等待1s后进行设置balance,这样1s后能不读到正确的值100.0,而是默认值0,再过2s后能读到正确的值100.0

    import java.util.concurrent.TimeUnit;
    
    /**
     * @Date: 2022/08/08/22:09
     * @Description:
     **/
    //singletonMode 单例模式
    public class 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) {
            Account account = new Account();
            new Thread(() -> account.set("zkj", 100.0)).start();
            try{
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(account.getBalance("zkj"));
            try{
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(account.getBalance("zkj"));
        }
    
    }
    
    • 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

    new Account的时候就没有设置balance,而是等待1s后进行设置balance,但是由于getBalance操作设置的了锁,这样无法读取数据,只能等待设置了值之后,再进行读操作,这样1s后能读到正确的值100.0,而是默认值0,再过2s后能读到正确的值100.0

    业务逻辑允许不枷锁就不枷锁 枷锁会影响效率百倍的差距

    synchronized可重入的锁

    一个同步方法可以调用另外一个同步方法,一个线程已经拥有某个对象的锁,再次申请的时候仍会得到该对象的锁,也就是说synchronized获得的是可重入的锁

    import java.util.concurrent.TimeUnit;
    
    /**
     * @Date: 2022/08/09/00:18
     * @Description: 一个同步方法可以调用另外一个同步方法,一个线程已经拥有某个对象的锁,再次申请的时候
     * 仍会得到该对象的锁,也就是说synchronized获得的是可重入的锁
     **/
    public class reentrant {
        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() {
            System.out.println("m1 start");
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("m1 end");
        }
    
        public static void main(String[] args) {
            new reentrant().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

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4uc1vByy-1660233819593)(…/Library/Application Support/typora-user-images/image-20220809214249757.png)]

    M1()方法由某线程创建,m2()也需要枷锁是同一个线程的同一个锁,m2()也会得到这把锁

    eg:image-20220809234840300

    如果synchronize不是可重入的锁子类调用父类的方法会死锁

    程序在执行过程中,如果出现异常,默认情况锁会被释放

    import java.util.concurrent.TimeUnit;
    
    /**
     * @Date: 2022/08/10/21:29
     * @Description:
     **/
    public class synceexception {
        /**
         * 程序在执行过程中,如果出现异常,默认情况锁会被释放
         * 所以,在并发处理的过程,有异常要多加小心,不然可能会发生不一致的情况,
         * 比如,在一个web app处理过程中,多个servlet线程共同访问同一个资源,这时如果异常处理不合适
         * 在第一个线程中抛出异常,其他线程就会进入同步代码区,有可能会访问到异常产生时的数据
         * 因此要非常小心的处理同步业务逻辑中的异常
         */
        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) {
            synceexception synceexception = new synceexception();
            Runnable runnable = new Runnable() {
                @Override
                public void run() {
                    synceexception.m();
                }
            };
            new Thread(runnable,"t1").start();
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            new Thread(runnable,"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

    image-20220811201248830

    可见t1抛出异常的同时释放了锁,t2获得了锁

    Synchronized 的底层实现(hotspot)

    jdk早期的是重量级锁是向操作系统申请的(OS),后来随着java开始有越来越多的高并发的项目,Synchronized的效率就太低了,经过改造才有了现在的锁

    现在的锁是有锁升级的概念

    偏向锁:sync(Object)如果只有一个线程 只markwoed 记录这个线程ID

    自选锁 :此时如果出现了线程争用这个锁 争用的线程就会自旋起来(默认的次数是10)此时锁升级为自旋锁

    重量级锁:10次之后会升级为重量级锁,向OS操作系统申请资源,此时的锁不在占用cpu,变为等待状态,但是锁只能升级不能降级

    Atomicl 、lock都是自旋锁,占用cpu但是不访问操作系统,是在用户态处理效率要高于内核态

    什么时候使用自旋锁,什么时候用重量级锁(OS进入等待队列)

    执行时间长的使用重量级锁,

  • 相关阅读:
    使用四则运算实现异或
    常用的数据库类别及介绍
    html中获取标签内的值的两种方法
    基于 Delphi 的前后端分离:之一
    Spring笔记(三)(Spring整合web环境)
    OpenCV4 图像处理与视频分析实战教程 笔记
    Netcode for Entities里如何对Ghost进行可见性筛选(1.2.3版本)
    NPDP考试需要携带什么?文具携带说明
    java基础
    Vue中多条件图片路径通过Map存储获取避免嵌套if-else
  • 原文地址:https://blog.csdn.net/lfssst/article/details/126295536