多线程基础运用
Posted 烟锁迷城
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了多线程基础运用相关的知识,希望对你有一定的参考价值。
1、进程与线程
进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。线程是进程的最小单位,一个进程有多个线程组成,它至少包含一个线程。
- 对于单核CPU而言,多线程就是不断分配时间片来切换线程。
- 对于多核CPU而言,多线程就是多核心并行执行,每一个核心处理一个线程。
多线程可以有效的提升程序运行的效率,在JAVA中是一种非常常用的编程方法。
2、具体实现
2.1、thread类
线程可以通过继承thread类,重写run方法,用start方法来进行调用。
public class ThreadDemo extends Thread{
@Override
public void run() {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("thread show");
}
public static void main(String[] args) {
ThreadDemo threadDemo = new ThreadDemo();
threadDemo.start();
System.out.println("main show");
}
}
2.1、runnable接口
实现Runnable接口后,重写run方法,用tread类的start方法进行调用
public class RunnableDemo implements Runnable {
@Override
public void run() {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("thread show");
}
public static void main(String[] args) {
RunnableDemo runnableDemo = new RunnableDemo();
new Thread(runnableDemo).start();
System.out.println("main show");
}
}
2.3、Callable接口
实现Callable接口,重写call方法,可以实现有返回值的线程,但是要注意,这里future的get方法是阻塞的,因为需要返回值。
import java.util.concurrent.*;
public class CallableDemo implements Callable<String> {
@Override
public String call() throws Exception {
Thread.sleep(2000);
return "callable show";
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
ExecutorService executorService = Executors.newFixedThreadPool(1);
CallableDemo callableDemo = new CallableDemo();
Future<String> future = executorService.submit(callableDemo);
System.out.println(future.get());
System.out.println("main show");
}
}
3、基础知识
3.1、生命周期
java线程有六种状态,分别是new(创建),runnable(运行),time_waiting(超时等待),waiting(等待),blocked(阻塞),terminated(死亡)。
系统线程的五种状态,分别是新生,就绪(ready),运行(running),等待(waiting),死亡。
- 一个线程先被创建,然后进入运行(分为就绪,运行两种)状态
- 加锁(synchronized修饰)会进入阻塞状态
- 使用不带等待时间的等待函数sleep(0),wait(),join(),LockSupport.park()会进入等待状态
- 使用加入唤醒时间的等待函数sleep(time),wait(time),join(time),LockSupport.parkUntil(time)会进入超时等待状态
- notify(),notifyall(),LockSupport.unpark()可以唤醒沉睡中的线程,使之进入运行(分为就绪,运行两种)状态
- run函数运行结束后,线程进入死亡状态。
3.2、线程结束
在线程运行中,可能会因为某些原因导致线程持续运行,无法停止。
3.2.1、线程终止
使用函数stop()可以将线程强制终止。
public class StopDemo extends Thread{
@Override
public void run() {
while (true) {
try {
System.out.println("thread show");
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
Thread thread = new StopDemo();
System.out.println("main show");
thread.stop();
}
}
使用stop()终止线程是有风险的。在java程序本身没有线程,它需要借助jvm调用操作系统的线程,所以java程序强制终止线程时,实际上终止的是操作系统的线程,这会带来未知的风险,因此终止线程还是建议使用中断标志。
3.2.2、中断标志
使用线程的中断标志,可以安全的中断线程。但实际上中断标志是逻辑中断,并不会真的终止线程,所以当线程处于类似死循环的状态时,中断标志无法中断线程。
public class Demo implements Runnable {
@Override
public void run() {
while (!Thread.currentThread().isInterrupted()) {
try {
System.out.println("thread show");
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("processor stop");
}
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(new Demo());
System.out.println("main show");
thread.start();
thread.interrupt();
}
}
中断标志Thread.currentThread().isInterrupted()的默认值是false,当执行thread.isInterrupted()方法后,中断标志变为true,这样就可以从线程外对线程逻辑进行控制。
3.2.3、线程复位
假如在上一个例子中的主程序增加一行代码,sleep(1000),会发生什么呢?
public class Demo implements Runnable {
@Override
public void run() {
while (!Thread.currentThread().isInterrupted()) {
try {
System.out.println("thread show");
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("processor stop");
}
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(new Demo());
System.out.println("main show");
thread.start();
Thread.sleep(1000);
thread.interrupt();
}
}
执行结果为:
从执行结果中可以发现,线程没有被中断,而是在抛出异常后继续执行,这意味着,线程的中断标志被恢复了,线程继续运行。
发生这个结果的原因是线程内部sleep(2000)这个语句开始生效,因为之前的中断标志在线程还未进入TIME_WAITING状态就已经被结束了,在设置延时1秒中断之后,线程成功进入超时等待状态,此刻线程中断标志变更,正在等待的线程被唤醒,抛出异常,但是没有任何响应,所以线程被复位,中断标志恢复成false,线程继续执行。
假如在异常处理中,再次执行线程中断呢?
public class Demo implements Runnable {
@Override
public void run() {
while (!Thread.currentThread().isInterrupted()) {
try {
System.out.println("thread show");
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
Thread.currentThread().interrupt();
}
}
System.out.println("processor stop");
}
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(new Demo());
System.out.println("main show");
thread.start();
Thread.sleep(1000);
thread.interrupt();
}
}
执行结果为:
此时可以看到,线程被成功中断。
这里推测,在interrupt()方法中,会做两件事情
- 将一个共享变量设置为true
- 将进入等待或超市等待状态下的线程唤醒
可以发现在所有的等待或是超时等待函数中,都要求必须被捕捉异常,这就是因为线程进入等待状态后用中断标志进行线程中断时,需要进行对应的执行操作,线程可以选择是继续执行,还是就此中断,这就将线程的执行权力交给当前线程而不是外部操作,这样的执行方式更加友好。
除了程序自动复位,还可以手动复位,使用静态方法interrupted方法即可复位。
Thread.interrupted();
3.2.4、阻塞状态中断与复位
既然等待与超时等待的线程有中断与复位,那么阻塞状态的线程是否可以用中断标志中断呢?
public class Demo implements Runnable {
@Override
public void run() {
synchronized (Demo.class){
while (!Thread.currentThread().isInterrupted()) {
try {
System.out.println(Thread.currentThread().getName()+" thread show");
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
Thread.currentThread().interrupt();
}
}
System.out.println(Thread.currentThread().getName()+" processor stop");
}
}
public static void main(String[] args) throws InterruptedException {
Thread thread1 = new Thread(new Demo(),"thread1");
Thread thread2 = new Thread(new Demo(),"thread2");
System.out.println("main start");
thread1.start();
thread2.start();
Thread.sleep(2000);
thread2.interrupt();
System.out.println("main thread2 end");
Thread.sleep(2000);
thread1.interrupt();
System.out.println("main thread1 end");
System.out.println("main stop");
}
}
执行结果为:
由此可见,虽然线程2早已经执行中断,但是因为处于阻塞状态,无法进行中断,只有线程1执行结束,放开锁,线程2开始执行,中断标志直接生效,甚至没有进入等待状态,也就没有复位发生,直接结束了线程。
对于阻塞状态的线程,中断和复位都没有意义。
以上是关于多线程基础运用的主要内容,如果未能解决你的问题,请参考以下文章
求解答以下的java源代码,详细点,说明这个程序的设计思路,还有比如运用了多线程的话运用了多线程的啥