java并发包和多线程

本文深入探讨了Java并发编程的核心概念和技术,包括LockSupport的优势、Atomic类的特性、各种锁机制如ReentrantLock和StampedLock的工作原理,以及线程池的创建与管理。此外,还介绍了如何使用CountDownLatch和CyclicBarrier实现线程间的协调。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1. LockSupport比Object的wait/notify有两大优势

    ①LockSupport不需要在同步代码块里 。所以线程间也不需要维护一个共享的同步对象了,实现了线程间的解耦。

    ②unpark函数可以先于park调用,所以不需要担心线程间的执行的先后顺序。

2. atomic基本原子类

 compareAndSwapInt()通过原子的方式将期望值和内存中的值进行对比,如果两者相等,则执行更新操作。
 staticFieldOffset()和objectFieldOffset()两方法分别提供两静态、非静态域的偏移量计算方法。

3. ReentrantLock和ReentrantReadWriteLock,在读和写都有的情况下选则可重入读写锁

4. StampedLock是Java8引入的一种新的所机制,简单的理解,可以认为它是读写锁的一个改进版本,读写锁虽然分离了读和写的功能,使得读与读之间可以完全并发,但是读和写之间依然是冲突的,读锁会完全阻塞写锁,它使用的依然是悲观的锁策略.如果有大量的读线程,他也有可能引起写线程的饥饿

而StampedLock则提供了一种乐观的读策略,这种乐观策略的锁非常类似于无锁的操作,使得乐观锁完全不会阻塞写线程

5.乐观锁和悲观锁

  即加锁是一种悲观策略,无锁是一种乐观策略,因为对于加锁的并发程序来说,它们总是认为每次访问共享资源时总会发生冲突,因此必须对每一次数据操作实施加锁策略。而无锁则总是假设对共享资源的访问没有冲突,线程可以不停执行,无需加锁,无需等待,一旦发现冲突,无锁策略则采用一种称为CAS的技术来保证线程执行的安全性

CAS在cpu的支持下是原子性操作

 

 

6. ConcurrentLinkedDeque有数目不详的元素列表,你可以添加、阅读、或删除任何位置的元素。

    此外并发列表允许不同的线程列  表中添加或删除元素时不产生任何数据不一致, 非阻塞列表提供如下操作,

     如果操作不能立即完成,列出抛出异常或者返回一个null值

 

7. CountDownLatch利用它可以实现类似计数器的功能。比如有一个任务A,它要等待其他4个任务执行完毕之后才能执行,

    此时就可以利用CountDownLatch来实现这种功能了, countdown方法是对count进行减一操作, 最后在await(); 方法;

        检测count如果不为0,所有线程等待,未完成的线程返回结果; 最后统计返回个前端;

 

8. CyclicBarrier通过它可以实现让一组线程等待至某个状态之后再全部同时执行。叫做回环是因为当所有等待线程都被

  释放以后,CyclicBarrier可以被重用。我们暂且把这个状态就叫做barrier,当调用await()方法之后,线程就处于barrier了

 

9 CountDownLatch和CyclicBarrier区别:

    CountDownLatch和CyclicBarrier都能够实现线程之间的等待,只不过它们侧重点不同:

    CountDownLatch一般用于某个线程A等待若干个其他线程执行完任务之后,它才执行;

   CyclicBarrier一般用于一组线程互相等待至某个状态,然后这一组线程再同时执行;

    另外,CountDownLatch是不能够重用的,而CyclicBarrier是可以重用的。

 

10. Semaphore可以控同时访问的线程个数,通过 acquire() 获取一个许可,如果没有就等待,而 release() 释放一个许可。

    Semaphore其实和锁有点类似,它一般用于控制对某组资源的访问权限。

 

==============================多线程==================================================
1.Executor 是所有和线程有关的接口; 

2.ExecutorService 是Executor的子接口 , 开启线程池的方法;

   创建四种线程池:

   ①newFixedThreadPool 创建一个定长的线程,超出的线程会在队列中等待

   ②.newScheduledThredPool 创建一个定长的线程池,支持定时及周期性任务执行.

   ③.newCachedThreadPool 川建一个可缓存的线程池,如果线程池长度超过处理需要,可以灵活回收,若无可回收线程,则创建线程

   ④newSingleThreadExecutor 创建一个单线程化的线程池, 只会用唯一工作线程来执行任务,保证任务顺序执行优先级执行;

 

3. ThreadPoolExecutor实现了AbStractExectorServic,

     他的构造方法参数有:

        ①.核心线程数,默认核心线程一直存活

        ② 线程池所容纳的最大线程数,超过这个数会被阻塞.在LinkedBlockingDeque时候才无效

        ③. keepAliveTime ,非核心线程闲置时间,超过这个时间会被回收

        ④. unit, 闲置单位

        ⑤. 线程池中任务队列;  

              synchronousQueue:  在核心线程用完了,就会在最大线程数情况下创建线程,超过后抛异常;

                                                 它是一个特殊的队列,它的名字其实就蕴含了它的特征 – - 同步的队列。为什么说是同步的呢?

                                                 这里说的并不是多线程的并发问题,而是因为当一个线程往队列中写入一个元素时,

                                                 写入操作不会立即返回,需要等待另一个线程来将这个元素拿走;

                                                 同理,当一个读线程做读操作的时候,同样需要一个相匹配的写线程的写操作。

                                                 这里的 Synchronous 指的就是读线程和写线程需要同步,一个读线程匹配一个写线程。
 

              linkedBlockingDeque: 他就是超过核心线程数,就放在队列中排队;

                                                  就是一个双向队列,任何一端都可以进行元素的出入。底层基于单向链表实现的阻塞队列,

                                                   可以当做无界队列也可以当做有界队列来使用。

              ArrayBlockingQueue: 是一个阻塞式的队列,数组式的,性能不好,稳定

                                                 ArrayBlockingQueue 是 BlockingQueue 接口的有界队列实现类,

                                                底层采用数组来实现。其并发控制采用可重入锁来控制,不管是插入操作还是读取操作,

                                                 都需要获取到锁才能进行操作。

       ⑥. threadFactory 线程创建工厂 ,可以添加线程名称等等属性

       ⑦. RejectedEecutionHandler:  拒绝策略 当添加新线程被拒绝的时候,会执行他的方法;

 

4. ThreadPoolTaskExecutor 异步方法;

   ①.无返回值的任务使用execute(Runnable)

   ②.有返回值使用submit(Runnable) , 使用Future对象封装返回值.

         shutdown则只是将线程池的状态设置为shutdown,然后中断所有没有执行任务的线程,并将剩余的任务执行完。

         通过继承线程池,重写beforeExecute,afterExecute和terminated方法来在线程执行任务前,
        线程执行任务结束,和线程终结前获取线程的运行情况,根据具体情况调整线程池的线程数量。

        TaskExecutor.execute(new OverDueTask(orderInfoRedis, now))  异步方法;


   比如 渠道统计:

    一张渠道表:  设及到子父级渠道;

   一张用户注册表:  用户注册时,统计的百分比,

   一张用户详细信表: 还有用户详细表中用户渠道首次借款超过10天统计状态 到底统不统计

   一张订单表:  用户首次借款额度;借款成功次数,

   筛选条件为用户注册时间 开始日期到结束日期;

  首先就是判断用户选择日期间隔,是否大于10天

   大于10天: 两天一个线程  

   小于10天: 一天一个线程

线程5种状态:

1. 新建(NEW):新创建了一个线程对象。

2. 可运行(RUNNABLE):线程对象创建后,其他线程(比如main线程)调用了该对象的start()方法。该状态的线程位于可运行线程池中,等待被线程调度选中,获取cpu 的使用权 。

3. 运行(RUNNING):可运行状态(runnable)的线程获得了cpu 时间片(timeslice) ,执行程序代码。
4. 阻塞(BLOCKED):阻塞状态是指线程因为某种原因放弃了cpu 使用权,也即让出了cpu timeslice,暂时停止运行。直到线程进入可运行(runnable)状态,才有机会再次获得cpu timeslice 转到运行(running)状态。阻塞的情况分三种: 

(一). 等待阻塞:运行(running)的线程执行o.wait()方法,JVM会把该线程放入等待队列(waitting queue)中。
(二). 同步阻塞:运行(running)的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池(lock pool)中。
(三). 其他阻塞:运行(running)的线程执行Thread.sleep(long ms)或t.join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入可运行(runnable)状态。

5. 死亡(DEAD):线程run()、main() 方法执行结束,或者因异常退出了run()方法,则该线程结束生命周期。死亡的线程不可再次复生。

 

sleep和wait的区别:

1、sleep是Thread的静态方法,wait是Object的方法,任何对象实例都能调用。

2、sleep不会释放锁,它也不需要占用锁。wait会释放锁,但调用它的前提是当前线程占有锁(即代码要在synchronized中)。

3、它们都可以被interrupted方法中断。

原文链接:https://blog.youkuaiyun.com/a4227139/article/details/76644901

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值