• java-多线程与锁


    概述:

      多线程的使用,锁,线程池,更多的锁。

    1:多线程的使用。

      1.1:使用多线程需要保证:原子性,可见性,有序性。

      1.2:启动多线程的方法:(不做 多余赘述,写两种常见的方式)

        继承Thread类,重写run方法。

        实现Runnable接口,重写run方法。

      1.3:这里引出一个方法  join()。(一个执行先后的方法)

        假设开启了两个线程T1,T2,如果同时 start 之后T1,T2的执行顺序比较随机,如果在T2的 run 方法中写上T1.join(),那么就是说T2在T1执行完之后执行。

    2:高并发保证线程安全:锁。

      2.1:分类:synchronized(内置锁),lock。

      2.2:区别:lock需要unlock方法手动解锁,synchronized在执行完锁的部分或者异常就是释放锁。

      2.3:对于synchronized的使用。(解决的是高并发情况多线程公用一个变量引起的线程安全问题)

        2.3.1:同步方法。

    public class Test {
    
        /**
         * 同步方法
         * 说明:锁的对象:这个类,即Test类,也叫this锁。
         */
        public synchronized void syncTest() {
            // TODO Auto-generated method stub
    
        }
    }

        2.3.2:同步代码块。

    public class Test {
        
        private Object object = new Object();
    
        /**
         * 同步代码块
         * 说明:锁的对象:
         *         这个类,即Test类,
         *         也可以的一对象。
         * 以下两种皆可
         */
        public void syncTest() {
            //锁的类
            synchronized (this) {
                
            }
            //锁的对象
            synchronized (object) {
                
            }
        }
    }

        2.3.3:静态同步代码。

    public class Test {
        
        /**
         * 同步代码块
         * 说明:锁的对象:
         *         这个类(即Test类)的字节码文件,
         */
        public static void syncTest() {
            synchronized (Test.class) {
                
            }
        }
    }

      2.4:延伸的概念。

        2.4.1:修饰词 threadLocal(解决的是多线程之间公用的变量隔离出来使用,不影响其他线程)

          概述:将共享变量私有化。具体使用网上有很多的帖子。

        2.4.2:修饰词 volatile (修改变量,快速写入主内存)

          解释说明:首先了解共享内存模型(jmm),即一个变量在内存中存于主内存,还有一块虚拟的私有内存(虚拟的,不存在的)。

            内存结构是我们常说的 jvm。

            普通变量:修改的是自己私有内存的变量,然后再同步到主内存中。

            被volatile 修饰的变量:1:会快速的被写入主内存,然后其他线程使用时候直接从主内存中拿取。我的理解是不是volatile 修饰的变量

                      一直操作的是主内存中的呢?(没有看过源码进行深入的研究,只是一个疑问,不过不太影响对这个的理解)

                      2:volatile 可以禁止指令重排序(指令重排是cpu为了提高程序的执行性能的一种方法)。

          对比synchronized:

            1:优点,volatile 不会造成线程阻塞,性能方面要由于锁。volatile 可以禁止指令重排序。

            2:缺点,volatile 是可以保证可见性,有序性,但是不能保证原子性,所以不能取代synchronized。

            总结:volatile 是轻量级的synchronized,如果有对变量进行操作等场景使用synchronized。

          使用场景:

            单例模式是个典型的应用。

            说明:变量需要volition修饰,变量的构造赋值部分是用synchronized修饰的,目的是T1进入了synchronized部分之后,

               如果对象还未完全构建成功,那么T2进入,发现判断的对象不为null,这样会存在问题。

        2.4.3:wait(),notify(),notifyAll()应用。(wait让线程进入等待状态,notify唤醒当前线程,notifyAll唤醒所以线程)

          假设场景 :假如对一个共享变量,T1进行set值,T2进行get值。我们要保证:T2 get值时候,变量里面要有值的,T1 set值时候变量是无指的。

               T1与T2中使用wait(),notify()组合。

          对比 sleep() :sleep等待多长时间继续执行,不会释放锁。wait会释放锁。

    3:线程池。

      3.1:为何使用线程池:

        频繁的以1的描述方式开启线程,关闭线程,很消耗性能,所以引入线程池。

      3.2:线程池种类:

        newCachedThreadPool:缓存线程池

        newFixedThreadPool:定长线程池

      3.3:spring项目中的应用。(自定义一个线程池)

    @Configuration
    @EnableAsync    //开启异步调用
    public class Test {
        
        /**
         * 详细的参数解释可参见,里面有相应的线程池参数配置规则
         * https://www.cnblogs.com/waytobestcoder/p/5323130.html
         * @return
         */
        @Bean("pool1")
        public Executor threadPool1() {
            ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
            //线程核心数目
            threadPoolTaskExecutor.setCorePoolSize(10);
            //核心线程,超时是否关闭,一般缓存线程池默认60S,其他线程池设置的是0S,我们默认这个也是60S
            threadPoolTaskExecutor.setAllowCoreThreadTimeOut(true);
            //最大线程数
            threadPoolTaskExecutor.setMaxPoolSize(10);
            //配置队列大小
            threadPoolTaskExecutor.setQueueCapacity(50);
            //配置线程池前缀,相当于@Bean("pool1")已经指定了使用的线程池名称
    //        threadPoolTaskExecutor.setThreadNamePrefix("pool1");
            //配置拒绝策略
            threadPoolTaskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());
            //数据初始化
            threadPoolTaskExecutor.initialize();
            return threadPoolTaskExecutor;
        }
        
    }
    @Async("pool1")
    public void test(){
            
    }

      如果调用了test(),就是使用了线程池pool1。

    4:更多的锁

      4.1:乐观锁,悲观锁,重入锁,自旋锁,分布式锁,CAS无锁

      CAS概述:三个值,初始值,期望值,新值。如果初始值=期望值,那么初始值=新值,否则代表有其他线程已经改了,再重新取值对比。

      

  • 相关阅读:
    剑指 Offer 56 II. 数组中数字出现的次数 II
    剑指 Offer 57 II. 和为s的连续正数序列
    剑指 Offer 63. 股票的最大利润
    剑指 Offer 55 II. 平衡二叉树
    剑指 Offer 59 II. 队列的最大值
    剑指 Offer 57. 和为s的两个数字
    剑指 Offer 64. 求1+2+…+n
    239. 滑动窗口最大值
    剑指 Offer 58 I. 翻转单词顺序
    剑指 Offer 60. n个骰子的点数
  • 原文地址:https://www.cnblogs.com/dblog/p/12158259.html
Copyright © 2020-2023  润新知