目录
什么情况下应该使用多线程
如何使用多线程
继承Thread类创建线程
实现Runnable接口创建线程
实现Callable接口通过FutureTask包装器来创建Thread线程
如何把多线程用的更优雅
Request
RequestProcessor
PrintProcessor
SaveProcessor
Test
Java并发编程基础
线程状态State
通过代码演示线程的状态
通过命令现实线程状态
线程停止
interrupt方法
Thread.interrupted()一种线程复位的实现方式
其他线程复位场景
volatile关键字停止线程
根据一些常见的问题,对并发编程进行整理,总结。
线程出现的目的是什么?解决进程中多任务的实时性问题;简单来说,就是解决阻塞问题,阻塞意思就是程序运行到某个函数或过程后等待某些时间发生而暂时停止CPU占用的情况。这么看来,使用场景可以总结为:通过并行计算提高程序执行性能;需要等待网络、IO相应导致小号大量执行时间,可以采用异步线程的方式减少阻塞
在Java中,有多种方式来实现多线程。继承Thread类、实现Runnable接口、使用ExecutorService、Callable、Future实现带返回结果的多线程
Thread类本质上是实现了Runnable接口的一个实例,代表一个线程的实例。启动线程的唯一方法就是通过Thread类的start()实例方法。start()方法是一个native方法,它会启动一个新的线程,并执行run()方法。这种方式实现多线程很简单,通过自己的类直接extend Thread,并覆写run()方法,就可以启动新线程并执行自己定义的run()方法。
/**
* @author King Chen
* @Date: 2019/3/4 22:22
*/
public class ThreadTest extends Thread {
public static void main(String[] args) {
ThreadTest threadTest = new ThreadTest();
threadTest.start();
}
@Override
public void run() {
System.out.println("this is my Thread.run()");
}
}
运行结果
如果自己的类已经extends另一个类,就无法直接extends Thread,此时,可以实现一个Runnable接口
/**
* @author King Chen
* @Date: 2019/3/4 22:27
*/
public class RunnableTest implements Runnable{
public static void main(String[] args) {
RunnableTest runnableTest = new RunnableTest();
Thread thread = new Thread(runnableTest);
thread.start();
}
@Override
public void run() {
System.out.println("this is my Runnable.run()");
}
}
运行结果
有的时候,我们坑你需要让一步执行的线程在执行完成以后,提供一个返回值给到当前的主线程,主线程需要依赖这个值进行后续的逻辑处理,那么对这个时候,就需要用到带返回值的线程了。Java提供了这样的方式:
/**
* @author King Chen
* @Date: 2019/3/4 22:32
*/
public class CallableTest implements Callable {
public static void main(String[] args) throws ExecutionException, InterruptedException {
ExecutorService executorService = Executors.newFixedThreadPool(1);
CallableTest callableTest = new CallableTest();
Future future = executorService.submit(callableTest);
System.out.println(future.get());
executorService.shutdown();
}
@Override
public String call() throws Exception {
int a = 1;
int b = 2;
System.out.println(a + b);
return "执行结果:" + (a + b);
}
}
结果
合理的利用异步操作,可以大大提升程序的处理性能,下面这个案例,是从zookeeper抽取出来的,通过阻塞队列以及多线程的方式,实现对请求的异步化处理,提升处理性能
/**
* @author King Chen
* @Date: 2019/3/4 22:45
*/
public class Request {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Request{" +
"name='" + name + '\'' +
'}';
}
}
/**
* @author King Chen
* @Date: 2019/3/4 22:46
*/
public interface RequestProcessor {
void processRequest(Request request);
}
/**
* @author King Chen
* @Date: 2019/3/4 22:47
*/
public class PrintProcessor extends Thread implements RequestProcessor {
private final RequestProcessor nextProcessor;
LinkedBlockingDeque requests = new LinkedBlockingDeque<>();
public PrintProcessor(RequestProcessor nextProcessor) {
this.nextProcessor = nextProcessor;
}
@Override
public void run() {
while (true) {
try {
Request request = requests.take();
System.out.println("print data : " + request.getName());
nextProcessor.processRequest(request);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
@Override
public void processRequest(Request request) {
requests.add(request);
}
}
/**
* @author King Chen
* @Date: 2019/3/4 22:53
*/
public class SaveProcessor extends Thread implements RequestProcessor {
LinkedBlockingDeque requests = new LinkedBlockingDeque<>();
@Override
public void run() {
while (true) {
try {
Request request = requests.take();
System.out.println("begin save request info : " + request);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
@Override
public void processRequest(Request request) {
requests.add(request);
}
}
/**
* @author King Chen
* @Date: 2019/3/4 22:55
*/
public class Test {
PrintProcessor printProcessor;
protected Test() {
SaveProcessor saveProcessor = new SaveProcessor();
saveProcessor.start();
printProcessor = new PrintProcessor(saveProcessor);
printProcessor.start();
}
public static void main(String[] args) {
Request request = new Request();
request.setName("King");
new Test().doTest(request);
}
private void doTest(Request request) {
printProcessor.processRequest(request);
}
}
线程作为操作系统调度的最小单元,并且能够让多线程同时执行,极大的提高了程序的性能,在多喝环境下的优势更加明显。但是在使用多线程的过程中,如果对他的特性和原理不够理解,很容易造成各种问题。根据源码,来分析多线程的一些基本状态属性等
public enum State {
/**
* Thread state for a thread which has not yet started.
*/
NEW,
/**
* Thread state for a runnable thread. A thread in the runnable
* state is executing in the Java virtual machine but it may
* be waiting for other resources from the operating system
* such as processor.
*/
RUNNABLE,
/**
* Thread state for a thread blocked waiting for a monitor lock.
* A thread in the blocked state is waiting for a monitor lock
* to enter a synchronized block/method or
* reenter a synchronized block/method after calling
* {@link Object#wait() Object.wait}.
*/
BLOCKED,
/**
* Thread state for a waiting thread.
* A thread is in the waiting state due to calling one of the
* following methods:
*
* - {@link Object#wait() Object.wait} with no timeout
* - {@link #join() Thread.join} with no timeout
* - {@link LockSupport#park() LockSupport.park}
*
*
* A thread in the waiting state is waiting for another thread to
* perform a particular action.
*
* For example, a thread that has called Object.wait()
* on an object is waiting for another thread to call
* Object.notify() or Object.notifyAll() on
* that object. A thread that has called Thread.join()
* is waiting for a specified thread to terminate.
*/
WAITING,
/**
* Thread state for a waiting thread with a specified waiting time.
* A thread is in the timed waiting state due to calling one of
* the following methods with a specified positive waiting time:
*
* - {@link #sleep Thread.sleep}
* - {@link Object#wait(long) Object.wait} with timeout
* - {@link #join(long) Thread.join} with timeout
* - {@link LockSupport#parkNanos LockSupport.parkNanos}
* - {@link LockSupport#parkUntil LockSupport.parkUntil}
*
*/
TIMED_WAITING,
/**
* Thread state for a terminated thread.
* The thread has completed execution.
*/
TERMINATED;
}
Thread类中已经对线程状态描述的非常清晰了,这里就不再赘述。这里再贴上一张非常diao的图(不是我画的,不是我画的)
对源码的完整总结,有木有。
/**
* @author King Chen
* @Date: 2019/3/4 23:04
*/
public class Test1 {
public static void main(String[] args) {
//TIME_WAITING
new Thread(() -> {
while (true) {
try {
TimeUnit.SECONDS.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "timewaiting").start();
// //WAITING,线程在ThreadStatus类锁上通过wait进行等待
new Thread(() -> {
while (true) {
synchronized (Test1.class) {
try {
Test1.class.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}, "Waiting").start();
//线程在ThreadStatus加锁后,不会释放锁
new Thread(new BlockedTest(), "BlockDemo-01").start();
new Thread(new BlockedTest(), "BlockDemo-02").start();
}
static class BlockedTest extends Thread {
@Override
public void run() {
synchronized (BlockedTest.class) {
while (true) {
try {
TimeUnit.SECONDS.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
}
H:\IDEAWorkspace\......>jps
2480 Launcher
14548 Jps
5880 Test1
7532 RemoteMavenServer
8396
H:\IDEAWorkspace\......>jstack 5880
2019-03-04 23:16:16
Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.181-b13 mixed mode):
"DestroyJavaVM" #17 prio=5 os_prio=0 tid=0x00000000043fe800 nid=0x36c8 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"BlockDemo-02" #16 prio=5 os_prio=0 tid=0x000000001f9d8800 nid=0x2ddc waiting on condition [0x000000002068f000]
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.king.demo.mytest4.Test1$BlockedTest.run(Test1.java:44)
- locked <0x000000076b4ea1c8> (a java.lang.Class for com.king.demo.mytest4.Test1$BlockedTest)
at java.lang.Thread.run(Thread.java:748)
"BlockDemo-01" #14 prio=5 os_prio=0 tid=0x000000001f9d7800 nid=0x1134 waiting for monitor entry [0x000000002058f000]
java.lang.Thread.State: BLOCKED (on object monitor)
at com.king.demo.mytest4.Test1$BlockedTest.run(Test1.java:44)
- waiting to lock <0x000000076b4ea1c8> (a java.lang.Class for com.king.demo.mytest4.Test1$BlockedTest)
at java.lang.Thread.run(Thread.java:748)
"Waiting" #12 prio=5 os_prio=0 tid=0x000000001f9d7000 nid=0x2164 in Object.wait() [0x000000002048f000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x000000076b07caf8> (a java.lang.Class for com.king.demo.mytest4.Test1)
at java.lang.Object.wait(Object.java:502)
at com.king.demo.mytest4.Test1.lambda$main$1(Test1.java:26)
- locked <0x000000076b07caf8> (a java.lang.Class for com.king.demo.mytest4.Test1)
at com.king.demo.mytest4.Test1$$Lambda$2/1831932724.run(Unknown Source)
at java.lang.Thread.run(Thread.java:748)
"timewaiting" #11 prio=5 os_prio=0 tid=0x000000001f9d2000 nid=0x26d4 waiting on condition [0x000000002038f000]
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.king.demo.mytest4.Test1.lambda$main$0(Test1.java:15)
at com.king.demo.mytest4.Test1$$Lambda$1/990368553.run(Unknown Source)
at java.lang.Thread.run(Thread.java:748)
"Service Thread" #10 daemon prio=9 os_prio=0 tid=0x000000001f74c800 nid=0x534 runnable [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"C1 CompilerThread2" #9 daemon prio=9 os_prio=2 tid=0x000000001f747800 nid=0x27c8 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"C2 CompilerThread1" #8 daemon prio=9 os_prio=2 tid=0x000000001f6d7000 nid=0x3690 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"C2 CompilerThread0" #7 daemon prio=9 os_prio=2 tid=0x000000001f6d6800 nid=0x1050 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"Monitor Ctrl-Break" #6 daemon prio=5 os_prio=0 tid=0x000000001f6e5000 nid=0x36d8 runnable [0x000000001fd8f000]
java.lang.Thread.State: RUNNABLE
at java.net.SocketInputStream.socketRead0(Native Method)
at java.net.SocketInputStream.socketRead(SocketInputStream.java:116)
at java.net.SocketInputStream.read(SocketInputStream.java:171)
at java.net.SocketInputStream.read(SocketInputStream.java:141)
at sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:284)
at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:326)
at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:178)
- locked <0x000000076b1d09d8> (a java.io.InputStreamReader)
at java.io.InputStreamReader.read(InputStreamReader.java:184)
at java.io.BufferedReader.fill(BufferedReader.java:161)
at java.io.BufferedReader.readLine(BufferedReader.java:324)
- locked <0x000000076b1d09d8> (a java.io.InputStreamReader)
at java.io.BufferedReader.readLine(BufferedReader.java:389)
at com.intellij.rt.execution.application.AppMainV2$1.run(AppMainV2.java:64)
"Attach Listener" #5 daemon prio=5 os_prio=2 tid=0x000000001e1ef000 nid=0x3628 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"Signal Dispatcher" #4 daemon prio=9 os_prio=2 tid=0x000000001e1d9800 nid=0x3240 runnable [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"Finalizer" #3 daemon prio=8 os_prio=1 tid=0x0000000004b7b800 nid=0x3878 in Object.wait() [0x000000001f52f000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x000000076af08ed0> (a java.lang.ref.ReferenceQueue$Lock)
at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:144)
- locked <0x000000076af08ed0> (a java.lang.ref.ReferenceQueue$Lock)
at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:165)
at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:216)
"Reference Handler" #2 daemon prio=10 os_prio=2 tid=0x0000000004b72000 nid=0x1d4c in Object.wait() [0x000000001f42f000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x000000076af06bf8> (a java.lang.ref.Reference$Lock)
at java.lang.Object.wait(Object.java:502)
at java.lang.ref.Reference.tryHandlePending(Reference.java:191)
- locked <0x000000076af06bf8> (a java.lang.ref.Reference$Lock)
at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:153)
"VM Thread" os_prio=2 tid=0x000000001e1a7800 nid=0x2600 runnable
"GC task thread#0 (ParallelGC)" os_prio=0 tid=0x0000000004a98000 nid=0x100c runnable
"GC task thread#1 (ParallelGC)" os_prio=0 tid=0x0000000004a99800 nid=0x3e28 runnable
"GC task thread#2 (ParallelGC)" os_prio=0 tid=0x0000000004a9b000 nid=0x28dc runnable
"GC task thread#3 (ParallelGC)" os_prio=0 tid=0x0000000004a9c800 nid=0x2720 runnable
"VM Periodic Task Thread" os_prio=2 tid=0x000000001f79b000 nid=0x924 waiting on condition
JNI global references: 317
线程的启动过程想必已经非常熟悉了,如何终止一个线程呢?线程的终止,并不是简单的调用stop命令。虽然api仍然可以使用,但是和其他的线程控制方法suspend、resume一样都是过期了的不建议使用,stop方法再结束一个线程时并不会保证线程的组员正常释放,因此回到是程序可能出现一些不确定状态。如何优雅终端线程呢,来看看interrunpt方法吧。
当他打线程通过调用当前线程的interrupt方法,表示向当前线程打个招呼,告诉线程可以终端线程的执行了,中断的决定全在于线程自己。
线程通过检查自身是否被中断来进行相应处理,可以通过isInterrupted()来判断是否被中断;
/**
* @author King Chen
* @Date: 2019/3/4 23:25
*/
public class InterruptTest {
private static int i;
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(() -> {
while (!Thread.currentThread().isInterrupted()) {
i++;
}
}, "interruptTest");
thread.start();
TimeUnit.SECONDS.sleep(1);
thread.interrupt();
}
}
通过interrupt,设置一个标识告知线程终止,线程中还提供了静态方法,Thread.interrupted()对设置终端表示的线程复位。比如通过调用thread.interrupt();方法标识线程中断,而在线程中,可通过Thread.interrupted()方法将标识的线程复位。
/**
* @author King Chen
* @Date: 2019/3/4 23:25
*/
public class InterruptTest {
private static int i;
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(() -> {
boolean ii;
while (ii = Thread.currentThread().isInterrupted()) {
System.out.println("before : " + ii);
Thread.interrupted();
System.out.println("after : " + Thread.currentThread().isInterrupted());
}
}, "interruptTest");
thread.start();
TimeUnit.SECONDS.sleep(1);
thread.interrupt();
}
}
除了主动的调用Thread.interrupted()方法进行线程复位,还有一种被动的方式,在线程中,抛出InterruptedException异常。在该异常抛出之前,JVM会先把线程的中断表示位清楚,然后才会抛出InterruptedException,这个时候如果调用isInterrupted方法,将会返回false
定义一个volatile修饰的成员变量,来控制线程的终止。这实际上是应用了volatile能够实现多线程之间共享变量的可见性特点来实现的。
/**
* @author King Chen
* @Date: 2019/3/5 21:45
*/
public class VolatileDemo {
private volatile static boolean stop = false;
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(() -> {
int i = 0;
while (!stop) {
i++;
}
});
thread.start();
System.out.println("begin start thread");
Thread.sleep(1000);
stop = true;
}
}