随着计算机单核到多核的发展,程序追求的并行处理,实时性需求变得更好的实现。线程能充分利用当代多核处理器CPU处理能力,增强程序运行效率,达到多任务并行处理的效果。
Java中的线程
Java中创建一个线程有4种方式
- 实现Runnable接口
- Thread类(实现了Runnable 接口)
- ThreadPool(线程池执行)
- Callable/Future(带返回值的线程、线程池提交任务)
基本的应用
Thread
public class CallableDemo implements Callable<String>
{
@Override
public String call() throws Exception {
System.out.println("callable running");
return "Callable return";
}
}
public class RunnableDemo implements Runnable
{
@Override
public void run() {
System.out.println("Runnable running");
}
}
public class ThreadExtendDemo extends Thread{
@Override
public void run() {
System.out.println("Extend Thread running");
}
}
public class ThreadDemo {
public static void main(String[] args) {
// 方式一
RunnableDemo runnableDemo = new RunnableDemo();
new Thread(runnableDemo).start();
// 方式二
Thread threadRunning = new Thread(() -> System.out.println("new thread running"));
threadRunning.start();
new ThreadExtendDemo().start();
// 方式三
// 构建一个线程池 参数说明
// 核心线程数,最大线程数,空闲worker线程的存活时间,时间单位,用于缓存任务的阻塞队列,线程池达到最大线程数的拒绝策略
ThreadPoolExecutor
threadPoolExecutor = new ThreadPoolExecutor(2, 4, 3,
TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(3),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.DiscardOldestPolicy());
threadPoolExecutor.execute(() -> {
System.out.println("Thread pool create thread running");
});
threadPoolExecutor.shutdown();
//方式四
ExecutorService threadPool = Executors.newSingleThreadExecutor();
Future<String> future = threadPool.submit(new CallableDemo());
try {
System.out.println(future.get());
} catch (Exception e) {
// ignore
} finally {
threadPool.shutdown();
}
}
}
运行结果
Runnable running
new thread running
Extend Thread running
callable running
Callable return
Thread pool create thread running
并发的基础
线程的状态
从Thread类中State枚举可以看到线程共有6种状态
public enum State {
//新建
NEW,
// 运行(包括就绪和运行)
RUNNABLE,
//阻塞
BLOCKED,
//等待
WAITING,
// 等待,带超时时间(sleep(1000)方式转化过来)
TIMED_WAITING,
//结束
TERMINATED;
}
我们通过流程图了解线程状态的转化
线程状态demo
public class ThreadStatusDemo {
public static void main(String[] args) {
new Thread(() -> {
while (true) {
try {
TimeUnit.SECONDS.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "Time Waiting Thread").start();
new Thread(() -> {
while (true) {
synchronized (ThreadStatusDemo.class) {
try {
ThreadStatusDemo.class.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}, "Waiting Thread").start();
new Thread(() -> {
synchronized (ThreadStatusDemo.class) {
while (true) {
}
}
}, "Blocked Thread Holder").start();
new Thread(() -> {
synchronized (ThreadStatusDemo.class) {
while (true) {
}
}
}, "Blocked Thread").start();
}
}
通过jps查看进程,在使用jstack查看线程运行状态可以看到
"Blocked Thread" #13 prio=5 os_prio=31 tid=0x00007fb44d05f000 nid=0x3e03 waiting for monitor entry [0x000070000dbc7000]
java.lang.Thread.State: BLOCKED (on object monitor)
at com.ognice.threaddemo.ThreadStatusDemo.lambda$main$3(ThreadStatusDemo.java:38)
- waiting to lock <0x000000076ada7c58> (a java.lang.Class for com.ognice.threaddemo.ThreadStatusDemo)
at com.ognice.threaddemo.ThreadStatusDemo?Lambda$4/1791741888.run(Unknown Source)
at java.lang.Thread.run(Thread.java:748)
"Blocked Thread Holder" #12 prio=5 os_prio=31 tid=0x00007fb44da27800 nid=0x3f03 runnable [0x000070000dac4000]
java.lang.Thread.State: RUNNABLE
at com.ognice.threaddemo.ThreadStatusDemo.lambda$main$2(ThreadStatusDemo.java:31)
- locked <0x000000076ada7c58> (a java.lang.Class for com.ognice.threaddemo.ThreadStatusDemo)
at com.ognice.threaddemo.ThreadStatusDemo?Lambda$3/2065951873.run(Unknown Source)
at java.lang.Thread.run(Thread.java:748)
"Waiting Thread" #11 prio=5 os_prio=31 tid=0x00007fb44d05a000 nid=0x4103 in Object.wait() [0x000070000d9c1000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x000000076ada7c58> (a java.lang.Class for com.ognice.threaddemo.ThreadStatusDemo)
at java.lang.Object.wait(Object.java:502)
at com.ognice.threaddemo.ThreadStatusDemo.lambda$main$1(ThreadStatusDemo.java:22)
- locked <0x000000076ada7c58> (a java.lang.Class for com.ognice.threaddemo.ThreadStatusDemo)
at com.ognice.threaddemo.ThreadStatusDemo?Lambda$2/363771819.run(Unknown Source)
at java.lang.Thread.run(Thread.java:748)
"Time Waiting Thread" #10 prio=5 os_prio=31 tid=0x00007fb44e0d8800 nid=0x3a03 waiting on condition [0x000070000d8be000]
java.lang.Thread.State: TIMED_WAITING (sleeping)
at java.lang.Thread.sleep(Native Method)
at java.lang.Thread.sleep(Thread.java:340)
at java.util.concurrent.TimeUnit.sleep(TimeUnit.java:386)
at com.ognice.threaddemo.ThreadStatusDemo.lambda$main$0(ThreadStatusDemo.java:12)
at com.ognice.threaddemo.ThreadStatusDemo?Lambda$1/2129789493.run(Unknown Source)
at java.lang.Thread.run(Thread.java:748)
这里只展示了可以观察的状态,其他状态基本直接转化完成。关于线程的启动:我们知道通过Thread.start()方法可以启动一个线程。其底层原理是通过os::create_thread(this,thr_type,stack_sz); (hotsopt源码thread.cpp)调用操作系统命令去创建线程。
Thread的终止
jvm底层中Thread持有_interrupted标记(osThread.hpp文件中),用于判断线程终止状态。通过调用
//线程对象调用,出发终止信号
thread.interrupt();
//判断线程终止状态
Thread.currentThread().isInterrupted();
Thread中的native方法
//判断当前线程是否停止
private native boolean isInterrupted(boolean ClearInterrupted);
关于InterruptedException
当线程进行阻塞操作(调用wait,sleep,join等方法)时,如果这时调用了interrupt()方法去终止线程,阻塞线程会尝试终止正在做的事情,并会抛出InterruptedException,但是并不会终止线程,需要自己处理如何终止这个线程。即线程只是收到了终止的信号,并抛出了异常,但并不会帮你终止线程。只是告诉你线程终止了,后续需要怎么处理还是需要你自己决定。这时可以在异常处理去终止线程,或者抛出给外层处理。通过代码我们可以看到触发InterruptedException后,如果没有做处理,线程还是继续执行。
public class ThreadInterruptedDemo {
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(() -> {
while (!Thread.currentThread().isInterrupted()) {
try {
TimeUnit.SECONDS.sleep(2);
System.out.println("doing");
} catch (InterruptedException e) {
e.printStackTrace();
// 可通过Thread.currentThread.interrupt();break;等能终止的方法终止
}
}
});
thread.start();
TimeUnit.SECONDS.sleep(1);
thread.interrupt();
System.out.println(thread.isInterrupted());
}
}
运行情况
false
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at java.lang.Thread.sleep(Thread.java:340)
at java.util.concurrent.TimeUnit.sleep(TimeUnit.java:386)
at com.ognice.threaddemo.ThreadInterruptedDemo.lambda$main$0(ThreadInterruptedDemo.java:10)
at java.lang.Thread.run(Thread.java:748)
doing
doing
doing
doing
关于Thread.interrupted()
重置当前线程终止状态,属于类方法。其作用是告诉外界线程已经终止(状态标识复位为false),但是什么时候终止线程需要自己处理。
wait()与sleep()的区别
- wait属于对象的方法,sleep属于Thread方法
- wait会释放锁,sleep不会释放锁
- wait必须先获取到锁否则触发IllegalMonitorStateException异常,notify/notifyAll唤醒一样也需要
join()与yield()
- join:对象方法。join是等待线程的执行方法执行完成再继续望下走,可以用来实现线程的有序执行,需要捕获InnterruptedException
- yield:Thread类静态方法。yield作用是转化线程为就绪状态,让cpu重新调度线程。可以让优先级比当前线程高的线程获得执行的机会。