java并发(一):创建线程、线程状态、线程属性

Java线程

runnable 创建线程

在一个单独线程中运行一个任务需要三步:

1.需要一个实现了Runnable接口的类,将这个任务需要执行的代码放到这个类的run方法中。

public class multithread implements Runnable{
    public static void main(String[] args) {
        multithread m = new multithread();
        Thread thread = new Thread(m);
        thread.start();
    }

    @Override
    public void run() {
        System.out.println(123);
    }
}

因为Runnable是函数式接口,所以可以用lambda表达式代替实现这个接口。

public class multithread{
	public static void main(String[] args) {
		Runnable r = () -> { System.out.println(123); };
		Thread t = new Thread(r);
		t.start();
	}
}

2.从Runnable构造Thread对象。
3.启动线程。

下面让两个线程同时运行:

public class multithread{
    public static void main(String[] args) {
        Runnable r1 = () -> {
            for (int i = 0; i < 5; i++) {
                System.out.println("thread 1: " + i);
            }
        };
        Runnable r2 = () -> {
            for (int i = 0; i < 5; i++) {
                System.out.println("thread 2: " + i);
            }
        };
        new Thread(r1).start();
        new Thread(r2).start();
    }
}

结果是交替出现的,说明确实是并发运行。

thread 1: 0
thread 1: 1
thread 1: 2
thread 1: 3
thread 2: 0
thread 2: 1
thread 2: 2
thread 2: 3
thread 2: 4
thread 1: 4

继承thread创建线程

这是一种过时的创建方式,因为这种方法将要执行的任务和并行运行的机制绑定了,应该对二者解耦。

class MyThread extends Thread{
	public void run(){
		// task	
	}
}

调用thread或runnable的run方法

public class multithread{
    public static void main(String[] args) {
        Runnable r1 = () -> {
            for (int i = 0; i < 5; i++) {
                System.out.println("thread 1: " + i);
            }
        };
        Runnable r2 = () -> {
            for (int i = 0; i < 5; i++) {
                System.out.println("thread 2: " + i);
            }
        };
        r1.run();
        new Thread(r1).start();
        new Thread(r2).start();
    }
}

这样做不会创建一个新的线程去执行任务,还是在原来的线程执行。

thread 1: 0
thread 1: 1
thread 1: 2
thread 1: 3
thread 1: 4 // 在main线程执行完r1的run才能执行后面的并行操作
thread 1: 0
thread 2: 0
thread 2: 1
thread 2: 2
thread 2: 3
thread 2: 4
thread 1: 1
thread 1: 2
thread 1: 3
thread 1: 4

线程状态

六种状态:

  1. New
  2. Runnable
  3. Blocked
  4. Waiting
  5. Timed waiting
  6. Terminated

下面对每种状态进行详细描述。

新建

new Thread(r)之后对线程处于新建状态,没有开始运行。

可运行

调用start()方法之后线程变成可运行的,但不一定正在运行。操作系统一般使用抢占式调度,根据优先级给每个线程一定的时间片来执行,时间片用完后剥夺运行权。

阻塞和等待

此时的线程是不活动的,也就是不运行代码,需要重新调度来激活。以下是几种导致线程进入阻塞或等待的原因:

  1. 当线程尝试获取一个被其他线程占有的锁,此线程就会被阻塞,直到其他线程都释放了这个锁且线程调度器允许该线程持有此锁。
  2. 当 条件
  3. 调用一些有超时参数的方法会让线程进入计时等待,如Thread.sleepObject.waitThread.join

当线程被重新激活时,调度器会检查它的优先级并安排运行。

终止线程

线程在完成run方法后自然终止,或因没有捕获的异常意外终止。以前有些方法能暂停或终止线程,如stopsuspendresume,但这些方法现在都已经废弃。

线程属性

中断线程

除了已经被废弃的方法,Java不能强制终止进程,但是可以用interrupt和异常机制来实现终止。

对一个线程对象调用interrupt方法会设置线程的中断状态,每个线程都会不时的检查这个状态

thread.interrupt();

当线程被阻塞就无法检查中断状态,Java引入了InterruptedException异常,在一个因为调用sleepwait而阻塞的线程上调用interrupt方法,那个阻塞调用就会被异常中断。被中断的线程可以自己处理异常,比如继续执行或终止线程。

Runnable r = () -> {
	try{
		// task
	}
	catch(InterruptedException e){
		// thread is interrupted during sleep or wait
	}
	finally{
		// clean up, if require
	}
};

如果线程执行的代码中有Thread.sleep(),那么必须给用try catch捕捉InterruptedException异常或将异常抛出到run方法,原因就是上面说的,这些阻塞调用会被异常中断,所以我们需要在代码中处理。

守护线程

守护线程是给其他线程提供服务的,比如计时器线程、清空过期缓存项的线程等。当进程中只剩下守护线程,虚拟机就会退出。在线程启动之前调用setDaemon(true)可以把线程转为守护线程。

线程名

在线程转储时,线程的名字可能比较有用,可以用setName("name")来设置。

线程优先级

默认,一个线程会继承构造它的线程的优先级,可以用setPriority设置线程优先级,最小Thread.MIN_PRIORITY为1,最大为10。但这个优先级依赖于系统的实现,所以实践中并不一定有效。

上一篇:HarmonyOS 多线程


下一篇:多线程(上)