多线程

多线程
  • 程序
    • 一系列的cpu指令
    • 执行的时候位于内存中
    • 在外存中储存
  • 进程
    • 运行中的程序,
    • cpu运输的基本单位
  • 线程
    • 线程中的一个片段
Thread类
  • 主要方法
  • 线程信息
  • static Thread currentThread() 获取线程名称 Returns a reference to the currently executing thread object.
  • String getName() 获取线程名称 Returns this thread’s name.
  • void setName(String name) 设置线程名称 Changes the name of this thread to be equal to the argument name.
  • boolean isAlive() 线程是否活着 Tests if this thread is alive.
  • void join() 添加进程,等到他运行完成 Waits for this thread to die.
  • 线程调节
  • static void sleep(long millis) 线程休眠,释放cpu资源,但监控器不释放,对象锁不释放 Causes the currently executing thread to sleep (temporarily cease execution) for the specified number of milliseconds, subject to the precision and
  • void interrupt() 打断进程 Interrupts this thread.
  • 线程启动
  • void run() 方法体,线程入口 If this thread was constructed using a separate Runnable run object, then that Runnable object’s run method is called; otherwise, this method does nothing and returns.
  • void start() 启动线程 Causes this thread to begin execution; the Java Virtual Machine calls the run method of this thread.
创建新线程方式
  1. 实现Runnable接口
    1. 通过具体类
      1. 初始化,写一个具体类,实现Runnable接口,run方法就是线程体,是线程的入口
      2. 创建,创建Thread类,具体类为实参
      3. 开始,调用线程的start方法,启动线程,会调用的run方法
      4. 停止,线程直接改变标志变量来停止不同循环,通知的方式停止线程
    2. 通过匿名内部类,的方式来实现Runnable接口,并重写需要执行的方法体
      • 匿名类的缺点是没有保存对象名及类名,不能对重写的匿名类和对象进行操作
    3. 原理
      1. 理解, 通过关联Runnable的方式来调用run方法体,来执行另一个线程
      2. 内存原理, 开辟另一个栈
      3. 代码原理, Thread对象的run方法调用runnable类的run方法,栈里两层run方法
  2. 继承Thread类
    1. 通过具体类继承
      1. 初始化,写一个类继承Thread,重写父类run方法
      2. 创建,创建具体类的对象,相当于创建创建了线程对象
      3. 调用这个对象的Start的方法
    2. 通过匿名内部类,的方式来继承Thread类,创建的同时重写run方法体
调度策略
  1. 调度策略
    • 时间片轮转
    • 抢占式:高优先度线程优先抢占资源
  2. java调度方法
    1. 同级优先度的线程组成先进先出对列(先到先服务),仍然使用抢占式策略
    2. 高优先度优先, 使用优先度的抢占策略
    • 线程的优先级控制
      • MAX_PRIORITY(10);
      • MIN _PRIORITY (1);
      • NORM_PRIORITY (5);
    • 涉及的方法:
      • getPriority() :返回线程优先值
      • setPriority(int newPriority) :改变线程的优先级
        • 线程创建时继承父线程的优先级
      • Thread类方法
        • static void yield():线程让步
          • 暂停当前正在执行的线程,把执行机会让给优先级相同或更高的线程
          • 若队列中没有同优先级的线程,忽略此方法
        • join() :当某个程序执行流中调用其他线程的 join() 方法时,调用线程将被阻塞,直到 join() 方法加入的 join 线程执行完为止
          • 低优先级的线程也可以获得执行
        • static void sleep(long millis):(指定时间:毫秒)
          • 令当前活动线程在指定时间段内放弃对CPU控制,使其他线程有机会被执行,时间到后重排队。
          • 抛出InterruptedException异常
          • 导致了程序暂停执行指定的时间,让出cpu该其他线程,但是他的监控状态依然保持者,当指定的时间到了又会自动恢复运行状态,在调用sleep()方法的过程中,线程不会释放对象锁。
        • stop(): 强制线程生命期结束
        • boolean isAlive():返回boolean,判断线程是否还活着
        • void interrupt() 打断 Interrupts this thread.
生命周期
  1. 新建: 创建Tread类对象,建栈,
  2. 就绪: 调用start方法,线程进入排队状态
  3. 运行: 线程获取到cpu资源,进行执行
  4. 阻塞: 当前资源得不到满足,无法执行,等待其他进程释放
  5. 死亡: 完成了当前进程所有任务,或被其他进程终止, 一旦死亡,不可以使用start重启,即每个创建的Thread对象只能启动一次

流程转换图

线程调用start方法启动时
获取到cpu资源
当前资源得不到满足,比如等待输入
获资源进入排队状态
当前任务完成,或被其他线程终止
新建
就绪
运行
阻塞
死亡
分类
  1. 守护线程
    • 保护用户线程
    • 服务用户线程,通过在start()方法前调用
    • thread.setDatemon(true)可以把一个用户线程变成一个守护线程
    • gc()线程,即垃圾回收器
    • jvm中都是守护线程,没有用户线程运行时当前jvm将退出。
    • mian方法不能设置为守护线程
  2. 用户线程
    • 重要
线程同步与安全
  • 问题提出,同时修改同一个数据
    • 多个线程对同一个数据的共享
    • 多个线程对同一账号的操作
    • 当前线程的条件判断和执行不在同一单位执行,导致条件改变依旧执行
  1. sysnchronized(this){ …} //
    • 对象锁,使用同步代码块,代码块需要一个同步锁对象:同一时间内,只允许一个线程进入此代码
    • 存在于Object里面
    • 一旦被锁定,则锁定内容具有原子性,不可分割
    • sysnchronized可以修饰run函数,表示要执行完才释放资源,不就和单线程就没什么区别了
    • sysnchronized(),参数为多锁对象,当执行到sysnchronized代码块时检查锁对象是否还在,还在则持有执行,没有等待
    • 多线程的对象锁必须时一个才能互斥
    • 最好的锁对象时常量
    • 隐式锁,自动解锁
  2. 缺点,效率降低
死锁问题
  1. 不同的线程分别占用字节所需的资源,等对方释放资源
//死锁问题举例
public class TestDeadLock {
static StringBuffer s1 = new StringBuffer();
static StringBuffer s2 = new StringBuffer();

    public static void main(String[] args) {
    
        new Thread() {                      //线程1,运行需要s1,s2并依次获取
            public void run() {
                synchronized (s1) {         //对象锁S1
                    s2.append("A");
                    synchronized (s2) {     //对象锁s1
                        s2.append("B");
                        System.out.print(s1);
                        System.out.print(s2);
        }}}}.start();
            
        new Thread() {                      //线程2,运行需要s2,s1,并依次获取
            public void run() {
                synchronized (s2) {         //对象锁s2
                    s2.append("C");
                    synchronized (s1) {     //对象锁s1
                        s1.append("D");
                        System.out.print(s2);
                        System.out.print(s1);
        }}}}.start();
    }
}

线程通信&对象锁方法
  1. wait()
    • 令当前线程挂起并放弃cpu资源,监视器,对象锁,使别的线程可以访问并修改共享资源,而当前的线程被唤醒之后再排队等候拿到锁资源之后再次执行
    • 结束等待需要其他线程使用notify唤醒,否则线程死亡。
  2. notify()
    • 唤醒正在排队等待同步资源的线程中优先级最好者结束等待
    • 只能通知一个线程
  3. notifyAll() ,通知多个线程,一般指通过wait方法睡眠的线程
  4. 对象锁方法在sysnchronized语句块中可以调用,其他位置调用报错
线程同步问题(一个线程不间歇的访问另一个线程的标志变量,不会察觉到标志变量的改变)
  • 解决方式加volatile
  • volatile //主存的数据不会子线程储存副本
  • 百度查询
  1. 问题来源&内存模型
    1. cpu速度远远大于主存,为了加速优化,cpu所需要用到的数据会在高速缓冲中保留副本,之行完当前指令会再刷新回去,单线程的化不存在任何问题,但当多线程的时候当主存的内存改变了,而cpu还在使用高速缓存中的数据(不同的cpu有不同的缓存,或者单cpu使用轮回来实现多线程的时候也有不同的缓存)
      • 解决方式:
        1. 总线加锁方式,类似于读写保护,当有cpu进行访问主存一个数据时,其他cpu不允许访问,直达当前cpu完成操作。早期采用方式,但是这种方式会导致效率低
        2. 缓存一致协议,当前cpu写数据的时候发现它时共享变量时,向其他cpu发送消息,表明其他缓冲中的数据无效,当其他cpu需要用到缓存数据时要再次从缓存中读取。例:Intel 的MESI协议
    2. 多线程中的几个概念(多线程会导致的问题)
      1. 原子性
        1. 原子性可保证当前执行不会被其他线程打断
        2. 原子性指当前命令是否由cpu一次执行完
      2. 可见性
        1. 修改共享数据会让相关的cpu立即可知
      3. 有序性
        1. 指令是否重排
        2. cpu为了优化加速,对没有拓扑关系的命令会进行重排,保证结果的正确性,但多线程会打乱这个过程
  2. volatile关键字的两层语义
    • 一旦一个共享变量(类的成员变量、类的静态成员变量)被volatile修饰之后,那么就具备了两层语义
      • 保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的。
      • 禁止进行指令重排序。
  3. 作用效果
    1. 使用volatile关键字会强制将修改的值立即写入主存;
    2. 使用volatile关键字的话,当线程2进行修改时,会导致线程1的工作内存中缓存变量stop的缓存行无效(反映到硬件层的话,就是CPU的L1或者L2缓存中对应的缓存行无效);
    3. 由于线程1的工作内存中缓存变量stop的缓存行无效,所以线程1再次读取变量stop的值时会去主存读取。
线程池
  • 系统启动一个新线程的成本是比较高的,因为它涉及与os交互。这种情况下,系统启动时即创建大量空闲的线程,就可以很好地提高性能,尤其是当程序需要创建大量生存期很短暂的线程时。

  • 除此之外,使用线程池可以有效地控制系统中并发线程的数量。避免因并发创建的线程过多,导致系统性能下降,JVM崩溃。

  • Java 5以前,需要手动创建自己的线程池;Java 5开始,新增了Executors工厂类产生线程池。

  • 使用线程池执行线程任务的步骤如下:

    1. 调用Executors 类的静态方法newFixedThreadPool(int nThreads),创建一个可重用的、具有固定线程数的线程池ExecutorService对象
    2. 创建Runnable实例,作为线程执行任务
    3. 调用ExecutorService对象的submit()提交Runnable实例
    4. 调用ExecutorService对象的shutDown()方法关闭线程池。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
本火锅店点餐系统采用Java语言和Vue技术,框架采用SSM,搭配Mysql数据库,运行在Idea里,采用小程序模式。本火锅店点餐系统提供管理员、用户两种角色的服务。总的功能包括菜品的查询、菜品的购买、餐桌预定和订单管理。本系统可以帮助管理员更新菜品信息和管理订单信息,帮助用户实现在线的点餐方式,并可以实现餐桌预定。本系统采用成熟技术开发可以完成点餐管理的相关工作。 本系统的功能围绕用户、管理员两种权限设计。根据不同权限的不同需求设计出更符合用户要求的功能。本系统中管理员主要负责审核管理用户,发布分享新的菜品,审核用户的订餐信息和餐桌预定信息等,用户可以对需要的菜品进行购买、预定餐桌等。用户可以管理个人资料、查询菜品、在线点餐和预定餐桌、管理订单等,用户的个人资料是由管理员添加用户资料时产生,用户的订单内容由用户在购买菜品时产生,用户预定信息由用户在预定餐桌操作时产生。 本系统的功能设计为管理员、用户两部分。管理员为菜品管理、菜品分类管理、用户管理、订单管理等,用户的功能为查询菜品,在线点餐、预定餐桌、管理个人信息等。 管理员负责用户信息的删除和管理,用户的姓名和手机号都可以由管理员在此功能里看到。管理员可以对菜品的信息进行管理、审核。本功能可以实现菜品的定时更新和审核管理。本功能包括查询餐桌,也可以发布新的餐桌信息。管理员可以查询已预定的餐桌,并进行审核。管理员可以管理公告和系统的轮播图,可以安排活动。管理员可以对个人的资料进行修改和管理,管理员还可以在本功能里修改密码。管理员可以查询用户的订单,并完成菜品的安排。 当用户登录进系统后可以修改自己的资料,可以使自己信息的保持正确性。还可以修改密码。用户可以浏览所有的菜品,可以查看详细的菜品内容,也可以进行菜品的点餐。在本功能里用户可以进行点餐。用户可以浏览没有预定出去的餐桌,选择合适的餐桌可以进行预定。用户可以管理购物车里的菜品。用户可以管理自己的订单,在订单管理界面里也可以进行查询操作。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值