高并发2

17、线程池

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {

注意:这个提交任务的两个方法的区别。、

这里写图片描述

几个线程池的区别:
见博客:
https://www.cnblogs.com/ljp-sun/p/6580147.html

注意 这个 可以执行定时任务的线程池

Executors.newScheduledThreadPool(4);
ervice.scheduleAtFixedRate(()->{
            try {
                TimeUnit.MILLISECONDS.sleep(new Random().nextInt(1000));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName());
        }, 1, 3000, TimeUnit.MILLISECONDS);

    延迟1秒之后,每隔3秒去执行线程池里面的任务。。。。可以定时


这个线程池和Timer非常像, Timer相当于是一个定时器,定时去执行某些任务,也可以指定参数,只不过它不是多线程的。
![这里写图片描述](https://img-blog.csdn.net/20180419225228441?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTI1MzIwOTI=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)


18、 配置线程池

线程池不能滥用,,它的任务调度和任务管理 ,也要很大的开销。。。

这里写图片描述

19、死锁

所谓死锁:指的是两个或者两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象。。。
如果没有外界作用,他们就无法继续下去。。

一个线程要锁定两个对象才能完成,比较有意思的是 ,另外一个线程也要锁定两个对象。
所以解决死锁办法之一:别锁定俩对象,把锁的粒度变加粗一些,锁定当前整个对象,别锁定当前对象里面的两个小对象
//尽量只锁定一个对象

记一个例子就行
import lombok.extern.slf4j.Slf4j;

/**
 * 一个简单的死锁类
 * 当DeadLock类的对象flag==1时(td1),先锁定o1,睡眠500毫秒
 * 而td1在睡眠的时候另一个flag==0的对象(td2)线程启动,先锁定o2,睡眠500毫秒
 * td1睡眠结束后需要锁定o2才能继续执行,而此时o2已被td2锁定;
 * td2睡眠结束后需要锁定o1才能继续执行,而此时o1已被td1锁定;
 * td1、td2相互等待,都需要得到对方锁定的资源才能继续执行,从而死锁。
 */

@Slf4j
public class DeadLock implements Runnable {
    public int flag = 1;
    //静态对象是类的所有对象共享的   ----  注意这里是static的才可能形成死锁。
    private static Object o1 = new Object(), o2 = new Object();

    @Override
    public void run() {
        log.info("flag:{}", flag);
        if (flag == 1) {
            synchronized (o1) {
                try {
                    Thread.sleep(500);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                synchronized (o2) {
                    log.info("1");
                }
            }
        }
        if (flag == 0) {
            synchronized (o2) {
                try {
                    Thread.sleep(500);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                synchronized (o1) {
                    log.info("0");
                }
            }
        }
    }

    public static void main(String[] args) {
        DeadLock td1 = new DeadLock();
        DeadLock td2 = new DeadLock();
        td1.flag = 1;
        td2.flag = 0;
        //td1,td2都处于可执行状态,但JVM线程调度先执行哪个线程是不确定的。
        //td2的run()可能在td1的run()之前运行
        new Thread(td1).start();
        new Thread(td2).start();
    }
}

20、多线程并发最佳实践

1、
2、不可变类,比如String,一旦创建就不可改变了,不可变类可以降低代码中需要的同步数量
3、
有关并行的两个重要公式
【1】S = 1/F+1/n(1-F) Amdal 安达尔定律
【2】S = n - F(n-1) Gustafson 古斯塔夫森定律
S:加速比 n:处理器个数 a:串行时间 b:并行时间 F = a/(a+b):串行比例
这里写图片描述
这里写图片描述
这里写图片描述

21、spring与线程安全、

1、
spring作为一个IOC容器,帮我们管理了许许多多的bean,但是spring并没有保证这些对象的线程安全,这需要程序员自己编写解决线程问题的代码,spring对每个bean提供了scope属性来表示该bean的作用域,它是这个bean的生命周期。scope默认值是singleton、单例,单例的生命周期和spring的IOC容器是一致的。它会在第一次被注入时,创建。
prototype则是在每每次请求都会创建并返回一个新的实例。。

2、
被spring管理的大多数都是无状态的对象。(重要)

总之:spring根本就没有对bean的多线程安全问题作出任何的保证与措施,对个每个bean的线程安全问题,根本原因是因为每个bean的设计 没有在bean中声明任何有状态的实例变量或者类变量。、
也就是说无状态对象不管是单例、多例那都是线程安全的。。不过单例能够节省不断创建的对象与GC的开销,我们平时 使用单例的spring bean没有什么线程问题,很关键一点是,我们只是简单的使用,不涉及到修改这个bean的内部属性。。不会出现多个线程去修改bean中相同的变量的场景。。

(感觉这里写的不严谨)什么是无状态对象呢? 比如我们经常使用的DTO、VO,这些实体类对象。还有我们常用的service、dao、controller啊,都是,这些对象只是用来执行某些操作的,并没有自己的状态。比如dao就是执行数据库的CRUD操作,并且每个数据库的connection就是作为函数的局部变量,局部变量是分配在栈上的,是线程独享的。
关于有状态变量,看看博客:
https://www.cnblogs.com/xubiao/p/6567349.html
还有这个博客:这一篇写的比较详细和系统,优先看这个博客
https://blog.csdn.net/a236209186/article/details/61460211

插一句:springMVC对每个请求的拦截粒度是基于每个方法的,而strut2是基于每个类的.
如果把controller设置为多例的话,那将会频繁的创建和回收对象,这将严重的影响系统的开销。

如果必须要在bean中加入有状态的实例变量的时候,那么这时候就需要用threadlocal把变量变成线程私有的。
如果bean的实例变量需要在多个线程之间共享呢? 那就用synchronized或者lock 这些来实现线程同步。

如果对这些单例的bean不用做什么特殊处理,就不用关心多线程问题了,spring能做到原本的线程安全,就是因为它无状态的设计。。。。。

这里写图片描述

22、HashMap和ConcurrentHashMap

补充一下:2的N次方问题
因为计算机中取模的代价要远远高于位操作的代价,所以hashmap要求数组的长度必须是2的N次方,此时它将key的hash值和2的N次方-1 进行与运算。(用一种位运算,而不是取模)
补充:
HashMap计算索引的方式是h&(length-1),而Hashtable用的是模运算,效率上是低于HashMap的。
https://blog.csdn.net/chdjj/article/details/38581035
如果你传入的容量不是2的N次方,那hashmap会自动算出一个容量值。
这里写图片描述

haspmap在多线程 再哈希的 时候 容易出现死循环。原因就是扩容的方法没有加锁,多线程访问就会出问题,至于为什么出现死循环,画图说明一下即可。。(可能有点复杂)

而ConcurrentHashMap就是线程安全的 并发容器。。
JDK7中ConcurrentHashMap的结构仍然是数组+链表。。。
不同的是,它的外部不是一个大的数组,而是一个segment数组,每个segment包含一个和hashmap中差不多的链表数组,结构图见图:
在读写的时候,先取出key的hash值,将hash值的高N位对segment的个数取模,从而得到该key属于哪个segment。接着就像操作hashmap一样操作segment,为了保证不同的值均匀的分布到segment里,它计算哈希值也做了优化。。。这个segment是继承于JUC里的ReentrantLock,所以可以对每个segment加锁。就保证了线程安全。。
总之:JDK7的ConcurrentHashMap是基于分段锁来处理的。。
这里写图片描述

区别:
1、
ConcurrentHashMap:线程安全 key和value不 可以为null。。它可以在遍历的时候修改

HashMap:非线程安全。key和value可以为null。遍历时修改会出现ConcurrentModificationException。
java8当链表长度超过指定数默认8时,用红黑树实现。寻址复杂度从o(n)转换成了olog(n)

2、JDK8 为了进一步提高 并发度,废弃了分段锁的方案,

关于ConcurrentHashMap具体是怎么实现线程安全的:
可以看博客:
http://www.importnew.com/23610.html
也可以看博客: (
其实就是对散列的每个节点加锁,而且锁用的是synchronized )
代码中加锁片段用的是synchronized关键字,而不是像1.7中的ReentrantLock。这一点也说明了,synchronized在新版本的JDK中优化的程度和ReentrantLock差不多了。
https://blog.csdn.net/fouy_yun/article/details/77816587

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值