Java:多线程入门

person 匿名

发布日期: 2021-07-12 08:56:48 浏览量: 85
评分:
star star star star star star star star star star_border
*转载请注明来自write-bug.com

进程与线程

  • 进程:每个进程都有独立的代码和数据空间(进程上下文),进程间的切换会有较大的开销,一个进程包含1—n个线程。(进程是资源分配的最小单位)

  • 线程:同一类线程共享代码和数据空间,每个线程有独立的运行栈和程序计数器(PC),线程切换开销小。(线程是cpu调度的最小单位)

  • 线程和进程一样分为五个阶段:创建、就绪、运行、阻塞、终止

Java线程有六个状态:NEW(新建)、RUNNABLE(可运行)、BLOCKED(阻塞)、TIMED_WAITING(指定时间等待)、TERMINATED(终止)

Java线程是不区分 Ready(就绪)和 Running(运行)的,它们都是 Runnable 状态。

多进程是指操作系统能同时运行多个任务(程序),同一程序中有多个顺序流在执行。

在java中要想实现多线程,有两种手段,一种是继承Thread类,另外一种是实现Runable接口。(其实准确来讲,应该有三种,还有一种是实现Callable接口,并与Future、线程池结合使用)

一、创建与调用线程

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

  1. public class MyThread extends Thread {
  2. /**
  3. * run 方法就是线性要执行的任务方法
  4. */
  5. @Override
  6. public void run() {
  7. // 这个的代码,就是一条新的执行路径
  8. // 这个执行路径的触发方式,不是调用 run 方法,而是通过 thread 对象的 start() 来启动任务
  9. for (int i = 0; i < 10; i++) {
  10. System.out.println("分支任务执行"+i);
  11. }
  12. }
  13. }
  14. public class Test {
  15. /**
  16. * 多线程技术
  17. */
  18. public static void main(String[] args) {
  19. MyThread m = new MyThread();
  20. m.start();
  21. for (int i = 0; i < 10; i++) {
  22. System.out.println("主任务执行"+i);
  23. }
  24. }
  25. }

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

  1. 主任务执行0
  2. 主任务执行1
  3. 分支任务执行0
  4. 主任务执行2
  5. 分支任务执行1
  6. 主任务执行3
  7. 分支任务执行2
  8. 分支任务执行3
  9. 主任务执行4
  10. 主任务执行5
  11. 主任务执行6
  12. 分支任务执行4
  13. 主任务执行7
  14. 分支任务执行5
  15. 主任务执行8
  16. 分支任务执行6
  17. 主任务执行9
  18. 分支任务执行7
  19. 分支任务执行8
  20. 分支任务执行9

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

  1. public static void main(String[] args) {
  2. new Thread(){
  3. @Override
  4. public void run() {
  5. for (int i = 0; i < 10; i++) {
  6. System.out.println("分支任务执行"+i);
  7. }
  8. }
  9. }.start();
  10. for (int i = 0; i < 10; i++) {
  11. System.out.println("主任务执行"+i);
  12. }
  13. }

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

  1. public class MyRunnable implements Runnable {
  2. @Override
  3. public void run() {
  4. // 线程的任务
  5. for (int i = 0; i < 10; i++) {
  6. System.out.println("分支任务执行"+i);
  7. }
  8. }
  9. }
  10. public class Test {
  11. /**
  12. * 多线程技术
  13. */
  14. public static void main(String[] args) {
  15. // 实现 Runnable
  16. // 1. 创建一个任务对象
  17. MyRunnable r = new MyRunnable();
  18. // 2. 创建一个线程,并为其分配一个任务
  19. Thread t = new Thread(r);
  20. // 3. 执行这个线程
  21. t.start();
  22. for (int i = 0; i < 10; i++) {
  23. System.out.println("主任务执行"+i);
  24. }
  25. }
  26. }

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

  1. public static void main(String[] args) {
  2. Thread t = new Thread(new Runnable() {
  3. @Override
  4. public void run() {
  5. for (int i = 0; i < 10; i++) {
  6. System.out.println("分支任务执行"+i);
  7. }
  8. }
  9. });
  10. t.start();
  11. for (int i = 0; i < 10; i++) {
  12. System.out.println("主任务执行"+i);
  13. }
  14. }

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

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

  1. public static void main(String[] args) {
  2. Thread t = new Thread(() -> {
  3. for (int i = 0; i < 10; i++) {
  4. System.out.println("分支任务执行"+i);
  5. }
  6. });
  7. t.start();
  8. for (int i = 0; i < 10; i++) {
  9. System.out.println("主任务执行"+i);
  10. }
  11. }

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

  • 通过创建任务,然后给线程分配的方式来实现多线程,更适合多个线程同时执行相同任务的情况

  • 可以避免单继承所带来的局限性,实现 Runnable 可以继承其他的类增强实用性

  • 任务与线程本身是分离的分离的,提高了程序的健壮性

  • 线程池技术只接受 Runnable 类型的任务,而不接受 Thread 类型的线程

二、操作线程的常用方法

1.设置和获取线程名称

  1. public static void main(String[] args) {
  2. System.out.println(Thread.currentThread().getName());
  3. new Thread(new MainRunnable(),"分支任务").start();
  4. }
  5. static class MainRunnable implements Runnable{
  6. @Override
  7. public void run() {
  8. System.out.println(Thread.currentThread().getName());
  9. }
  10. }

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

  1. main
  2. 分支任务

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

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

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

  1. public static void main(String[] args) throws InterruptedException {
  2. Thread t = new Thread(() -> {
  3. for (int i = 0; i < 10; i++) {
  4. System.out.println(Thread.currentThread().getName()+":"+i);
  5. try {
  6. Thread.sleep(1000);
  7. } catch (InterruptedException e) {
  8. // 1. e.printStackTrace();
  9. // 2. System.out.println("发现了中断标记");
  10. // 3.
  11. System.out.println("发现了中断标记,令进程死亡");
  12. return;
  13. }
  14. }
  15. });
  16. t.start();
  17. for (int i = 0; i < 5; i++) {
  18. System.out.println(Thread.currentThread().getName()+":"+i);
  19. Thread.sleep(1000);
  20. }
  21. t.interrupt();
  22. }
上传的附件
eject