线程

进程和线程

  • 进程

    • 一个应用程序的实例
    • 是系统进行资源分配和调度的基本单位,是操作系统结构的基础。
  • 线程

    • 属于进程中的一个执行单元,线程是通过cpu执行的基本单元
    • 一个进程中至少有一个主线程存在,主线程可以去申请子线程

cpu执行任务是通过调用进程中的线程
一台电脑中 同时又很多个进程在运行状态
这些进程,又同时有很多个线程在运行状态
单核cpu 同一时间只能调用一个线程

java中一个main方法一个进程,并且开启一个主线程,每个线程有个ID,在java中线程类是Thread类

Thread.currentThread()获取当前线程运行实例对象
Thread.currentThread().getId()获取当前运行的线程ID

JVM启动时就启动了多个线程,至少有两个线程可以分析的出来

  1. 执行main函数的线程
    • 该线程的任务代码都定义在main函数中
  2. 负责垃圾回收的线程

线程的状态

  1. 新建状态(New):新创建了一个线程对象。
  2. 就绪状态(Runnable):线程对象创建后,其他线程调用了该对象的start()方法。该状态的线程位于可运行线程池中,变得可运行,等待获取CPU的使用权。
  3. 运行状态(Running):就绪状态的线程获取了CPU,执行程序代码。
  4. 阻塞状态(Blocked):阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。阻塞的情况分三种:
    (一)、等待阻塞:运行的线程执行wait()方法,JVM会把该线程放入等待池中。(wait会释放持有的锁)
    (二)、同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池中。
    (三)、其他阻塞:运行的线程执行sleep()或join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。(注意,sleep是不会释放持有的锁)
  5. 死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。

创建线程的方法

在java中开启线程有3种方法:

  1. 继承Thread类
  2. 实现Runnable接口
  3. 线程池(这里暂时先不写了)
创建线程方式一:继承Thread类
  1. 定义一个类继承Thread类
  2. 覆盖Thead类中的run方法

    为什么要覆盖run方法?

    创建线程的目的是为了开启一条执行路径,去运行指定的代码和其他代码实现同时运行。
    而运行的制定代码就是这个执行路径的任务,jvm创建的主线程的任务都定义在主函数中

    而自定义的线程它的任务在哪呢?

    Thread类用于描述线程,线程是需要任务的,所以Thread类也对任务的描述。
    这个任务即使通过Thread类中的run方法来体现,也就是说,run方法就是封装自定义线程运行任务的函数

    run方法中定义就是线程要运行的任务代码

    开启线程是为了运行指定代码,所以只有继承Thread类,并覆盖run方法。将运行的代码定义在run方法中即可

  3. 直接创建Thread的子类对象,并启动 object.start();
大专栏  线程建线程方式二:通过接口的形式完成 Runnable的实现
  1. 定义类实现 implements Runnable
  2. 覆盖接口中的run 方法,将线程的任务代码封装到run方法中
  3. 通过Thread类创建线程对象,并将Runnable接口的子类对象最为Thread类的构造函数的参数进行传递 Thread t1 = new Thread(d);
  4. 调用线程对象的start方法开启线程 t1.start();

线程中常用方法

  • Thread.yield( )

    • Java线程中的Thread.yield( )方法,译为线程让步。顾名思义,就是说当一个线程使用了这个方法之后,它就会把自己CPU执行的时间让掉,让自己或者其它的线程运行,注意是让自己或者其他线程运行,并不是单纯的让给其他线程。
    • yield()的作用是让步。它能让当前线程由“运行状态”进入到“就绪状态”,从而让其它具有相同优先级的等待线程获取执行权;但是,并不能保证在当前线程调用yield()之后,其它具有相同优先级的线程就一定能获得执行权;也有可能是当前线程又进入到“运行状态”继续运行!
  • synchronized

    • synchronized加在方法前面就是方法锁,作用是保证同一时间只能有一个线程进入该方法执行
    • synchronized作用于对象 就是对象锁
      • 对象锁是实例方法使用synchronized关键字后,如果是多个线程访问同个对象的sychronized块,是同步的,但是访问不同对象的话还是不同步的。
1
2
3
4
5
6
7
8
9
User user = new User();
synchronized(user){
while(...){
...
}
user.notify() 唤醒其中一个等待操作对象(wait)的线程
user.wait(); //当前线程睡眠 释放对象锁
//当前线程睡眠,直到被别人用notify或notifyAll唤醒该线程
}

Object.wait()当前线程睡眠 释放对象锁
Object.notify() 随机唤醒其中一个wait的线程
Object.notifyAll() 唤醒所有wait中的线程
Thread.sleep(20000); 睡眠

wait和sleep的区别:
wait是object类的方法,sleep是Thread类的方法
2个方法都可以让线程暂停运行
但是wait会释放对象锁,sleep不会。

  • 中断:
    中断在java中主要有3个方法:interrupt(),sInterrupted()和interrupted()。

    • interrupt(),在一个线程中调用另一个线程的interrupt()方法,即会向那个线程发出信号——线程中断状态已被设置。至于那个线程何去何从,由具体的代码实现决定。
    • isInterrupted(),用来判断当前线程的中断状态(true or false)。
    • interrupted()是个Thread的static方法,用来恢复中断状态。
  • Thread. join();

    • join的意思是使得放弃当前线程的执行,并返回对应的线程
    • 例如下面代码的意思就是:
      程序在main线程中调用t1线程的join方法,则main线程放弃cpu控制权,并返回t1线程继续执行直到线程t1执行完毕
      所以结果是t1线程执行完后,才到主线程执行,相当于在main线程中同步t1线程,t1执行完了,main线程才有执行的机会
1
2
3
4
5
ThreadJoinTest t1 = new ThreadJoinTest("小明");
ThreadJoinTest t2 = new ThreadJoinTest("小东");
t1.start();
t1.join();
t2.start();

就这样吧

原文地址:https://www.cnblogs.com/lijianming180/p/12366307.html