juc相关
补充:并发安全问题
当使用多线程时,发生并发安全的会在同一个类对象中,且对于成员变量容易发生并发安全。局部变量不会导致并发安全,因为每调用一次方法,都会有一个独立的栈帧存放各自的变量信息。
1. 实现生产者消费者问题要使用while循环,不可以使用if否则会导虚假唤醒。
2. 用synchronized实现生产者和消费者用的是wait和notify,用lock可以调用newCondition方法用的是await和signal而且可以让线程交叉执行
lock lock = new reantlock()
condition condition =lock.newCondition
condition.await()
condition.signal()
synchronized加在普通方法锁的是当前的对象,即方法的调用者,锁的执行顺序是先拿到锁先执行,如果新建两个对象调用锁的方法那么默认先按顺序 如果一个阻塞了则先执行另一个。
synchronized锁的是静态方法时锁的是class对象,一个类只能有一个class对象,如果说既调用了锁的静态方法又调用了普通锁方法,不管哪个在前哪个在后一定会先执行静态的那
个 ,如果一个锁的静态方法被两个对象调用了,那么默认先按顺序 如果一个阻塞了则先执行另一个。
synchronized方法体比较便捷,就是想锁普通就锁普通,想锁类就锁类
3. copyonwritearraylist是线程安全的,和vector区别在于它是用lock锁实现的,Vector 添加,查询的方法都添加了同步锁,在写操作时,其他线程是不能读的,效率很低。
但是在操作时读操作是比较多的,但是读不会改变数据,一次只允许一个线程读数据。所以我们需要一个更好的集合来提升并发时读的效率。选择了copyonwritearraylist
4. callable可以返回值也可以抛出异常,call方法代替run方法。
和继承runable接口一样,先实现callable接口并且重写call方法(call方法中可以return一个值),runablefuture接口下有一个实现类叫做FutureTask,runablefuture接口继承了runable接口和future接口(future是用来在主线程中开辟一个新的线程来处理耗时的业务的,这里先不研究)而FutureTask有一个参数类型为callable的构造方法,new thread中的参数为new Runable就等价于new FutureTask,那么futuretask就可以充当runable当作参数传递到new thread中,就可以调用参数类型为callable的了,调用futuretask中的get方法可以获取到返回值,get可能会阻塞。
ThreadMythread这个类中继承了callable接口。
ThreadMythread myThread = new ThreadMythread();
FutureTask task =new FutureTask(myThread);
new Thread(task,"线程一").start();
system.out.println(task.get());
5. CountDownLatch(其实作用和join差不多,都是等待当前线程执行完毕再执行另一个线程,只不过countdownlatch适用于等待某些线程执行完毕,不能循环)
6. CyclicBarrier(每执行一个线程都会等待,给定的参数parties 是4,那么只有当等待的线程个数为4的时候冲破栅栏接着执行其它线程(CyclicBarrier构造函数的lambda函数可以当作此处所说的其他线程,注意它不在等待队列中),可以循环,如果4后面还有四个线程执行,可以再冲破一次栅栏,如果不满足4个线程则会阻塞,CyclicBarrier构造函数的lambda函数每冲破一次栅栏都会执行一次,但是不保证执行顺序)
已连接到目标 VM, 地址: ''127.0.0.1:50756',传输: '套接字''
运动员 2 准备就绪...
运动员 4 准备就绪...
运动员 3 准备就绪...
运动员 1 准备就绪...
运动员 5 准备就绪...
所有运动员已到达起点,比赛开始!
运动员 5 开始跑步!
运动员 1 开始跑步!
运动员 4 开始跑步!
运动员 3 开始跑步!
运动员 2 开始跑步!
与目标 VM 断开连接, 地址为: ''127.0.0.1:50756',传输: '套接字''
进程已结束,退出代码为 0
7. Semaphore(可以用来限流,每次只可以执行指定数量的线程),它通过维护若干个许可证来控制线程对共享资源的访问。 如果许可证剩余数量大于零时,线程则允许访问该共享资源;如果许可证剩余数量为零时,则拒绝线程访问该共享资源。 Semaphore所维护的许可证数量就是允许访问共享资源的最大线程数量。 所以,线程想要访问共享资源必须从Semaphore中获取到许可证。
内部有acquire方法和release方法。 当调用acquire方法时线程就会被阻塞(new Semaphore时指定了他的许可证的初始值,每调用一次acquire,如果acquire值大0的话许可证就会减1,需要调用release方法释放许可证后,许可证就会加1,才会继续执行其他线程),直到Semaphore中可以获得到许可证为止,然后线程再获取这个许可证。 当调用release方法时将向Semaphore中添加一个许可证,如果有线程因为获取许可证被阻塞时,它将获取到许可证并被释放;如果没有获取许可证的线程,Semaphore只是记录许可证的可用数量。
Semaphore内部主要通过AQS(AbstractQueuedSynchronizer)实现线程的管理。Semaphore在构造时,需要传入许可证的数量,它最后传递给了AQS的state值。线程在调用acquire方法获取许可证时,如果Semaphore中许可证的数量大于0,许可证的数量就减1,线程继续运行,当线程运行结束调用release方法时释放许可证时,许可证的数量就加1。如果获取许可证时,Semaphore中许可证的数量为0,则获取失败,线程进入AQS的等待队列中,等待被其它释放许可证的线程唤醒。
默认是非公平锁,所以不会按照顺序交替实现,如果需要用到公平锁,就和reentrylock一样调用对应的构造方法传入true。
下面这个代码模拟的是当前有4个车位,只有当有人离开了才可以进来新车。
8. ReadWriteLock(读写锁)
读-读(不阻塞)
读-写(阻塞)
写-写(阻塞)
9. 阻塞队列(BlockingQueue)适用于生产者消费者
也就是说,它是有限的。如果该阻塞队列到达了其临界点,负责生产的线程将会在往里边插
入新对象时发生阻塞。它会一直处于阻塞之中,直到负责消费的线程从队列中拿走一个对象。
负责消费的线程将会一直从该阻塞队列中拿出对象。如果消费线程尝试去从一个空的队列中
提取对象的话,这个消费线程将会处于阻塞之中,直到一个生产线程把一个对象丢进队列。不能插入null会空指针异常
Queue队列是collection下的一个接口,BlockingQueue继承了Queue
四组api:
方式 | 抛出异常 | 有返回值,不抛出异常 | 阻塞,等待(一直等待) | 超时等待(等待超过指定时间没有执行退出) |
添加 | add | offer() | put() | offer(参数,参数,参数) |
移除/取元素 | remove | poll() | take() | poll(参数,参数) |
检测队首元素 | element | peek() | - | - |
10. 同步队列SynchronousQueue
11. 线程池
线程池的好处:1.降低资源的消耗 2.提高响应速度 3.方便管理
阿里巴巴开发手册:线程池不允许使用Excutors创建,而是通过ThreadPoolExcutor的方式。
execute和submit都可以执行线程,但是推荐下submit,它既可以执行不带返回值的线程,也可以执行带返回值的,而execute只能执行不带参数的
原因:
newFixedThreadPool 和 newSingleThreadExcutor : 允许的请求队列长度为 Integer.MAX_VALUE ,可能会堆积大量的请求,从而导致 OOM 。
newCachedThreadPool 和 newScheduledThreadPool : 允许的最大创建线程数量为 Integer.MAX_VALUE ,可能会创建大量的线程,从而导致 OOM 。
newFixedThreadPool: 可以给定一个线程池中线程数量,线程数量是固定的
newSingleThreadExcutor: 和fix差不多,只不过这里的线程数量已经确定为1不需要给定参数,线程数量固定为1
newCachedThreadPool: 线程数量是弹性的根据当前线程多少来确定,最大为Integer.MAX()大约为21亿
newScheduledThreadPool : 可以设置定时任务,返回的是ScheduledExcutorService对象,可以指定线程的数量,但是最大线程数量也是21亿
newSingleThreadScheduledExecutor(): 定时任务的单线程池,可以创建定时任务
newWorkStealingPool(): 根据cpu来创建线程池的
举例:
自定义线程池ThreadPoolExecutor:
ThreadPoolExecutor七大参数
LinkedBlockingDeque: 使用双向队列实现的有界双端阻塞队列。双端意味着可以像普通队列一样 FIFO(先进先出),也可以像栈一样 FILO(先进后出)。
LinkedTransferQueue: 它是ConcurrentLinkedQueue、LinkedBlockingQueue 和 SynchronousQueue 的结合体,但是把它用在 ThreadPoolExecutor 中,和 LinkedBlockingQueue 行为 一致,但是是无界的阻塞队列。
注意有界队列和无界队列的区别:如果使用有界队列,当队列饱和时并超过最大线程数时就会执行拒绝策略;而如果使用无界队列,因为任务队列永远都可以添加任务,所以设置 maximumPoolSize 没有任何意义。
12. 四大原生接口函数(一个两个泛型一个参数有返回值的函数,一个有一个泛型有一个参数和返回值的函数,一个有一个泛型没有参数有返回值的函数,一个有一个泛型有一个参数没有返回值的参数)
任意的接口函数都可以改用lambda函数进行改写
1. function函数(传入T类型函数,返回一个R类型的函数)apply方法

2. predicate(断定型接口)传一个值,返回值为boolean,重写的是test方法
3. Supplier(供给型函数,给定什么泛型就返回什么类型的结果,不带参数,可以在get方法中实现最后是泛型类型的业务)
4. Consumer(消费型接口,没有返回值,只有一个泛型类型参数,给定一什么泛型类型就用这个泛型类型做一些业务,没有返回值解释为消耗掉了,重写accept方法)
__EOF__

本文链接:https://www.cnblogs.com/liu-jin/p/17268755.html
关于博主:hello~好久不见,喜欢的话点个赞吧
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角【推荐】一下。您的鼓励是博主的最大动力!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY