4-5 多线程
1、继承Thread类
public class MyThread extends Thread {
public static void main(String[] args) {
MyThread myThread = new MyThread();
myThread.start();
}
@Override
public void run() {
System.out.println("一二三四五");
try {
Thread.sleep(1000);
} catch (Exception e) {
e.printStackTrace();
}finally {
System.out.println("六七八九十");
}
}
}
继承Thread类的方式创建线程算是最简单的了,但是你的线程类往往要继承项目中的其他类,而Java是单继承机制的,所以使用此方法会有很大的局限性。
2、实现Runnable 接口
public class MyRunnable implements Runnable {
public static void main(String[] args) {
MyRunnable myRunnable = new MyRunnable();
Thread thread = new Thread(myRunnable);
thread.start();
}
@Override
public void run() {
System.out.println("一二三四五");
try {
Thread.sleep(1000);
} catch (Exception e) {
e.printStackTrace();
}finally {
System.out.println("六七八九十");
}
}
}
Thread类的构造方法允许传入一个实现runnable接口的target进去,线程启动将会执行target.run方法。实现runnable接口的方式可以很好的避免单继承问题。
3、实现Callable接口
public static void main(String[] args) throws Exception {
System.out.println("12123");
FutureTask<Integer> futureTask = new FutureTask<>(new MyThread());
new Thread(futureTask).start();
// futureTask可以在指定时间内获取线程执行的返回值,超时则丢弃任务
// 因此futureTask可以用作异步任务处理
futureTask.get(1000, TimeUnit.SECONDS);
}
@Override
public Integer call() throws Exception {
int x = 100;
int y = 200;
return x+y;
}
call方法与run方法最大的区别在于call方法存在返回值futureTask 的get方法可以获取这个返回值。使用此种方法实现线程的好处是当你创建的任务的结果不是立即就要时,你可以提交一个线程在后台执行,而你的程序仍可以正常运行下去,在需要执行结果时使用futureTask去获取即可。这是一种典型的异步任务处理的方法。
4、线程的各种状态及转换
4.1、六种状态
- New:初始状态,线程被创建,没有调用start()
- Runnable:运行状态,Java线程把操作系统中的就绪和运行两种状态统一称为“运行中”
- Blocked:阻塞,线程进入等待状态,线程因为某种原因,放弃了CPU的使用权
阻塞的几种情况:- 等待阻塞:运行的线程执行了wait(),JVM会把当前线程放入等待队列
- 同步阻塞:运行的线程在获取对象的同步锁时,如果该同步锁被其他线程占用了,JVM会把当前线程放入锁池中
- 其他阻塞:运行的线程执行sleep(),join()或者发出IO请求时,JVM会把当前线程设置为阻塞状态,当sleep()执行完,join()线程终止,IO处理完毕线程再次恢复
- Waiting:等待状态
- timed_waiting:超时等待状态,超时以后自动返回
- terminated:终止状态,当前线程执行完毕
当实例化一个线程之后,首先进入初始状态,即New状态,此时线程在启动的时候并不是立刻就运行,而是要等到操作系统调度之后才运行,然后调用start()进入运行状态,即runnable,其中运行状态中包括运行(running)和就绪(ready)两种状态,这两种状态在操作系统的调度下可以互相转换,如果运行中的线程时间片被CPU抢占的话就会变成就绪状态;运行中的线程通过调用synchronized方法或synchronized块进入阻塞状态,即blocked,当线程获取到锁之后进入运行状态;如果线程在执行过程中调用了sleep(),wait().join(),Locksupported.parkUtil()等方法时,会进入等待状态(waiting)或超时等待状态,即timed_waiting,再次调用notify(),notifyAll(),Locksupported.unpark()等方法时,又会重新进入运行时状态,当线程执行完成时,就进入了终止状态,即terminated状态。
5、线程的停止 stop() 和 suspend()
要终止一个线程,并不是简单的调用stop()方法,stop()就像linux中的kill一个线程一样是非常暴力的,虽然在Java的API中仍然可以调用stop()方法,但是和suspend,resume方法一样,是过期了的,不建议使用的,因为stop()在结束一个线程时并不会保证线程的资源正常释放,会导致程序可能会出现一些不确定的状态。
suspend与wait和sleep的区别:wait会使当前线程等待但是会释放锁,sleep不释放锁但是会自己唤醒自己,因此sleep与wait都不会死锁。而suspend即不释放锁也不能自己唤醒,会产生死锁所以被弃用。
6、Lambda表达式
语法结构 () -> {};
():方法列表
{}:方法体