Java 多线程入门

2020-09-18  本文已影响0人  Lifg

目录

  • 基本概念:程序、进程、线程
  • 线程的创建与使用
  • 线程的生命周期
  • 线程的同步
  • 线程的通信

一 程序、进程、线程

二 多线程的创建和启动

  1. Java语言JVM允许程序运行多个线程,通过java.lang.Thread来体现
    • Thread类特性
      • 每个线程都是通过某个特定Thread对象的run()方法来完成操作的,经常把run()方法的主体称为线程体
      • 通过该 Thread 对象的start()方法来启动这个线程,而非直接调用run()
  2. API 创建线程的方式

JDK 1.5之前

  1. 继承 Thread 类方式
  2. 实现 Runnable 接口方式
    JDK 1.5之后新增
  3. 实现Callable 接口方式
  4. 使用线程池
public class MyThread extends Thread {
    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println(Thread.currentThread().getName() + "_" + i);
        }
    }
}

class Test {
    public static void main(String[] args) {

        MyThread myThread = new MyThread();
        myThread.start();
        for (int i = 0; i < 5; i++) {
            System.out.println(Thread.currentThread().getName() + "_" + i);
        }
    }
}

注意点

  1. 如果自己手动调用run()方法,那么就只是普通方法,没有启动多线程模式。
  2. run() 方法有JVM 调用,什么时候调用,执行的过程控制都有操作系统的CPU调度决定。
  3. 想要启动多线程,必须调用start方法。
  4. 一个线程对象只能调用一次start()方法启动,如果重复调用了,则将抛出以上的异常“IllegalThreadStateException”。
  1. 定义子类,实现Runnable接口。
  2. 子类中重写Runnable接口中的run方法。
  3. 通过Thread类含参构造器创建线程对象。
  4. 将Runnable接口的子类对象作为实际参数传递给Thread类的构造器中。
  5. 调用Thread类的start方法:开启线程,调用Runnable子类接口的run方法。
public class MyThread2 implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println(Thread.currentThread().getName() + "_" + i);
        }
    }
}

class Test1 {
    public static void main(String[] args) {
        Thread thread = new Thread(new MyThread2());
        thread.start();
        for (int i = 0; i < 5; i++) {
            System.out.println(Thread.currentThread().getName() + "_" + i);
        }
    }
}
public class MyThread3 implements Callable<Integer> {
    @Override
    public Integer call() throws Exception {
        int total = 0;
        for (int i = 0; i < 5; i++) {
            total += i;
            System.out.println(Thread.currentThread().getName() + ":" + i);
        }
        return total;
    }
}
class Test2 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        FutureTask<Integer> task = new FutureTask<>(new MyThread3());
        new Thread(task).start();
        Integer integer = task.get();
        System.out.println(Thread.currentThread().getName() + "_" + integer);
    }
}
public class MyThread4 implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println(Thread.currentThread().getName() + ":" + i);
        }
    }
}
class Test5 {
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        //将Runnable 实现类作为形参传入开启线程
        executorService.execute(new MyThread4());
        //结束线程的使用
        executorService.shutdown();
    }
}
线程的调度

三 线程的生命周期

JDK中用Thread.State类定义了线程的几种状态
要想实现多线程,必须在主线程中创建新的线程对象。Java语言使用Thread类 及其子类的对象来表示线程,在它的一个完整的生命周期中通常要经历如下的五 种状态:

  1. 新建: 当一个Thread类或其子类的对象被声明并创建时,新生的线程对象处于新建 状态
  2. 就绪:处于新建状态的线程被start()后,将进入线程队列等待CPU时间片,此时它已 具备了运行的条件,只是没分配到CPU资源
  3. 运行:当就绪的线程被调度并获得CPU资源时,便进入运行状态, run()方法定义了线 程的操作和功能
  4. 阻塞:在某种特殊情况下,被人为挂起或执行输入输出操作时,让出 CPU 并临时中 止自己的执行,进入阻塞状态
  5. 死亡:线程完成了它的全部工作或线程被提前强制性地中止或出现异常导致结束

四 线程的同步

Java对于多线程的安全问题提供了专业的解决方式:同步机制
*Synchronized的使用方法

  1. 同步代码块: synchronized (对象){ // 需要被同步的代码; }
  2. synchronized还可以放在方法声明中,表示整个方法为同步方法。public synchronized void show (String name){ .... }
  • 注意: 必须确保使用同一个资源的多个线程共用一把锁,这个非常重要,否则就无法保证共享资源的安全
  • 一个线程类中的所有静态方法共用同一把锁(类名.class),所有非静态方法共用同一把锁(this),同步代码块(指定需谨慎)
  1. 如何找问题,即代码是否存在线程安全?
    • 明确哪些代码是多线程运行的代码
    • 明确多个线程是否有共享数据
    • 明确多线程运行代码中是否有多条语句操作共享数据
  2. 解决方案
    • 对多条操作共享数据的语句,只能让一个线程都执行完,在执行过程中,其 他线程不可以参与执行。 即所有操作共享数据的这些语句都要放在同步范围中
  • 切记
    范围太小:没锁住所有有安全问题的代码
    范围太大:没发挥多线程的功能。
上一篇 下一篇

猜你喜欢

热点阅读