多线程-笔记
多线程的应用场景
1、常见的浏览器、Web服务(现在写的web是中间件帮你完成了线程的控制),web处理请求,各种专用服务器(如游戏服务器)
2、servlet多线程
3、FTP下载,多线程操作文件
4、数据库用到的多线程
5、分布式计算,把大的任务分布成小任务去计算。
6、★
tomcat,tomcat内部采用多线程,上百个客户端访问同一个WEB应用,tomcat接入后就是把后续的处理扔给一个新的线程来处理,这个新的线程最后调用我们的servlet程序,比如doGet或者dpPost方法
7、后台任务:如定时向大量(100W以上)的用户发送邮件;定期更新配置文件、任务调度(如quartz),一些监控用于定期信息采集
8、自动作业处理:比如定期备份日志、定期备份数据库
9、异步处理:如发微博、记录日志
10、页面异步处理:比如大批量数据的核对工作(有10万个手机号码,核对哪些是已有用户)
11、数据库的数据分析(待分析的数据太多),数据迁移
12、★
多步骤的任务处理,可根据步骤特征选用不同个数和特征的线程来协作处理,多任务的分割,由一个主线程分割给多个线程完成
13、desktop应用开发,一个费时的计算开个线程,前台加个进度条显示
14、swing编程
一、大任务分割成小任务处理
1.1 队列
package com.esint.boot.controller;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;
/**
* 阻塞队列 重点掌握带星号3个
* // ★ArrayBlockingQueue : 由数组结构组成的有界阻塞队列
* // ★LinkedBlockingQueue : 由链表结构组成的由界(但大小默认值为integer.Max_value)的阻塞队列
* // PriorityBlockingQueue : 支持优先级排列的无界限阻塞队列
* // DelayQueue : 使用优先级队列实现的延迟无界阻塞队列
* // ★SynchronousQueue : 不存储元素的阻塞队列,也即是单个元素的队列
* // LinkedTransferQueue : 由链表组成的无界阻塞队列
* // LinkedBlockingDeque : 由链表组成的双向阻塞队列 此处的de代表double
* 数据结构:栈/队列
* 栈:后进先出
* 队列:先进先出
* T 是类;E是元素(集合汇总使用常见的结合:list,set ,map);KV是 key-value
*
* 队列Queue方法
* add 增加一个元索 如果队列已满,则抛出一个IIIegaISlabEepeplian异常
* remove 移除并返回队列头部的元素 如果队列为空,则抛出一个NoSuchElementException异常
* element 返回队列头部的元素 如果队列为空,则抛出一个NoSuchElementException异常
* offer 添加一个元素并返回true 如果队列已满,则返回false
* poll 移除并返问队列头部的元素 如果队列为空,则返回null
* peek 返回队列头部的元素 如果队列为空,则返回null
* put 添加一个元素 如果队列满,则阻塞
* take 移除并返回队列头部的元素 如果队列为空,则阻塞
*
*/
public class BlockQueueDemo1 {
public static void main(String[] args) throws InterruptedException {
BlockingQueue<String> blockingQueue = new ArrayBlockingQueue<>(3);
System.out.println("maruis------>" + "抛异常-----------------------");
// 往队列中插入元素
System.out.println("maruis------>" + blockingQueue.add("A"));
System.out.println("maruis------>" + blockingQueue.add("B"));
System.out.println("maruis------>" + blockingQueue.add("C"));
// 此处会爆出 .IllegalStateException: Queue full 的异常,队列已满
// System.out.println("maruis------>" + blockingQueue.add("D"));
// 移除时遵循先进先出的原则,打印结果 A B C
// 移除队列总的元素
System.out.println("maruis------>" + blockingQueue.remove());
System.out.println("maruis------>" + blockingQueue.remove());
System.out.println("maruis------>" + blockingQueue.remove());
// 检查队列中的队首元素,如果由表示队列中有队列,负责抛异常 java.util.NoSuchElementException
// System.out.println("maruis------>" + blockingQueue.element());
// 此处会包异常 java.util.NoSuchElementException 没有元素异常
// blockingQueue.remove();
// System.out.println("maruis------>" + "特殊值-----------------------");
System.out.println("maruis------>" + blockingQueue.offer("a"));
System.out.println("maruis------>" + blockingQueue.offer("b"));
System.out.println("maruis------>" + blockingQueue.offer("c"));
// // 此处不会抛出一样,返回false 表示插入元素不成功
// System.out.println("maruis------>" + blockingQueue.offer("d"));
// // 移除队列中的元素
System.out.println("maruis------>" + blockingQueue.poll());
System.out.println("maruis------>" + blockingQueue.poll());
System.out.println("maruis------>" + blockingQueue.poll());
System.out.println("maruis------>" + blockingQueue.poll());
// 检查队列中的队首元素
System.out.println("maruis------>" + blockingQueue.peek());
System.out.println("maruis------>" + "阻塞-----------------------");
blockingQueue.put("AA");
blockingQueue.put("BB");
blockingQueue.put("CC");
// 此时由于队列总装不下第四个元素,所以被阻塞
// blockingQueue.put("DD");
System.out.println("maruis------>" + blockingQueue.take());
System.out.println("maruis------>" + blockingQueue.take());
System.out.println("maruis------>" + blockingQueue.take());
// 此时也会被阻塞
// System.out.println("maruis------>" + blockingQueue.take());
System.out.println("maruis------>" + "超时-----------------------");
System.out.println("maruis------>" + blockingQueue.offer("aa"));
System.out.println("maruis------>" + blockingQueue.offer("bb"));
System.out.println("maruis------>" + blockingQueue.offer("cc"));
// 此处会等待3s 才返回false
System.out.println("maruis------>" + blockingQueue.offer("dd",3L,TimeUnit.SECONDS));
// 移除队列中的元素
System.out.println("maruis------>" + blockingQueue.poll());
System.out.println("maruis------>" + blockingQueue.poll());
System.out.println("maruis------>" + blockingQueue.poll());
System.out.println("maruis------>" + blockingQueue.poll());
}
}
1.2 Lock和synchronized该如何选择
Lock和synchronized的特点:
1)Lock是一个接口,而synchronized是Java中的关键字,synchronized是内置的语言实现;
2)synchronized在发生异常时,会自动释放线程占有的锁,因此不会导致死锁现象发生;而Lock在发生异常时,如果没有主动通过unLock()去释放锁,则很可能造成死锁现象,因此使用Lock时需要在finally块中释放锁;
3)Lock可以让等待锁的线程响应中断,而synchronized却不行,使用synchronized时,等待的线程会一直等待下去,不能够响应中断;
4)通过Lock可以知道有没有成功获取锁,而synchronized却无法办到。
5)Lock可以提高多个线程进行读操作的效率。
结论:
在性能上来说,如果竞争资源不激烈,两者的性能是差不多的,而当竞争资源非常激烈时(即有大量线程同时竞争),此时Lock的性能要远远优于synchronized。所以说,在具体使用时要根据适当情况选择。
1.3 8锁机制
package com.esint.boot.controller;
import java.util.concurrent.TimeUnit;
/**
* 八锁机制
* Description:
* 1. 标准访问,请问应emai先答l和是sms 对象锁 先email因为两个方法都是同步方法,线程调用同步方式时会先把这个线程锁住,只有执行完成后下一个线程才能使用。
* 2. 暂停4s中在邮件的方法中,请问是先答应右键还是sms 同1
* 3. 新增了普通的sayHello方法,请问是邮件还是 先sayHell 因为sayhello不是同步方法,所以不受锁的印象
* 4. 两部手机,请问是先打印Email还是sms 先sms 相当于两个对象所以,互不干扰,由于email中由延时,所以先执行sms
* 5. 两个静态同步方法,同一部手机,请问先打印email还是sms 先email 全局锁(相当于锁了模板)
* 6. 两个静态同步方法,二部手机,请问先打印email还是sms 先email 全局锁(同一个模板上)
* 7. 一个静态同步方法,一个普通同步方法,同一部手机,请问先email还是sms 先sms 一个锁了模板,一个锁了对象,不影响
* 8. 一个静态同步方法,一个普通同步方法,二部手机,请问先Email还是sms 先sms 一个锁了模板,一个锁了对象,不影响
*/
public class Lock8Demo05 {
public static void main(String[] args) throws InterruptedException {
Phone phone = new Phone();
Phone phone2 = new Phone();
// 1,2 为对象锁,一个对象中由多个synchronized方法 只要由一个线程去访问其中的一个方法那么这个对象久会被锁,其他线程将无法访问
// System.out.println("maruis------>"+"1");
// select8Block(1,phone,phone2);
System.out.println("maruis------>"+"2");
select8Block(2,phone,phone2);
// System.out.println("maruis------>"+"3");
// select8Block(3,phone,phone2);
// System.out.println("maruis------>"+"4");
// select8Block(4,phone,phone2);
// daiyou static 是全局锁,相当于锁了phone这个模板
// System.out.println("maruis------>"+"5");
// select8Block(5,phone,phone2);
// System.out.println("maruis------>"+"6");
// select8Block(6,phone,phone2);
// System.out.println("maruis------>"+"7");
// select8Block(7,phone,phone2);
// System.out.println("maruis------>"+"8");
}
public static void select8Block(int type, Phone phone, Phone phone2) throws InterruptedException {
switch (type) {
case 1: { // * 1. 标准访问,请问先答应email和是sms
new Thread(() -> {
try {
phone.sendEmail();
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "A").start();
new Thread(() -> {phone.sendSMS();}, "B").start();
}
break;
case 2: { // * 2. 暂停4s中在邮件的方法中,请问是先答应右键还是sms
new Thread(() -> {
try {
phone.sendEmail4s();
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "A").start();
new Thread(() -> {phone.sendSMS();}, "B").start();
}
break;
case 3: { // * 3. 新增了普通的sayHello方法,请问是邮件还是hello
new Thread(() -> {
try {
phone.sendEmail4s();
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "A").start();
new Thread(() -> {phone.sayHello();}, "B").start();
}
break;
case 4: { // * 4. 两部手机,请问是先打印Email还是sms
new Thread(() -> {
try {
phone.sendEmail4s();
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "A").start();
new Thread(() -> {phone2.sendSMS();}, "B").start();
}
break;
case 5: { // * 5. 两个静态同步方法,同一部手机,请问先打印email还是sms
new Thread(() -> {
try {
phone.sendEmailS();
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "A").start();
new Thread(() -> {phone.sendSMSS();}, "B").start();
}
break;
case 6: { // * 6. 两个静态同步方法,二部手机,请问先打印email还是sms
new Thread(() -> {
try {
phone.sendEmailS();
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "A").start();
new Thread(() -> {phone2.sendSMSS();}, "B").start();
}
break;
case 7: { // * 7. 一个静态同步方法,一个普通同步方法,同一部手机,请问先email还是sms
new Thread(() -> {
try {
phone.sendEmailS();
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "A").start();
new Thread(() -> {phone.sendSMS();}, "B").start();
}
break;
case 8: { // * 8. 一个静态同步方法,一个普通同步方法,二部手机,请问先Email还是sms
new Thread(() -> {
try {
phone.sendEmailS();
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "A").start();
new Thread(() -> {phone2.sendSMS();}, "B").start();
}
break;
}
}
}
class Phone {
// 同步方法
public synchronized void sendEmail() throws InterruptedException{
System.out.println("maruis------>" + "sendEmail");
}
// 同步方法
public synchronized void sendEmail4s() throws InterruptedException{
// 暂停4s钟
TimeUnit.SECONDS.sleep(4);
System.out.println("maruis------>" + "sendEmail");
}
// 静态同步方法
public static synchronized void sendEmailS() throws InterruptedException {
// 暂停4s钟
TimeUnit.SECONDS.sleep(4);
System.out.println("maruis------>" + "sendEmail");
}
// 同步方法
public synchronized void sendSMS() {
System.out.println("maruis------>" + "sendSMS");
}
// 静态同步方法
public static synchronized void sendSMSS() {
System.out.println("maruis------>" + "sendSMS");
}
// 普通方法
public void sayHello() {
System.out.println("maruis------>" + "sayHello");
}
}
结论:同步方法锁的是对象,静态同步方法锁的是模板,非同步方法不受锁的限制
1.4 线程安全
package com.esint.boot.controller;
import java.util.*;
import java.util.concurrent.CopyOnWriteArraySet;
/**
* 集合的线程安全问题
* Arraylist 的初始容量为10 扩容时是当前容量的一半 10,15,22,33。。。。
* HashMap 的初始容量为16 扩容时是当前容量的一拜 16 32 64 。。。。
* 扩容的方法:Arrays.copyOf(elements, len + 1)
* Arraylist 是线程不安全的,下面写一个例子证明arraylist线程不安全
* 步骤:
* 1.故障现象
* java.util.ConcurrentModificationException 并发修改异常
* 2.导致原因
* 多线程并发争抢资源造成的。
* 3.解决办法
* 3.1.Vector vector是线程安全,但是速率不高
* 3.2.Collections.synchronizedList(new ArrayList<>())
* 3.3.new CopyOnWriteArrayList<>() 写时复制技术
* 4.优化建议(同样的错误不犯第二次)
*
*/
public class notsafe003 {
public static void main(String[] args) {
Set set;
Collection collection;
List list;
Map map;
// 线程不安全
// List<String> data = new ArrayList<String>();
// List<String> data = new Vector<String>();
// List<String> data = Collections.synchronizedList(new ArrayList<>());
// List<String> data = new CopyOnWriteArrayList<>();
// 线程不安全
// Set<String> data = new HashSet<>();
Set<String> data = new CopyOnWriteArraySet<>();
// data.forEach(System.out::println);
for(int i =0;i<30;i++){
new Thread(()->{
data.add(UUID.randomUUID().toString().substring(0,8));
System.out.println("maruis------>"+data);
},String.valueOf(i)).start();
}
}
}
1.5 ★线程池7大参数
线程池的写法有3种,我们应该如何选择线程池呢?
答案是:自定义。
为什么要用多线程?
答案是:快!猛虎架不住群狼
package com.esint.boot.controller;
import java.util.concurrent.*;
/**
* 线程池的3打方法
*
* ★线程池的7大参数
* public ThreadPoolExecutor(int corePoolSize, // 核心线程数,线程池中最少保留的线程数
* int maximumPoolSize, // 线程池中的最大线程数
* long keepAliveTime, // 超时没人调用就用释放
* TimeUnit unit, // 超时的时间单位
* BlockingQueue<Runnable> workQueue, // 线程队列
* ThreadFactory threadFactory, // 线程工厂,线程用的,一般用默认的
* RejectedExecutionHandler handler) // 拒绝策略, maximumPoolSize+workQueue 后,再进来的线程就会进入拒绝策略进行处理
*
* ★拒绝策略
* new ThreadPoolExecutor.AbortPolicy(); // 抛出异常 java.util.concurrent.RejectedExecutionException
* new ThreadPoolExecutor.CallerRunsPolicy(); // 哪来的去哪儿,不抛异常
* new ThreadPoolExecutor.DiscardPolicy(); // 队列已满,丢掉任务,效率最高的形式 不抛异常
* new ThreadPoolExecutor.DiscardOldestPolicy(); // 队列已满,尝试去和最早的竞争,不抛异常
*
*/
public class MyThreadPool7PropertiesDemo1 {
public static void main(String[] args) {
// 线程池的3大方法
// 新建一个固定大小的线程池
// ExecutorService executorService = Executors.newFixedThreadPool(5);
// 新建一个单个线程池
// ExecutorService executorService = Executors.newSingleThreadExecutor();
// 新建一个可扩容的线程池遇强则强,遇弱则弱
// ExecutorService executorService = Executors.newCachedThreadPool();
// 由于这个三个方法底层调用都是下面的ThreadPoolExecutor这个类,并且由于线程池的设定与你的计算机的核数或者IO操作有关,所以按照阿里巴巴的代码规范,线程池最好采用自定的方式
/**
* 问题:池的线程最大值如何去设置!
* 了解:IO密集型,CPU密集型(调优)
* 最大线程到底该如何定义
* 1、CPU 密集型,几核的计算机就设置为几+1,可以保存CPU的效率最高!
* 自动获取计算机CPU处理器个数Runtime.getRuntime().availableProcessors()
* 2、IO 密集型 —> 判断程序中十分耗IO的线程,io操作会阻塞线程
* 2.1 由于io密集型任务线程并不是一直在执行任务,则应配置尽可能多 汝:CUP核数*2
* 2.2 根据IO线程数来设定,比如:15个大型IO线程任务,可以最大线程数为30。
* 2.3 ★某大厂使用的公式:CUP核数/(1 - 阻塞系数【0.8-0.9之间】),例如:8/(1 - 0.9) = 80 个线程
*
**/
// 获取CPU的处理器数量
int availableProcessors = Runtime.getRuntime().availableProcessors();
System.out.println("maruis----最大线程数-->" + availableProcessors);
int corePoolSize = 1;
// 核心线程数据设置为最大的一半
if (availableProcessors > 2) {
corePoolSize = availableProcessors / 2;
}
ExecutorService executorService = new ThreadPoolExecutor(
corePoolSize, // 核心线程数
availableProcessors, // 最大线程数据
30, // 超时没人调用就用释放
TimeUnit.SECONDS, // 超时的时间单位 秒
new LinkedBlockingDeque<>(3), // 线程队列 可容纳三个线程
Executors.defaultThreadFactory(), // 默认的线程工厂
new ThreadPoolExecutor.AbortPolicy() // 拒绝策略 抛出异常 当前当实际线程数超过availableProcessors+3(线程队列)时进入拒绝错略
);
for (int i = 0; i <7 ; i++) {
executorService.execute(()->{
System.out.println("maruis------>" + Thread.currentThread().getName()+"办理业务!!!");
});
}
executorService.shutdown();
new Thread(()->{},"A").start();
new Thread(new Runnable() {
@Override
public void run() {
}
},"B").start();
// 最大特点,有返回值
new Thread(new FutureTask<String>(()->{
return null;
}),"C").start();
}
}
问题:线程池的线程最大值如何去设置!
/**
* 了解:IO密集型,CPU密集型(调优)
* 最大线程到底该如何定义
* 1、CPU 密集型,几核的计算机就设置为几+1,可以保存CPU的效率最高!
* 自动获取计算机CPU处理器个数Runtime.getRuntime().availableProcessors()
* 2、IO 密集型 —> 判断程序中十分耗IO的线程,io操作会阻塞线程
* 2.1 由于io密集型任务线程并不是一直在执行任务,则应配置尽可能多 汝:CUP核数*2
* 2.2 根据IO线程数来设定,比如:15个大型IO线程任务,可以最大线程数为30。
* 2.3 ★某大厂使用的公式:CUP核数/(1 - 阻塞系数【0.8-0.9之间】),例如:8/(1 - 0.9) = 80 个线程
*
**/
1.6 线程的3个辅助类
1.6.1 关门走人
package com.esint.boot.controller;
import lombok.SneakyThrows;
import java.util.concurrent.CountDownLatch;
/**
* 线程的辅助类:共三个
* 1.CountDownLatch 英文直译:倒计时锁 latch:门栓
* 2.CyclicBarrier 英文直译:篱栅
* 3.Semaphore 英文直译:信号量
* CountDownLatch
* 题目:人走完关门。
* 自习室里有7个人,班长和6个同学,班长负责关门,需要6个同学都走后才能关门
*/
public class ThreadFuZhuClassdemo1 {
@SneakyThrows
public static void main(String[] args) {
// 定义一个线程的计数器
CountDownLatch countDownLatch = new CountDownLatch(6);
for (int i = 1; i <= 6; i++) {
new Thread(()->{
System.out.println("maruis------>" + Thread.currentThread().getName()+" : "+"同学走了");
// 执行下面代码计数器减一
countDownLatch.countDown();
},String.valueOf(i)).start();
}
// 此处判断计数器是否归0,如果不归零就一直阻断
countDownLatch.await();
System.out.println("maruis------>" + Thread.currentThread().getName()+" : "+"关门走人!!!");
}
}
1.6.2 人到齐了开会
package com.esint.boot.controller;
import lombok.SneakyThrows;
import java.util.concurrent.CyclicBarrier;
/**
* 线程的辅助类:共三个
* 1.CountDownLatch 英文直译:倒计时锁 latch:门栓
* 2.CyclicBarrier 英文直译:篱栅
* 3.Semaphore 英文直译:信号量
* CyclicBarrier
* 题目:人到齐了开会
* 由6个人参加会议,但是必须等到6个人都到齐了才能开始。
*/
public class ThreadFuZhuClassdemo2 {
@SneakyThrows
public static void main(String[] args) {
// 定义一个线程的加法计数器
CyclicBarrier cyclicBarrier = new CyclicBarrier(6,()->{
System.out.println("maruis------>" + Thread.currentThread().getName()+" : "+"开始开会!!!");
});
for (int i = 1; i <= 6; i++) {
new Thread(()->{
try {
System.out.println("maruis------>" + Thread.currentThread().getName()+" : "+"同志进入会议室");
// 当执行完上面的代码后当前线程久会进阻塞状态,直到cyclicBarrier中的线程达到6个。
cyclicBarrier.await();
} catch (Exception e) {
e.printStackTrace();
}
},String.valueOf(i)).start();
}
}
}
1.6.3 抢占停车位
package com.esint.boot.controller;
import lombok.SneakyThrows;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
/**
* 线程的辅助类:共三个
* 1.CountDownLatch 英文直译:倒计时锁 latch:门栓
* 2.CyclicBarrier 英文直译:篱栅
* 3.Semaphore 英文直译:信号灯
* Semaphore
* 题目:争车位
* 7个车去争夺3个车位,每个车在车位上停留3s
* 用途:控制多线程的并发数
*/
public class ThreadFuZhuClassdemo3 {
@SneakyThrows
public static void main(String[] args) {
// 定义一个线程的计数器
Semaphore semaphore = new Semaphore(3);
for (int i = 1; i <= 6; i++) {
new Thread(()->{
try {
// 占领一个车位,通知信号量(加一)
semaphore.acquire();
System.out.println("maruis------>" + Thread.currentThread().getName()+" : "+"车停进来了");
TimeUnit.SECONDS.sleep(2);
// 停车2s后走
System.out.println("maruis------>" + Thread.currentThread().getName()+" : "+"车离开了");
} catch (Exception e) {
e.printStackTrace();
}finally {
// 释放车位 通知信号量(减一)
semaphore.release();
}
},String.valueOf(i)).start();
}
}
}
二、tomcat内部采用多线程
2.1 tomcat线程配置,验证http请求中的多线程
在web请求中,一个请求就是一个线程,tomcat默认设置的线程池的原始线程为10个。
application配置
server:
tomcat:
threads:
# 初始线程数 默认10
min-spare: 10
# 最大线程数默认200 (建议这个配置数可以在服务器CUP核心数的200~250倍之间)
max: 100
# 线程等待队列默认100,超过最大线程后进入队列
accept-count: 100
# 一时间,tomcat能够接收的最大连接数
max-connections: 100
# 最长等待时间,如果没有数据进来,等待一段时间后断开连接,释放线程
connection-timeout: 12000
测试代码
/**
* 获取当前请求的线程
* @return
*/
@RequestMapping("/getthread")
public String getCurrentThread(){
String threadName = Thread.currentThread().getName();
System.out.println("maruis------>"+ this.getClass().getMethods() + threadName);
threadName="当前线程:"+threadName;
return threadName;
}
效果:
2.2 spring boot 线程监控
略,此处是spring cloud和jvm垃圾回收中的内容,以后有机会再分享。
2.3 模拟高并发工具Jmeter的使用
下载地址:https://jmeter.apache.org/download_jmeter.cgi
使用方法
第一步:下载完成后找个地方法解压,如下图找到jmeter.bat 启动
第二步:新建计划
第三步:测试
2.4 小实验:秒杀
package com.esint.boot.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* User: maruis
* Date: 2021/5/11 15:09
* Description:
*/
@RestController
@RequestMapping("/thread")
public class ThreadTest {
/**
* 获取当前请求的线程
* @return
*/
@RequestMapping("/getthread")
public String getCurrentThread(){
String threadName = Thread.currentThread().getName();
System.out.println("maruis------>"+ this.getClass().getMethods() + threadName);
threadName="当前线程:"+threadName;
return threadName;
}
// 商品数量
private int count = 4;
// 队列数量
private final int queueSize = count;
Lock lock = new ReentrantLock();
java.util.concurrent.
BlockingQueue<String> blockingQueue = new ArrayBlockingQueue<>(5);
@RequestMapping("/sell")
@ResponseBody
public String sellTest(){
String name = Thread.currentThread().getName();
// for (int i = 0;i<blockingQueue.size();i++){
// System.out.println("maruis------>" + blockingQueue.element());
// }
lock.lock();
String context = "";
try {
if(blockingQueue.size()==queueSize){
context= "已经抢光,请您下回早点来!!!!!";
System.out.println("maruis------>" + context);
// 此处需要注意,虽然这里已经return了,但是还是会执行finally 方法去释放锁
return context;
}
System.out.println("maruis------>" + blockingQueue.size());
if(count==0){
System.out.println("maruis------>" + "卖完了");
context = "卖完了";
}else{
// TimeUnit.SECONDS.sleep(1);
blockingQueue.add(name);
count--;
System.out.println("maruis------>" + "卖掉了一个商品,剩余库存:"+count);
context = "卖掉了一个商品,剩余库存:"+count;
// blockingQueue.remove(name);
}
} catch (Exception e) {
e.printStackTrace();
}finally {
System.out.println("maruis------>" + "huizhixingma ?????");
lock.unlock();
}
return "当前的线程:"+name+"############"+context;
//特别感谢:朱库里和鹏哥的协助。
}
}
2.4 死锁现象
使用lock锁的时候,一定要注意死锁现象,死锁以后,由于锁得不到释放,其他的线程将无法进入被锁的代码,造成线程卡死
举例:还是上面的秒杀代码
// 商品数量
private int count = 4;
// 队列数量
private final int queueSize = count;
Lock lock = new ReentrantLock();
java.util.concurrent.
BlockingQueue<String> blockingQueue = new ArrayBlockingQueue<>(5);
@RequestMapping("/sell")
@ResponseBody
public String sellTest(){
String name = Thread.currentThread().getName();
// for (int i = 0;i<blockingQueue.size();i++){
// System.out.println("maruis------>" + blockingQueue.element());
// }
lock.lock();
String context = "";
// try {
if(blockingQueue.size()==queueSize){
context= "已经抢光,请您下回早点来!!!!!";
System.out.println("maruis------>" + context);
// 此处需要注意,虽然这里已经return了,但是还是会执行finally 方法去释放锁
return context;
}
try {
System.out.println("maruis------>" + blockingQueue.size());
if(count==0){
System.out.println("maruis------>" + "卖完了");
context = "卖完了";
}else{
// TimeUnit.SECONDS.sleep(1);
blockingQueue.add(name);
count--;
System.out.println("maruis------>" + "卖掉了一个商品,剩余库存:"+count);
context = "卖掉了一个商品,剩余库存:"+count;
// blockingQueue.remove(name);
}
} catch (Exception e) {
e.printStackTrace();
}finally {
System.out.println("maruis------>" + "huizhixingma ?????");
lock.unlock();
}
return "当前的线程:"+name+"############"+context;
}
演示时jvm配置
java7的配置
-Xmx=125m -Xms=125m -XX:PermSize=256m -XX:MaxPermSize=256m
java8的配置
-server -Xmx25m -Xms25m -XX:MetaspaceSize=25m -XX:MaxMetaspaceSize=25m -XX:+PrintGCDetails
-server -Xmx125m -Xms125m -XX:MetaspaceSize=125m -XX:MaxMetaspaceSize=125m -XX:+PrintGCDetails