1.线程简介

线程:线程是进程中的一个执行单元,负责当前进程中程序的执行,一个进程中至少有一个线程。
一个进程中是可以有多个线程的,这个应用程序也可以称之为多线程程序。
简而言之:一个程序运行后至少有一个进程,一个进程中可以包含多个线程

一个进程可以产生多个线程。同多个进程可以共享操作系统的某些资源一样,同一进程的多个线程也可以共享此进程的某些资源(比如:代码、数据),所以线程又被称为轻量级进程(lightweight process)。
1. 一个进程内部的一个执行单元,它是程序中的一个单一的顺序控制流程。
2. 一个进程可拥有多个并行的(concurrent)线程。
3. 一个进程中的多个线程共享相同的内存单元/内存地址空间,可以访问相同的变量和对象,而且它们从同一堆中分配对象并进行通信、数据交换和同步操作。
4. 由于线程间的通信是在同一地址空间上进行的,所以不需要额外的通信机制,这就使得通信更简便而且信息传递的速度也更快。
5. 线程的启动、中断、消亡,消耗的资源非常少。
1. 创建线程 - 图1

  • 程序运行原理
    • 分时调度
      所有线程轮流使用 CPU 的使用权,平均分配每个线程占用 CPU 的时间。
    • 抢占式调度
      优先让优先级高的线程使用 CPU,如果线程的优先级相同,那么会随机选择一个(线程随机性),Java使用的为抢占式调度。

其实,多线程程序并不能提高程序的运行速度,但能够提高程序运行效率,让CPU的使用率更高


2.创建线程方式1

继承Thread类

继承Thread类实现多线程的步骤:
1.自定义线程类继承Thread类
2.重写run()方法, 编写线程执行体
3.创建线程对象, 调用start()方法启动线程

示例:

  1. public class TestThread extends Thread {//自定义类继承Thread类
  2. //run()方法里是线程体
  3. public void run() {
  4. for (int i = 0; i < 10; i++) {
  5. System.out.println(this.getName() + ":" + i);//getName()方法是返回线程名称
  6. }
  7. }
  8. public static void main(String[] args) {
  9. TestThread thread1 = new TestThread();//创建线程对象
  10. thread1.start();//启动线程
  11. TestThread thread2 = new TestThread();
  12. thread2.start();
  13. }
  14. }


不推荐使用:**避免OOP单继承局限性


3.创建线程方式2

实现Runnable接口

实现Runnable接口i多线程的步骤:
1.自定义线程类实现Runnable接口
2.重写run()方法, 编写线程执行体
3.通过静态代理实现了Runnabled的子类, 作为参数传递给Thread类的构造函数
4.调用Thread类的start()方法开启线程

示例:

  1. public class TestThread2 implements Runnable {//自定义类实现Runnable接口;
  2. //run()方法里是线程体;
  3. public void run() {
  4. for (int i = 0; i < 10; i++) {
  5. System.out.println(Thread.currentThread().getName() + ":" + i);
  6. }
  7. }
  8. public static void main(String[] args) {
  9. //创建线程对象,把实现了Runnable接口的对象作为参数传入;
  10. Thread thread1 = new Thread(new TestThread2());
  11. thread1.start();//启动线程;
  12. Thread thread2 = new Thread(new TestThread2());
  13. thread2.start();
  14. }
  15. }

推荐使用: **避免单继承局限性,灵活方便, 方面同一个对象被多个线程使用**


4.创建线程方式3

实现Callable接口

使用Runnable接口实现的多线程可以避免单继承局限,但是他不能返回结果.

为了解决这个,提供了一个新的接口java.util.concurrent.Callable

  1. @FunctionalInterface
  2. public interface Callable<V> {
  3. V call() throws Exception;
  4. }

call()方法执行完线程的主题功能之后可以返回一个结果,而返回结果的类型由Callable的泛型决定

步骤

实现Callable接口

Thread类里面没有发现并没有直接支持Callable接口的多线程应用
从jdk1.5开始提供有java.util.concurrent.FutureTask<V>类,这个类主要负责Callable

  1. public class FutureTask<V> extends Object implement RunnableFuture<V>

发现他实现了RunnableFuture

  1. public interface RunnableFuture<V> extends Runnable, Future<V>

然后我们看,在FutureTask类里面定义有如下结构的构造方法:

public FutureTask(Callable<V> callable)

接收的目的只有一个,那么取得call()方法的返回结果

FutureTask是Runnable接口的子类,所以可以使用Thread类的构造来接收task对象
多线程执行完毕可以取得内容,依靠FutureTask的父接口Future中的get()方法完成

示例:

package com.study;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

class MyThread implements Callable<String> {

    private int ticket = 10;

    @Override
    public String call() throws Exception {
        for (int i = 0; i < 100; i++) {
            if (this.ticket > 0) {
                System.out.println("卖票--- " + this.ticket--);
            }
        }
        return "卖光了";
    }
}

public class HelloWorld {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        MyThread m1 = new MyThread();
        MyThread m2 = new MyThread();

        //目的为了取得线程的返回值,String是泛型,对应返回类型
        FutureTask<String> task1 = new FutureTask<>(m1);
        FutureTask<String> task2 =  new FutureTask<>(m2);

        //FutureTask是Runnable接口的子类,所以可以使用Thread类的构造来接收task对象
        //启动多线程
        new Thread(task1).start();
        new Thread(task2).start();

        //多线程执行完毕可以取得内容,依靠FutureTask的父接口Future中的get()方法完成
        System.out.println("A线程返回结果: "+ task1.get());
        System.out.println("B线程返回结果: "+ task2.get());
    }
}

5.简化线程写法

1.使用匿名内部类
2.在匿名内部类的基础上推导lambda表达式简化代码

使用线程的内匿名内部类方式,可以方便的实现每个线程执行不同的线程任务操作。

  • 方式1:创建线程对象时,直接重写Thread类中的run方法 ```java //1.匿名内部类的使用 new Thread() { public void run() {
      for (int x = 0; x < 40; x++) {
          System.out.println(Thread.currentThread().getName()
                             + "...X...." + x);
      }
    
    } }.start();

//2.jdk8 lambda new Thread(() ->{ for (int x = 0; x < 40; x++) { System.out.println(Thread.currentThread().getName()

                       + "...X...." + x);    }

}).start();


- 方式2:使用匿名内部类的方式实现Runnable接口,重新Runnable接口中的run方法

```java
//1.匿名内部类的使用
Runnable r = new Runnable() {
    public void run() {
        for (int x = 0; x < 40; x++) {
            System.out.println(Thread.currentThread().getName()
                    + "...Y...." + x);
        }
    }
};
new Thread(r).start();

//2.jdk8  lambda
new Thread(() ->{
    for (int x = 0; x < 40; x++) {
        System.out.println(Thread.currentThread().getName()
                           + "...Y...." + x);
    }
}).start();