提到多线程,那么不可避免的就会提到进程和线程。
我们回顾一下进程和线程的概念:
进程:
进程是资源分配的最小单位,是程序的运行实体,是正在运行的程序。
线程:
线程是CPU调度的最小单位,线程们共享所属进程的资源。
那么为什么会有线程呢?线程有什么优势呢?
首先是为什么会有线程呢?
这是因为某些聪明的人,为了让执行速度更快,而创建的线程。
我们知道,在最初,为了达到交互的目的,CPU分配时间片给进程去执行,所以就需要调度进程了,即保留当前进程的现场,和恢复下一个线程的执行环境等步骤。
而线程,相对于进程来说,他占有的资源更少,所以线程的调度更加的快,这样一来,对于同一个进程来说,就加快了执行的速度。
线程有什么优势呢?
线程快,可以并发执行。当然这是对于多核CPU来说的。对于单核CPU,显然,单线程比多线程更快,因为它节约了线程调度的时间。
对于JAVA的线程,一般我们使用的是官方的JDK,也就是Sun JDK,它是这么描述的:Windows版和Linux版都是使用一对一的线程模型来实现的,一条Java线程映射到一条轻量级进程之中。
一对一线程模型如下:
对于java等编程语言,实际上操作的是轻量级进程LWP,他和内核线程是一一对应的。所以可以被线程调度器调度到其他CPU上去执行。
这么看来,java的线程调度其实和java是没有关系了已经,线程调度完成交给了底层操作系统的线程调度器去执行,这样对于编程人员来说调度其实就是透明的,我们只需要知道他们会并行就可以了,所以JDK中没有怎么提到线程是如何调度的,因为这已经不是java的事了。这也是造成线程的setProperty()方法,设置线程优先级并不可靠的原因。
所以,我们创建出来的线程是利用多CPU的,这就实现了并行,提高了执行的效率。
那么如何创建线程呢?
有两种方式,一种继承Thread类,一种实现Runnable接口。
例如:
继承Thread类。
class MyThread extends Thread { ... @Override public void run() { ... } }实现Runnable接口。 class MyThread implements Runnable { ... @Override public void run() { ... } }他们两者的实现并没有多少区别。查看java api可以发现:
可以看到,Thread也实现了Runnable接口。他们的区别实际上就是继承Thread类的方式受限与java的单继承,相对来说没那么灵活,实际效果都是一样的。
Thread类有很多方法,请自行查看java api。
如何终止线程:
可以使用如下几种方式:
1.使用退出标志。
2.使用interrupt()方法中断线程。
3.使用stop方法强制终止线程(极度不推荐)。
1.使用退出标志终止线程:
public class Main { static class MyThread extends Thread { volatile boolean exit = false; public void stopThread() { exit = true; } @Override public void run() { while (!exit) { try { sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("线程退出了"); } } public static void main(String[] args) throws InterruptedException { MyThread mt = new MyThread(); Thread thread = new Thread(mt); thread.start(); sleep(5000); System.out.println("我停止线程了"); mt.stopThread(); } } 2.使用Interrupt()方法中断线程。使用interrupt()中断线程实际上和使用中断标记是差不多的。调用了线程的interrupt()方法,并不会真正意义上的中断线程,仅仅是给线程打上中断标记。
public class Main { static class MyThread extends Thread { @Override public void run() { while (!Thread.currentThread().isInterrupted()) { try { sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); System.out.println("异常了"); break; } } System.out.println("线程退出了"); } } public static void main(String[] args) throws InterruptedException { MyThread mt = new MyThread(); Thread thread = new Thread(mt); thread.start(); sleep(5000); System.out.println("我停止线程了"); thread.interrupt(); } }我们可以使用interrupt方法,然后通过判断isInterrupted()来选择是否终止线程。当然,他们的最根本的区别在于,如果线程处于阻塞状态,调用Interrupt方法的话,线程会抛出 InterruptedException异常。结果如下所示:
java.lang.InterruptedException: sleep interrupted at java.lang.Thread.sleep(Native Method) at Main$MyThread.run(Main.java:14) at java.lang.Thread.run(Thread.java:745) 我停止线程了 异常了 线程退出了3.stop方法终止线程。
stop方法终止线程非常的暴力,直接中断。这样会导致线程无法正确的释放某些资源而造成系统的损坏。这个方法已经是deprecated的,不建议使用。
线程的优先级
java线程的优先级分为十个级别,可以通过setPriority方法来设置线程的优先级。
java线程的优先级是不可靠的。通过最上面的原理我们可以知道,java的线程仅仅是使用了内核线程的抽象LWP,而实际上调度线程的是底层的操作系统。我们知道,Window的优先级的级别是7个,而java是10个,他们总要建立对应关系,但显然这是做不到契合的对应的,这是其一。
其二,我们可以查看java api的yield方法,他是这么描述的:
可以看到,第一句话,操作系统的线程调度器是可以忽略代码的执行的。虽然这个和优先级并不是太相关,但我们也可以从中看到java的线程的真正的执行顺序并不能由java本身来控制,而是和操作系统相关的。也就是说,我们不能期望利用线程的优先级去设想代码的执行顺序。