分布式与高并发(一)了解多线程的意义和使用

了解多线程的意义和使用

什么是线程

并发与高并发

客户端向Tomcat发送请求,在Tomcat中存在多个个并发数,这个并发数由什么决定的。
并发:单位时间内能够同时处理的请求数。
默认情况下Tomcat可以支持的最大请求数是150,也就是同时支持150个并发。

影响服务器吞吐量的因素

硬件

CPU,内存,磁盘,网络

软件

最大化的利用硬件资源
线程数量、JVM内存分配大小、网络通信机制(BIO、NIO、AIO)、磁盘IO

线程数量如何提升服务端的并发数量

并发和并行

并行:指两个或多个事件在同一时刻发生。多核CPU,可以同时处理多个线程。
并发:指两个或多个事件在同一时间间隔内发生。单核CPU在一段时间内可以给通过时间片的切换完成对多线程的处理。

多线程的特点

  • 异步
  • 并行
    多线程:是对cpu剩余劳动力的压榨,是一种技术。
    异步:强调的是非阻塞编程,是一种编程模式(pattern),主要解决UI响应被阻塞的问题,可借助线程技术或者硬件本身的计算能力解决。
    并行:同样是对CPU剩余劳动力的压榨,但是基于多线程技术,它强调的是高效完成计算任务,而不是并发量。

举例:有两辆车,需要从A开到B

  1. 阻塞式编程:先开回去一辆,再回来开另一辆。
  2. 传统异步变成:将一辆车快递,开一辆车回去。注意,快递公司派件(回调)时我不一定已经开到B,如果需要本人签收,会比较麻烦。–通过这种回调进行异步变成的方法,没有办法编写符合逻辑思维顺序的代码。
  3. 基于多线程的异步编程:获得瞬间移动的超能力(CPU算力提升)以毫秒级的速度在两辆车之间进行切换驾驶。其中一辆车(主线程)上友车载电话,可以处理其他事情。–期间频繁的上下文切换,会造成额外损耗,造成反应能力比较差,只能开到60迈。
  4. 并行变成:获得分身能力。两个人驾驶两辆车。充分发挥CPU的功能,没有额外上下文切换。

Java中的线程

  • Runnable
public class RunnableDemo implements Runnable{
    @Override
    public void run() {
        Thread.sleep(1000);
        System.out.println("come on baby");
    }


    public static void main(String[] args) {
        RunnableDemo runnableDemo = new RunnableDemo();
        Thread thread = new Thread(runnableDemo);
        thread.start();
        System.out.println("hello");
    }
}
  • Thread(类似Runnable)
  • Callable/feture(带返回值)
public class CallableDemo implements Callable<String> {

    //线程的控制是由操作系统控制,不由Java程序控制
    @Override
    public String call() throws Exception {
        System.out.println("come on baby!");
        Thread.sleep(3000);
        return "SUCCESS";
    }

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        ExecutorService executorService = Executors.newFixedThreadPool(1);
        CallableDemo callableDemo = new CallableDemo();
        Future<String> future = executorService.submit(callableDemo);
        System.out.println(future.get());   //线程阻塞
        System.out.println("hello");
    }

多线程使用情景

  1. 网络请求分发
  2. 文件导入
  3. 短信发送

注册时发送邮件
在这里插入图片描述

@postMapping("/sms/user")
public String register(User user) {
long start = System.currentTimeMillis();
userService.insert(user);
//异步
new Thread(()->{
	smsClient.sendSms("");
}).start() 
long end = System.currentTimeMillis();
return "SUCCESS" + (end - start);
}

在实际开发的时候使用线程池对线程进行管理。

 ExecutorService executorService = Executors.newFixedThreadPool(10);
@postMapping("/sms/user")
public String register(User user) {
long start = System.currentTimeMillis();
userService.insert(user);
//异步
executorService.submit(new Runnable(){
	@Override
	public void run(){
	smsClient.sendSms("");
	}
})
long end = System.currentTimeMillis();
return "SUCCESS" + (end - start);
}

线程的基础

线程的生命周期

阻塞:
WAITING
TIME_WAITING
BLOCKED
IO阻塞

Java的线程状态:

  1. 初始(NEW):新创建一个对象,但是还没有调用start()方法。
  2. 运行(RUNNABLE):Java线程中准备就绪(ready)和运行中(running)两种状态统称为“运行”。线程对象创建后,其他线程(比如main线程)调用了该对象的start()方法。该状态的线程位于可运行线程池中,等待被线程调度选中,获取CPU的使用权,此时处于就绪状态(ready)。就绪状态的线程在获得CPU时间片后变成运行中状态(running)。
  3. 阻塞(BLOCKED):标识线程阻塞与锁。
  4. 等待(WAITING):进入该状态的线程需要等待其他线程做出一些特定动作(通知或终端)。
  5. 超时等待(TIMED_WAITING):该状态与WAITING,它可以在指定的时间后自行返回。
  6. 终止(TERMINATED):表示线程已经执行完毕。

在这里插入图片描述
操作系统中的线程去除new和terminated状态,一个线程真实存在的状态只有:
ready:表示线程已经创建,正在等待系统调度分配CPU使用权。
running:表示线程获得了CPU使用权,正在进行运算。
waiting:便是线程等待(挂起),让出CPU资源给其他线程使用。
加上新建状态和死亡状态一共5种。

线程的启动

new Thread().start();	//启动一个线程
Thread t1 = new Thread();
t1.run();	//调用实例方法

实现原理:
首先Java调用Thread的start()方法,在start()方法中调用了JVM的start0()方法。jvm根据系统环境(例如:windows平台)调用os(操作系统)的创建线程方法create_thread然后调用线程:start_thread。
os中的线程调用jvm中的run方法:thread.run获取需要的run,而JVM需要去Java中调用run。
在这里插入图片描述

线程的终止

线程的终止:

  • 简单粗暴的方法:Thread.stop(),但是不推荐。原因:

    1. 调用stop()方法会立刻停止run()方法中剩余的所有工作,包括catch或finally语句中的,并抛出ThreadDeath异常(通常情况下此异常不需要显示的捕获),因此可能会导致清理性的工作得不到完成,如文件,数据库等的关闭。
    2. 调用stop()方法会立即释放该线程所吃用的锁,导致数据得不到同步,出现数据不一致的问题。
  • 标志位终止线程:

public class RunnableDemo extends Thread implements Runnable{
    @Override
    public void run() {
        while (!Thread.currentThread().isInterrupted()){
            System.out.println("come on baby");
        }
    }


    public static void main(String[] args) {
        RunnableDemo runnableDemo = new RunnableDemo();
        Thread thread = new Thread(runnableDemo);
        thread.start();
        System.out.println("hello");
        thread.interrupt(); //设置interrupt=true
    }
}
  • 使用Interceptor()中断线程

interrupt()的使用

但凡是让线程阻塞的机制,都抛出InterruptException异常
interrupt()方法并不像for循环语句中使用break语句那样干脆,马上就停止循环。调用interrupt()方法仅仅是在当前线程中打一个停止的标记,并不是真正的停止线程。
也就是说,线程中断并不会立即终止线程,而是通知目标线程,有人希望你终止。至于目标线程收到通知后会如何处理,则完全由目标线程自行决定(决定语句写在catch(InterruptException) 的语句中)。

所以Interrupt的作用:

  1. 设置一个共享变量的值true。
  2. 唤醒处于阻塞状态下的线程。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值