Java多线程01-------多线程的初级使用梳理

妖狐艹你老母 2022-05-31 13:26 333阅读 0赞

这一章主要说说什么是多线程,以及,多线程是怎么使用的。

简单来说:多线程最主要的目的就是合理的利用CPU的资源
当然,随着CPU越来越快,多线程方法变得没那么必要,但是,他仍然可以很好的节省资源。
例如:当我们执行代码像数据库索取资源,并不会那么快得到。此时,就可以灵活的执行其他的代码块。
让运算部分只运算,不用关注业务逻辑。

多线程在项目中最常见的应该是synchronized关键字的使用,所以这一章主要讲讲多线程使用的条件以及synchronized关键字的使用。

1.多线程的流程

重点:线程的作用是处理包裹

  • 线程一开始处于线程池。
  • 此时线程会进入就绪状态。
  • 然后,会随机挑选就绪状态的线程进行运行,优先级越高,越会优先
  • 此时线程池的线程不会被销毁,直到系统重启,所以在里面会有以下操作:当被选中时:

    1. a. 第一步:从包裹中取出参数。
    2. 打开包裹,里边有个HttpRequest对象,可以取到userName password两个参数。
    3. b. 当提交给数据库后,会根据运行情况,在执行的过程中随时!!可能被打断,让出CPU车间
    4. c. 等到数据来了,仍然得到就绪步骤,即处在就绪状态,等待CPU挑选
    5. d. 一旦出现硬盘、数据库这样耗时的操作,也得让出CPU去等待;

此处稍稍说一下线程的优先级:
线程的优先级并不是那么严格,系统会优先给予优先级高的资源,但是,并不是表示优先级高就一定会先执行完。因为线程的选择具有随机性,当优先级差不多的时候,甚至出现无效的现象

2.多线程的使用

  • 1.继承Thread 类
  • 2.实现Runable接口

    //main
    public class main {

    1. public static void main(String[] args){
    2. MyRunable myRunable = new MyRunable();
    3. Thread runableThread = new Thread(myRunable);
    4. Mythread mythread = new Mythread();
    5. runableThread.start();
    6. mythread.start();
    7. }

    }

    public class MyRunable implements Runnable{

    1. @Override
    2. public void run() {
    3. System.out.println("Runable success");
    4. }

    }

    public class Mythread extends Thread{

    1. @Override
    2. public void run(){
    3. System.out.println("Thread SUCCESS");
    4. }

    }

结果:
这里写图片描述
两者的作用是一样的,thread类同样继承了Runable接口

  1. public class Thread implements Runnable

但是,由于Java的单继承的关系,使得我们在继承了其他类的时候还需要使用多线程就只能通过实现接口的方式

正因为Thread实现了Runable接口,才有Thread(Runable runable)的方法,这类方法还有很多,可以在API手册里面查询。

3.共享数据和不共享数据

好了,我们进一步的去梳理多线程的重点。

什么叫共享数据和不共享数据呢?
这就是多线程的特点,用一种很形象的解释就是:
我们要取6个苹果:

  • 共享数据:让一个人取六次
  • 不共享数据:让六个人分别取一次

当然,这不是关键:
关键是:每个人身上有个计步器
所以,那个人走了六倍于其他人,这就造成了数据的不同。

用代码来解释:

  1. //数据不同步
  2. Mythread mythread01 =new Mythread();
  3. Mythread mythread02 =new Mythread();
  4. Mythread mythread03 =new Mythread();
  5. mythread01.start();
  6. mythread02.start();
  7. mythread03.start();
  8. public class Mythread extends Thread{
  9. int i = 1;
  10. @Override
  11. public void run(){
  12. while (i<5) {
  13. i++;
  14. System.out.println("当前线程"+this.currentThread().getName()+"数字"+i);
  15. }
  16. }
  17. }
  18. ///////////////////////////////////////////////////////
  19. //数据同步
  20. Mythread mythread =new Mythread();
  21. Thread mythread01 =new Thread(mythread);
  22. Thread mythread02 =new Thread(mythread);
  23. Thread mythread03 =new Thread(mythread);
  24. mythread01.start();
  25. mythread02.start();
  26. mythread03.start();

观察发现:
数据不同步:尽管打印循序不一样,但是数字减少是逐次减少的
数据同步:数字跳着减少了,一个数字打印了多回,数据被污染
这里写图片描述

数据同步:
这里写图片描述

4.synchronized

重点,也是我们最常用的地方:
上锁(synchronized:可以给任何对象或者方法上锁),上锁的代码被称为互斥区,顾名思义,就是我用了你就不能用,上锁的代码是会依次执行。

当调用一个方法时,会判断是否上锁,若上锁,必须等待别人用完才会执行:

  1. synchronized public void run(){
  2. while (i<10) {
  3. i++;
  4. System.out.println("当前线程"+this.currentThread().getName()+"数字"+i);
  5. }
  6. }

很明显:变得正常了
这里写图片描述

4.1死锁

  1. **死锁是上锁后才会出现的情况,大概就是:相互持有了对方的锁。**

A方法执行过程中发现:他要执行的方法的被B方法持有这,所以他要等B执行完才行!
此时B又发现,他要继续执行的方法的锁被A持有这,A不执行完他也执行不了。

所以:
此时AB均不能执行完。也均不能释放锁。所以,死循环锁死—即死锁
所以,一般有一条原则:
资源越大的锁先获得,从最大的加锁!!

4.2i++和system

  1. 这是我们需要警惕的一个地方,system可以轮流打印,但是就像前文说的,i++运算并不会受影响,必要的时候,我们要给参数上锁。

5.停止线程

停止线程的方式有很多种,这里只是简单列举一下:









































方式 详细 代码
自然停止 方法执行完
stop 强行使用stop命令停止线程,不推荐 thread.stop()
interrupt 暂停线程,注意,线程只是暂停,并没有停止 mythread.interrupt();
异常法 interrupt暂停后制造异常,达到停止的目的,这种有两种,线程里面的异常(线程后面的不会执行)和主函数里面的异常(线程中的还是会执行完)
return interrupt+return停止线程
suspend 暂停线程(会独占资源,造成不同步等现象)

停止异常的方法大概就这些,详细说说几点:

  • 异常法:
    异常法就是在暂停线程后,通过try…catch进入interruptedException,因为是异常处理的机制,并不会完全影响到程序的运行,但是停止了线程
  • stop()暴力停止的缺点:
    stop()强行停止会出现解锁,污染数据,简单点说,就是更改数据的时候中断,且对数据的锁定被解开了,其他的方法可以修改数据,造成了数据的污染
  • sleep后停止线程:此方法会报错,不要在线程睡眠的时候停止

6.常用方法

常用方法没篇都会汇总:






























































方法 作用
start() 线程启动
stop() 线程停止,已废弃
sleep() 可指定时间让线程休眠
interrupt() 暂停线程
suspend() 暂停线程,有缺点
resume() 恢复线程
interrupted() 测试线程是否已经中断 (使用后会改变线程中断状态。。相当于启动线程)
isInterrupted() 测试线程是否已经中断
currenThread() 当前代码段被什么线程调用(getName())
isAlive() 当前线程是否活动
getId() 取得线程唯一标识
yieId() 放弃当前CPU资源,回到线程池(因为线程池的特点,可能会马上又被启用)
setPriority() 设置线程优先级

7.总结

线程最简单的用法就梳理到这里,后面就是对于synchronized的详细使用以及线程间通信的一些问题,慢慢道来。

8.参考资料

《Java多线程编程核心技术》

发表评论

表情:
评论列表 (有 0 条评论,333人围观)

还没有评论,来说两句吧...

相关阅读

    相关 01. java 线

    java 多线程系列文章列表, 请查看目录: [《java 多线程学习笔记》][java] 预留一篇做总结 [java]: https://blog.csdn.n...

    相关 Java线研究01-创建线

    线程与进程 ● 操作系统支持的线程至少会有四种状态:就绪、执行、阻塞和终结。线程在四种状态下进行切换,都是要消耗不少的CPU计算能力的。 ● 并且根据操作系统使用线程