多线程基础(一)
2020-12-19 / highPhone啊

线程简介

进程:

   应用程序的执行实例,有独立内存空间和系统资源

线程:

   cpu调度和分派的基本单位,进程中执行运算最小的单位,可完成一个独立顺序控制流程

多线程:
   在一个进程中同时运行了多个线程,完成不同的工作
    多个线程交替占用cpu资源,而非真正并行执行
    好处:
    充分利用cpu资源
    简化编程模型
    带来良好用户体验

创建线程的三种方式

继承Thread类

1. 编写自己的线程类,使用extends关键字继承Thread类

2. 重写继承自Thread类的run方法,自定义自己的线程工作内容

3. 创建自定义线程对象,调用start()方法启动线程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class ThreadTest1 extends Thread{
public static void main(String[] args) {
ThreadTest1 thread1 = new ThreadTest1("线程一");
ThreadTest1 thread2 = new ThreadTest1("线程二");
thread1.start();
thread2.start();
}

public ThreadTest1(String name) {
super(name);
}

@Override
public void run() {//线程每秒打印i,从1到10
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + "正在打印:" + i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}

实现Runnable接口

1. 编写自己的线程类,使用implements关键字实现Runnable接口

2. 重写Runnable接口的run方法,自定义线程工作内容

3. 创建自定义线程对象,通过new Thread().start()传入自定线程对象的方式启动自定义线程(静态代理)。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class ThreadTest2 implements Runnable{
public static void main(String[] args) {
ThreadTest2 thread1 = new ThreadTest2();
ThreadTest2 thread2 = new ThreadTest2();
new Thread(thread1, "线程一").start();
new Thread(thread2, "线程二").start();
}

@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + "正在打印" + i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
  • Runnable接口其实是一个函数式接口,通过实现Runnable接口方式创建线程,还可以使用Lambda表达式创建自定义线程对象,这样可以让代码更加简洁:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    public class ThreadTest3 {
    public static void main(String[] args) {
    new Thread(()->{
    for (int i = 0; i < 10; i++) {
    System.out.println(Thread.currentThread().getName() + "正在打印" + i);
    try {
    Thread.sleep(1000);
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    }
    }, "线程一").start();
    }
    }

    实现Callable接口

    1. 编写自定义线程类,使用implements实现Callable接口

    2. 重写Callable接口的call方法,自定义线程工作内容,根据实际情况确定线程方法体的返回值,返回值就是Callable中的泛型。

    3. 创建ExecutorService,通过sumbit()启动线程,submit()返回方法体返回值类型一致的Future,可以获得线程执行完的返回值。

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      public class ThreadTest4 implements Callable<Boolean> {

      public static void main(String[] args) {
      ExecutorService service = Executors.newSingleThreadExecutor();
      Future<Boolean> future = service.submit(new ThreadTest4());
      try {
      Boolean callResult = future.get();
      System.out.println("线程执行结果返回是" + callResult);
      } catch (InterruptedException e) {
      e.printStackTrace();
      } catch (ExecutionException e) {
      e.printStackTrace();
      }
      finally {
      service.shutdown();
      }
      }

      @Override
      public Boolean call() throws Exception {
      for (int i = 0; i < 10; i++) {
      System.out.println(Thread.currentThread().getName() + "正在打印" + i);
      Thread.sleep(1000);
      }
      return true;
      }
      }

      虽然是实现 Callable ,但是在 Executor 实际运行时,会将 Runnable 的实例或 Callable 的实例转化为 RunnableFuture 的实例,而 RunnableFuture 继承了 Runnable 和 Future 接口。另外,相比实现Runnable接口,实现Callable接口有以下优点:

    • Callable 可以在任务结束的时候提供一个返回值,Runnable 无法提供这个功能
    • Callable 的 call 方法分可以抛出异常,而 Runnable 的 run 方法不能抛出异常。

线程状态及Thread常用方法

线程状态

  • 新生状态状态(new)
    Thread t = new Thread(),线程对象一旦创建就进入到新生状态
  • 就绪状态(Runnable)
    可执行状态,当调用start()方法启动线程后,线程进入就绪状态,可以被CPU调度执行,但不意味着会立即调度
  • 运行状态(Running)
    线程被CPU调度,获得CPU资源后进入运行状态,执行run()方法里的线程体代码块。
  • 阻塞状态(Blocked)
    当调用sleep(),wait()或同步锁定等,线程需要等待唤醒或者需要等待别的线程释放锁定资源时,线程进入阻塞状态,就是代码块不往下执行,等待阻塞时间解除后,重新进入就绪状态,等待CPU调度执行。
  • 死亡状态(Dead)
    线程发生异常或者被外部中断,或者线程执行完毕正常结束后,线程进入死亡状态。线程一旦进入死亡状态,就不能够再次启动。

Thread常用方法

  • setPriority(int newPriority)
    更改线程优先级,数字越大,优先级越高,但线程优先级并不是绝对的,有可能存在低优先级的线程比高优先级线程先被CPU调度的情况
  • static void sleep(long milllis)
    在指定毫秒数内让当前线程休眠,休眠结束后重新进入就绪状态等待CPU调度
  • void join()
    别的线程等待当前线程终止后再执行(插队)
  • static void yield()
    暂停当前正在执行的线程对象,释放CPU资源,重新进入就绪状态(礼让其他线程),但yield()礼让不一定会成功,只是让当前执行的线程重新回到就绪状态,和其他线程再次竞争CPU资源,也有可能会再次被CPU调度。
  • final void stop()
    停止当前线程,但是不建议使用,此方法已被声明为废弃
  • void destroy()
    销毁线程,同stop(),此方法也被废弃

    要终止一个线程,我们可以通过自定义标志位,并自行声明stop()供外界调用以终止线程

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    public class ThreadTest2 implements Runnable{

    private boolean flag = true;

    public static void main(String[] args) {
    ThreadTest2 thread1 = new ThreadTest2();
    ThreadTest2 thread2 = new ThreadTest2();
    new Thread(thread1, "线程一").start();
    new Thread(thread2, "线程二").start();
    }

    @Override
    public void run() {
    while (true)
    {
    for (int i = 0; i < 10; i++) {
    System.out.println(Thread.currentThread().getName() + "正在打印" + i);
    try {
    Thread.sleep(1000);
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    }
    }
    }
    public void stop()
    {
    this.flag = false;
    }
    }
本文链接:https://highphone.xyz/241f90c5.html