线程的基础概念

1.线程和进程的概念

1) 进程的概念

进程指在系统中能独立运行并作为资源分配的基本单位,它是由一组机器指令、数据和堆栈等组成的,是一个能独立运行的活动实体。
进程是系统进行资源分配和调度的一个独立单位。进程简单来理解就是每个应用都是一个进程。

2) 线程的概念

线程:是用来执行具体功能和任务的,需要进程为载体,是进程的一个实体**,是CPU调度和分派的基本单位**,它是比进程更小的能独立运行的基本单位.线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源。
与进程不同的是同类的多个线程共享进程的堆和方法区资源,但每个线程有自己的程序计数器、虚拟机栈和本地方法栈,所以系统在产生一个线程,或是在各个线程之间作切换工作时,负担要比进程小得多,也正因为如此,线程也被称为轻量级进程。

3)进程和线程的区别:

线程具有许多传统进程所具有的特征,故又称为轻型进程或进程元;而把传统的进程称为重型进程,它相当于只有一个线程的任务。在引入了线程的操作系统中,通常一个进程都有若干个线程,至少包含一个线程。
根本区别:进程是操作系统资源分配的基本单位,而线程是处理器任务调度和执行的基本单位
资源开销:每个进程都有独立的代码和数据空间(程序上下文),程序之间的切换会有较大的开销;线程可以看做轻量级的进程,同一类线程共享代码和数据空间,每个线程都有自己独立的运行栈和程序计数器(PC),线程之间切换的开销小。
操作系统资源管理方式:进程有独立的地址空间,进程崩溃后会有保护模式让其不会对其他的进程产生影响。
线程有自己的堆栈和局部变量,但线程之间没有单独的地址空间,所以一个线程挂掉可能影响整个进程挂掉。
进程的并发性没有线程高。
执行过程:每个独立的进程有程序运行的入口、顺序执行序列和程序出口。但是线程不能独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制,两者均可并发执行
内存分配:对于应用程序来说,多线程是可以同时有多个执行部分同时执行。但对于操作系统来说是没有将多个线程看做多个独立的应用,来实现进程的调度和管理以及资源分配
同一进程的线程共享本进程的地址空间和资源,而进程之间的地址空间和资源是相互独立的
**包含关系:**如果一个进程内有多个线程,则执行过程不是一条线的,而是多条线(线程)共同完成的;线程是进程的一部分,所以线程也被称为轻权进程或者轻量级进程。

2.线程的创建

1. 继承Thread类

1)定义Thread类的子类,并重写该类的run()方法,该方法的方法体就是线程需要完成的任务,run()方法也称为线程执行体。

2)创建Thread子类的实例,也就是创建了线程对象

3)启动线程,即调用线程的start()方法

class myThread extends Thread {
     @Override
     public void run() {
       
        }
    }
public class Test {
    public static void main(String[] args) {
       Thread thread = new MyThread();
        thread.start();

2.使用Callable和Future

Callable接口
1)Callable接口存在Executor框架中类,相比于Runnable更加强大
a.Callable可以在任务执行结束之后提供一个返回值
b.call方法可以抛出异常
c.运行Callable任务可以拿到一个Future对象,Future提供get方法
拿到返回值(异步)
Future接口
1、可以对具体Runnable、Callable任务对执行结果进行取消、查询是否完成、获取结果等。
2、FutureTask是Future接口等唯一实现类。
3、FutureTask同时实现了Runnable,Future接口。它既可以作为Runnable被线程执行,又可以作为Future得到Callable等返回值。
使用:
1)创建Callable接口的实现类,并实现call()方法,然后创建该实现类的实例(从java8开始可以直接使用Lambda表达式创建Callable对象)。

2)使用FutureTask类来包装Callable对象,该FutureTask对象封装了Callable对象的call()方法的返回值

3)使用FutureTask对象作为Thread对象的target创建并启动线程(因为FutureTask实现了Runnable接口)

4)调用FutureTask对象的get()方法来获得子线程执行结束后的返回值

class MyCallable implements Callable <Integer > {
    @Override
    public Integer call() throws Exception {
    
     }
}
class test{
public static void main(String[] args) {
   Callable<Integer> callableTask = new MyCallable();
   FutureTask<Integer> task = new FutureTask<>(callableTask);
    Thread thread = new Thread(task);
      thread.start();

3. 实现Runnable接口

class MyRunnable implements  Runnable {
     @Override
     public void run() {
     
    }
}
public class Test {
    public static void main(String[] args) {
     Thread thread = new Thread(new MyRunnable());
        thread.start();
     

4. 匿名

 new Thread(){
            @Override
            public void run() {
               System.out.println("thread-0");
            }
       }.start();

5. Callable和Runnable接口

1.区别
1.两者最大的不同点是:实现Callable接口的任务线程能返回执行结果;而实现Runnable接口的任务线程不能返回结果;
2、Callable接口的call()方法允许抛出异常;而Runnable接口的run()方法的异常只能在内部消化,不能继续上抛;
3.callable和runnable都可以应用于executors。而thread类只支持runnable
4.Runnnable接口的使用场景
1)传递给线程对象执行任务;
2)作为线程池方法execute()的入口参数来执行任务;
callable对象实例可以作为线程池的submit()方法入口参数;

2.相同点:
两者都是接口
两者都需要调用Thread.start启动线程
3.如何理解实现Callable接口的方式创建多线程比实现Runnable接口创建多线程更强大?
1.call()方法可以有返回值。
2.call()方法可以抛出异常,被外面的操作捕获,获取信息。
3.Callable是支持泛型的。

3.线程的生命周期及常用方法

(1)线程的生命周期

一个线程不是被创建了马上就开始执行,也不是一直处于执行状态。在线程的整个生命周期中会经历新建(NEW),就绪(Runnable),阻塞(Blocked)和等待状态(Waiting)、终止状态(Terminated)、睡眠状态(Timed_Waiting)6种状态。
新建
new关键字创建一个线程对象,它并不是处于一个执行状态,因为还没有start启动线程
就绪
当线程对象调用start()方法之后,该线程处于就绪状态。JVM会为其创建方法调用栈和程序计数器,等待调度运行。
运行
如果处于就绪状态的线程获得了CPU,开始执行run()方法的线程执行体,则该线程处于运行状态。
阻塞
等待一个监视器锁进入同步代码块或者同步方法,代码块/方法某一时刻只能够有一个线程执行,其他线程只能等待
阻塞状态是指线程因为某种原因放弃了CPU使用权,暂时停止运行。
阻塞分三种情况:
(1)等待阻塞(o.wait->等待队列):运行(running)的线程执行o.wait()方法,JVM会把该线程放入等待队列(waiting queue)中;
(2)同步阻塞(lock->锁池):运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池(lock pool)中;
(3)其他阻塞(sleep/join):运行的线程执行Thread.sleep(long ms)或thread.join方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时,join()等待线程终止或超时,或者I/O处理完毕时,线程重新转入可运行状态中。
等待
Object.wait()/Thread.join()/LockSupport.park()都会使得线程阻塞从Runnable转换到Waiting状态
睡眠
调用加超时参数的Object.wait(long mills)/Thread.slepp(long mills)/LockSupport. parkNano()/LockSupport.parkUntil()
最终
一个线程的最终状态,线程进入到Terminated状态,意味着该线程的生命周期 结束了,下面这些情况都会使得线程进入Terminated状态
1)线程执行正常结束
2)线程运行出错意外结束
3)JVM crash

(2)常用方法

1.start
启动一个线程,将线程添加一个线程组中,线程状态会从New状态转换到Runnable状态,然后获取 Cpu之后进入Running状态执行run
2.Sleep
作用:强迫一个线程睡眠N毫秒,线程进入TIMED_WAITING状态,只有等待另外线程的通知或者被中断才会返回;

注意:与wait方法不同的是sleep不会释放当前占有的锁
sleep是一个静态方法,其中存在两个重载函数

public static native void sleep(long millis)
public static void sleep(long millis, int nanos)

使得线程进入睡眠状态,暂停执行,sleep不会放弃monitor lock的所有权
jdk1.5之后,jdk引入一个枚举TimeUnit,其对sleep方法做了封装,直接使用时间单位

                Thread .sleep(10);
                TimeUnit.MILLISECONDS .sleep(3) ;
                TimeUnit.DAYS.sleep(2) ;

3.yield
作用:使当前线程让出CPU执行时间片,与其他线程一起重新竞争CPU时间片;
注意:一般情况下,优先级高的线程有更大的可能性成功竞争得到CPU时间片,但这不是绝对的,有的操作系统对线程优先并不敏感。

1.yield和sleep的区别

  • a.jdk1.5之前,yield调用了sleep
  • b.sleep使得当前线程暂停执行,不会占用cpu资源
  • yield只是对于cpu调度器的一个提示
  • c.sleep会导致线程短暂的阻塞,yield会使得线程Runnable-》Runnable
  • d.sleep会捕获到中断异常,yield不会捕获到中断异常

4.join
作用:使当前线程转为阻塞状态,直到另一个线程结束,当前线程再由阻塞状态变为就绪状态;
使用场景:主线程中启动子线程,主线程等待子线程的返回状态。
5.wait
作用:强迫一个线程等待,线程进入WAITING状态,只用等待另外线程的通知或被中断才会返回;
wait 调用某个对象的wait()方法可以让当前线程阻塞
注意:调用wait()方法后,会释放对象的锁。因此,wait方法一般用在同步方法或同步代码块中。

6.notify
作用:Object类中的notify方法,唤醒在此对象监视器上等待的单个线程;
7.notifyAll
作用:Object类中的notify方法,唤醒在此对象监视器上等待的所有线程;

8.线程中断方法

作用:中断一个线程,会影响这个线程内部的一个中断标识位;
注意:
(1)interrupt不会改变线程的状态,既不会强制线程进入阻塞,终止等状态;
(2)若线程处于TIMED-WAITING状态,这时调用interrupt方法,会抛出InterruptException,从而使线程提前结束TIMED-WAITING状态;
(3)许多申明抛出InterruptException的方法,抛出异常前,都会清除中断标识位,所以抛出异常后,调用isInterrupt方法会返回false;

1.interrupt()

  • 如下方法能够使得当前线程进入阻塞状态,调用interrupt方法可以打断阻塞,因此这种 方法被称之为可中断方法

  • Object.wait()/wait(long)

  • Thread.sleep(long)/TimUnit.XXX.sleep(long)

  • Thread.join()/Thread.join(long)

  • interrupt

  • 如果一个线程被interrupt,设置interrupt flag;如果当前线程正在执行可中断方法,调用interrupt方法,反而导致interrupt flag被清除

 thread.interrupt(); //将中断位置置为true

2.isInterrupted判断当前线程的中断状态位是否为true

3.interrupted

  • public static Boolean interrupted()
  • 调用interrupted会擦除中断状态位的标识
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值