第一章 多线程与并发基础知识
1、线程状态转换
Thread.State
源码:(6个状态)
public static enum State {
NEW,
RUNNABLE,
BLOCKED,
WAITING,
TIMED_WAITING,
TERMINATED;
private State() {
}
}
-
NEW
(新建):当线程对象被创建但还未启动时,处于这个状态。即线程对象已经通过new关键字创建,但还没有调用其start()
方法。 -
RUNNABLE
(可运行):当线程被start()方法启动后,处于这个状态。表示线程正在Java虚拟机中运行或准备运行。当线程获得了CPU
执行权时,就可以从Runnable
状态进入到Running
状态。 -
BLOCKED
(阻塞):当线程因为某些原因无法获得所需的锁资源时,处于这个状态。例如,如果一个线程正在执行同步代码块,并且另一个线程试图获取相同的锁,那么该线程将被阻塞,直到持有锁的线程释放锁。 -
WAITING
(等待):当线程处于等待状态时,表示线程暂时停止执行,直到其他线程显式地唤醒它。 ①线程可以通过调用wait()
方法进入等待状态(若需要退出等待,唤醒线程需调用notify()
或notifyAll()
);②线程通过Thread.join()
方法等待其他线程结束。在这种状态下,线程不会占用CPU
资源。 -
TIMED_WAITING
(计时等待):与Waiting
状态类似,但是可以指定等待的时间。线程可以通过调用带有超时参数的wait()
方法、sleep()
方法或join()
方法来进入计时等待状态。 -
TERMINATED
(终止):当线程的run()
方法执行完毕或者出现异常时,线程进入终止状态。一旦线程进入终止状态,它就不可再次启动。
Waiting / Timed Waitiing
进入等待 | 退出方法 |
---|---|
没有设置 Timeout 参数的 Object.wait() 方法 | Object.notify() / Object.notifyAll() |
Thread.sleep() 方法 | 时间结束 |
设置了 Timeout 参数的 Thread.join() 方法 | 时间结束 / 被调用的线程执行完毕 |
2、实现线程入门类和接口
- 实现
Runnable
接口 ; 无返回值 - 继承
Thread
类 ; 无返回值 - 实现
Callable
接口 ,结合Future
或FutureTask
使用 ; 有返回值
2.1、实现 Runnable 接口
需要实现 run()
方法。
Runnable
是一个函数式接口,在此使用 Java8 的函数式编程 创建了一个线程
new Thread(() -> {
log.info("Gao使用Java8 函数式编程,创建了一个线程.....");
}).start();
2.2、继承Thread类
因为 Thread
类也实现了 Runable
接口,所以同样需要实现 run()
方法
当调用 start()
方法启动一个线程时,虚拟机会将该线程放入就绪队列中等待被调用,当一个线程被调度时会执行该线程的 run()
方法。
@Slf4j
public class ThreadUtil extends Thread {
@Override
public void run() {
log.info("Gao 继承了Thread,创建了一个线程.....");
}
public static void main(String[] args) {
new ThreadUtil().start();
}
}
列举 Thread
类中的常用方法
currentThread()
:静态方法,返回对当前正在执行的线程对象的引用;start()
:开始执行线程的方法,java虚拟机会调用线程内的run()方法;yield()
:指的是当前线程愿意让出对当前处理器的占用。注意:就算当前线程调用了yield()方法,程序在调度的时候,也还有可能继续运行这个线程的;sleep()
:静态方法,使当前线程睡眠一段时间;join()
:使当前线程等待另一个线程执行完毕之后再继续执行,内部调用的是Object类的wait方法实现的;
2.3、实现Callable接口
与 Runnable
相比,Callable
可以有返回值,而且支持 泛型
@Slf4j
public class ThreadUtil implements Callable {
@Override
public Object call() throws Exception {
return " 实现Callable 接口.......";
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
ExecutorService pool = Executors.newCachedThreadPool();
ThreadUtil threadUtil = new ThreadUtil();
Future future = pool.submit(threadUtil);
// 注意调用get方法会阻塞当前线程,直到得到结果。
System.out.println(future.get());
}
}
2.4、Future 接口
Future
接口中只有几个简单的方法:
public interface Future<V> {
boolean cancel(boolean var1);
boolean isCancelled();
boolean isDone();
V get() throws InterruptedException, ExecutionException;
V get(long var1, TimeUnit var3) throws InterruptedException, ExecutionException, TimeoutException;
}
cancel()
: 方式是试图取消一个线程的执行(是试图取消,并不一定取消成功),参数 paramBoolean
表示是否采用中断的方式取消线程执行。
2.5、FutureTask类
FutureTask
是实现的 RunnableFuture
接口,而 RunnableFuture
接口同时继承了 Runnable
接口和 Future
接口;固 FutureTask
是 Future
的一个实现类。
@Slf4j
public class ThreadUtil implements Callable {
@Override
public Object call() throws Exception {
return " 实现Callable 接口.......";
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
ExecutorService pool = Executors.newCachedThreadPool();
ThreadUtil threadUtil = new ThreadUtil();
FutureTask task = new FutureTask<>(threadUtil);
pool.submit(task);
// 注意调用get方法会阻塞当前线程,直到得到结果。
System.out.println(task.get());
}
}
上述代码示例与 Future
的代码示例有着略微的区别:
- 此处调用
submit
方法是没有返回值的。实际上是调用的submit(Runnable task)
方法,而上面的Future
代码示例,调用的是submit(Callable<T> task)
方法 FutureTask
直接取get()
取值,而上面的Future
代码示例是通过submit()
方法返回的Future
去取值
在很多高并发的环境下,有可能Callable和FutureTask会创建多次。FutureTask能够在高并发环境下确保任务只执行一次。