多线程中线程池和锁的使用

多线程的应用场景

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 

  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值