Java多线程入门

进程与线程

**进程:**每个进程都有独立的代码和数据空间(进程上下文),进程间的切换会有较大的开销,一个进程包含1–n个线程。(进程是资源分配的最小单位)
**线程:**同一类线程共享代码和数据空间,每个线程有独立的运行栈和程序计数器(PC),线程切换开销小。(线程是cpu调度的最小单位)
**线程和进程一样分为五个阶段:**创建、就绪、运行、阻塞、终止
**Java线程有六个状态:**NEW(新建)、RUNNABLE(可运行)、BLOCKED(阻塞)、TIMED_WAITING(指定时间等待)、TERMINATED(终止)
Java线程是不区分 Ready(就绪)和 Running(运行)的,它们都是 Runnable 状态。
多进程是指操作系统能同时运行多个任务(程序),同一程序中有多个顺序流在执行。
在java中要想实现多线程,有两种手段,一种是继承Thread类,另外一种是实现Runable接口。(其实准确来讲,应该有三种,还有一种是实现Callable接口,并与Future、线程池结合使用)

一、创建与调用线程

1.创建类继承 Thread 类,重写 run 方法

public class MyThread extends Thread {
    /**
     * run 方法就是线性要执行的任务方法
     */
    @Override
    public void run() {
        // 这个的代码,就是一条新的执行路径
        // 这个执行路径的触发方式,不是调用 run 方法,而是通过 thread 对象的 start() 来启动任务
        for (int i = 0; i < 10; i++) {
            System.out.println("分支任务执行"+i);
        }
    }
}

public class Test {
    /**
     * 多线程技术
     */
    public static void main(String[] args) {
        MyThread m = new MyThread();
        m.start();
        for (int i = 0; i < 10; i++) {
            System.out.println("主任务执行"+i);
        }
    }
}

运行结果每次都会不一样,这是两个线程相互抢占,使用抢占式分配的结果,故后续相同功能的代码运行结果省略:

主任务执行0
主任务执行1
分支任务执行0
主任务执行2
分支任务执行1
主任务执行3
分支任务执行2
分支任务执行3
主任务执行4
主任务执行5
主任务执行6
分支任务执行4
主任务执行7
分支任务执行5
主任务执行8
分支任务执行6
主任务执行9
分支任务执行7
分支任务执行8
分支任务执行9

2.使用匿名内部类创建线程

public static void main(String[] args) {
    new Thread(){
        @Override
        public void run() {
            for (int i = 0; i < 10; i++) {
                System.out.println("分支任务执行"+i);
            }
        }
    }.start();
    for (int i = 0; i < 10; i++) {
        System.out.println("主任务执行"+i);
    }
}

3.创建类继承 Runnable 接口,重写 run 方法

public class MyRunnable implements Runnable {
    @Override
    public void run() {
        // 线程的任务
        for (int i = 0; i < 10; i++) {
            System.out.println("分支任务执行"+i);
        }
    }
}

public class Test {
    /**
     * 多线程技术
     */
    public static void main(String[] args) {
        // 实现 Runnable
        // 1. 创建一个任务对象
        MyRunnable r = new MyRunnable();
        // 2. 创建一个线程,并为其分配一个任务
        Thread t = new Thread(r);
        // 3. 执行这个线程
        t.start();
        for (int i = 0; i < 10; i++) {
            System.out.println("主任务执行"+i);
        }
    }
}

4.使用匿名内部类实现 Runnable

public static void main(String[] args) {
    Thread t = new Thread(new Runnable() {
        @Override
        public void run() {
            for (int i = 0; i < 10; i++) {
                System.out.println("分支任务执行"+i);
            }
        }
    });
    t.start();
    for (int i = 0; i < 10; i++) {
        System.out.println("主任务执行"+i);
    }
}

5.使用 Lambda 表达式,这是一个面向函数的编写方式,其中:

注:括号代表重写的方法,方法名可省略,括号内部可传参数类型,大括号中是重写的方法内容,大括号外可传参数的值

public static void main(String[] args) {
    Thread t = new Thread(() -> {
        for (int i = 0; i < 10; i++) {
            System.out.println("分支任务执行"+i);
        }
    });
    t.start();
    for (int i = 0; i < 10; i++) {
        System.out.println("主任务执行"+i);
    }
}

另外:实现 Runnable 与继承 Thread 相比有如下优势

  • 通过创建任务,然后给线程分配的方式来实现多线程,更适合多个线程同时执行相同任务的情况
  • 可以避免单继承所带来的局限性,实现 Runnable 可以继承其他的类增强实用性
  • 任务与线程本身是分离的分离的,提高了程序的健壮性
  • 线程池技术只接受 Runnable 类型的任务,而不接受 Thread 类型的线程

二、操作线程的常用方法

1.设置和获取线程名称

public static void main(String[] args) {
    System.out.println(Thread.currentThread().getName());
    new Thread(new MainRunnable(),"分支任务").start();
}

static class MainRunnable implements Runnable{
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName());
    }
}

运行结果如下,若不传进程名称只传目标类,则会生成默认名称:Thread-0,后续创建的进程名称依次加一: Thread-1、Thread-2……

main
分支任务

2.线程休眠 sleep,在任何线程中均能使用,能使当前正在执行的线程休眠(暂时停止执行)指定的时间,常用传入参数为毫秒

public static void main(String[] args) throws InterruptedException {
    for (int i = 0; i < 10; i++) {
        System.out.println(i);
        Thread.sleep(1000);
    }
}

3.线程的中断 interrupt,需要注意的是,这个方法只是给进程添加了一个标记,这个标记能被 InterruptedException 捕捉进入 catch 部分的代码,可在此处进行结束进程、释放资源等操作

public static void main(String[] args) throws InterruptedException {
    Thread t = new Thread(() -> {
        for (int i = 0; i < 10; i++) {
            System.out.println(Thread.currentThread().getName()+":"+i);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                // 1. e.printStackTrace();
                // 2. System.out.println("发现了中断标记");
                // 3.
                System.out.println("发现了中断标记,令进程死亡");
                return;
            }
        }
    });
    t.start();
    for (int i = 0; i < 5; i++) {
        System.out.println(Thread.currentThread().getName()+":"+i);
        Thread.sleep(1000);
    }
    t.interrupt();
}

你可能感兴趣的:(Java,教程,java,开发语言)