03-线程(一)

1.基本概念

1.1进程

操作系统中的独立运行的程序,每一个进程执行都有一个执行顺序。一个进程中可以有多个线程。

1.2 线程

从进程创建的,只是进程的一部分代码可以脱离出来,与主线程同时进行。

1.3 多线程

多线程是实现多任务的一种方式。

1.4并发

指两个或多个事件在同一时间段内发生。

1.5并行

指两个或多个事件在同一时刻发生(同时发生)。

并发的关键是你有处理多个任务的能力,不一定要同时。 
并行的关键是你有同时处理多个任务的能力。  

2.使用多线程的意义

提高执行效率

  1. 提高cpu的利用率,在单线程下,运行一个任务的时候,第二个任务便不能执行,而在多线程的下,主线程执行的情况下可以同时执行其他任务;

  2. 系统创建进程需要为该进程重新分配系统资源,创建线程代价比较小;

  3. 线程可以共享数据,而进程之间不能共享数据;

  4. Java语言内置了多线程功能支持,简化了java多线程编程。

3.线程的状态

  • 新建 :线程对象创建,但是没有在其上调用start()方法;
  • 就绪(可运行) :线程对象调用start()方法后,就处于就绪状态,但调度程序还没有把它选定为运行线程时线程所处的状态;
  • 运行 :就绪状态下的线程在获取CPU资源后就可以执行run(),此时的线程便处于运行状态,运行状态的线程可变为就绪、阻塞及死亡三种状态。
  • 等待/阻塞/睡眠 :在一个线程执行了sleep(睡眠)、suspend(挂起)等方法后会失去所占有的资源,从而进入阻塞状态,其共同点是:线程仍旧是活的,但是当前没有条件运行。换句话说,它是可运行的,但是如果某件事件出现,他可能返回到可运行状态。
  • 终止 :run()方法完成后或发生其他终止条件时就会切换到终止状态。

4.创建线程的方法

4.1 继承Thread类

步骤

  1. 定义类继承Thread;
  2. 重写Thread类中的run方法;
    目的:将自定义代码存储在run方法,让线程运行
  3. 调用线程的start方法
    启动线程,调用run方法。
public class MyThread extends Thread{//继承Thread类
  public void run(){
  //重写run方法
  }
}
public class Main {
  public static void main(String[] args){
    new MyThread().start();//创建并启动线程
  }
}

4.2 实现Runnable接口

步骤

  1. 定义子类,实现Runnable接口,并重写run();
  2. 启动线程,创建Runnable实现类的实例,并用这个实例作为Thread的target来创建Thread对象;
  3. 通过调用线程对象的start()方法来启动线程。
public class MyThread2 implements Runnable {//实现Runnable接口
  public void run(){
  //重写run方法
  }
}
public class Main {
  public static void main(String[] args){
    //创建并启动线程
    MyThread2 myThread=new MyThread2();
    Thread thread=new Thread(myThread);
    thread().start();
    //或者    new Thread(new MyThread2()).start();
  }
}

4.3 使用Callable和Future创建线程

步骤:

  1. 创建Callable接口的实现类,并实现call()方法,然后创建该实现类的实例(从java8开始可以直接使用Lambda表达式创建Callable对象)。

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

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

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

public class Main {
  public static void main(String[] args){
   MyThread3 th=new MyThread3();
   //使用Lambda表达式创建Callable对象
     //使用FutureTask类来包装Callable对象
   FutureTask<Integer> future=new FutureTask<Integer>(
    (Callable<Integer>)()->{
      return 5;
    }
    );
   new Thread(task,"有返回值的线程").start();//实质上还是以Callable对象来创建并启动线程
    try{
    System.out.println("子线程的返回值:"+future.get());//get()方法会阻塞,直到子线程执行结束才返回
    }catch(Exception e){
    ex.printStackTrace();
   }
  }
}

4.4 三种创建方式的对比

  1. 采用实现Runnable、Callable接口的方式创建多线程时

优势是:

线程类只是实现了Runnable接口或Callable接口,还可以继承其他类。

在这种方式下,多个线程可以共享同一个target对象,所以非常适合多个相同线程来处理同一份资源的情况,从而可以将CPU、代码和数据分开,形成清晰的模型,较好地体现了面向对象的思想。

劣势是:

编程稍微复杂,如果要访问当前线程,则必须使用Thread.currentThread()方法。

  1. 使用继承Thread类的方式创建多线程时,

优势是:

编写简单,如果需要访问当前线程,则无需使用Thread.currentThread()方法,直接使用this即可获得当前线程。

劣势是:

线程类已经继承了Thread类,所以不能再继承其他父类。

  1. Runnable和Callable的区别

(1) Callable规定(重写)的方法是call(),Runnable规定(重写)的方法是run()。

(2) Callable的任务执行后可返回值,而Runnable的任务是不能返回值的。

(3) call方法可以抛出异常,run方法不可以。

(4) 运行Callable任务可以拿到一个Future对象,表示异步计算的结果。它提供了检查计算是否完成的方法,以等待计算的完成,并检索计算的结果。通过Future对象可以了解任务执行情况,可取消任务的执行,还可获取执行结果。

注:一般推荐采用实现接口的方式来创建多线程

参考:

https://www.cnblogs.com/yjboke/p/8911220.html

发布了24 篇原创文章 · 获赞 1 · 访问量 1786

猜你喜欢

转载自blog.csdn.net/weixin_42106658/article/details/105046408
03-