suspend、resume、stop
- suspend:线程挂起:阻塞,但是不会释放持有资源,包含锁资源(过期)
- resume:恢复线程执行:挂起-->就绪
- stop: 暴力终止线程
终止线程优雅做法,设置线程执行的开关
volatile 保证数据的可见性,线程执行时每次都会从公共内存加载on数据
Thread.currentThread().isInterrupted(): 判断线程是否被中断:interrupted()
private static class Runner implements Runnable {
private long i;
private volatile boolean on = true;
[@Override](https://my.oschina.net/u/1162528)
public void run() {
while (on && !Thread.currentThread().isInterrupted()){
i++;
}
System.out.println("Count i = " + i);
}
public void cancel() {
on = false;
}
}
缺点:
-
独占,suspend方法是线程作用的公共同步对象的锁不会被释放,造成其他线程无法访问公共同步对象
-
数据不一致:例如对象属性赋值时中间挂起执行线程,导致整个线程的关联的对象属性赋值执行过程不完整,造成数据不一致的情况
- 测试代码
package com.zhiwei.thread;
public class ThreadSuspend {
@SuppressWarnings("deprecation")
public static void main(String[] args) throws InterruptedException {
SuspendThread suspendThread = new SuspendThread();
suspendThread.start();
Thread.sleep(5000);
System.out.println("线程第1次被挂起-------------------");
suspendThread.suspend();
System.out.println("线程第1次恢复运行-------------------");
suspendThread.resume();
Thread.sleep(5000);
System.out.println("线程第2次被挂起-------------------");
suspendThread.suspend();
}
}
class SuspendThread extends Thread{
private long i = 0;
public long get(){
return i;
}
public void set(long i){
this.i = i;
}
@Override
public void run() {
while(true){
System.out.println("i:"+i);
try {
Thread.sleep(1000);
i++;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
效果:
wait、notify
wait:当前访问对象的线程被阻塞,并且放弃之前拥有的资源,直到被notify/notifyAll方法唤醒,在原来中断的地方继续执行
扩展:线程从原来中断的地方继续执行的原因:操作系统是统一管理所有的软硬件资源的软件,进程是程序分配资源的单位,线程则是进程进行资源进一步划分的最小的分配资源单位,当线程或进程被中断的时候,操作系统会自动将当前线程/进程的状态存入线程控制块(TCB)和进程控制块(PCB),然后阻塞当前运行的线程/进程,当线程/进程达到运行的条件,操作系统会从之前保存的TCB/PCB中读取信息,恢复中断前的状态,所以线程/进程就成从中断的地方继续运行
notify: 唤醒访问目标对象的1个阻塞线程,线程进入就绪状态,分配CPU资源即可运行
注意:如果存在多个线程访问同一个对象,notify唤醒的线程是随机的,notifyAll可以唤醒所有的阻塞线程
测试案例:
package com.zhiwei.thread;
public class WaitAndNotify {
private static Object object = new Object();
public static void main(String[] args) throws InterruptedException{
threadWait tw = new threadWait();
threadNotify tn = new threadNotify();
Thread th1 = new Thread(tw);
Thread th2 = new Thread(tn);
th1.start();
th2.start();
}
private static class threadWait implements Runnable{
@Override
public void run() {
synchronized (object) {
try {
System.out.println(Thread.currentThread().getName() + ":object对象锁释放前!");
object.wait();
System.out.println(Thread.currentThread().getName() + ":object对象锁释放后!");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
private static class threadNotify implements Runnable{
@Override
public void run() {
synchronized (object) {
try {
System.out.println(Thread.currentThread().getName()+":object对象锁获取前!");
object.notify();
System.out.println(Thread.currentThread().getName()+":object对象锁获取后!(JVM决定唤醒另外线程)");
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
结果:
Thread-0: object对象锁释放前!
Thread-1:object对象锁获取前!
Thread-1: object对象锁获取后!(JVM决定唤醒另外线程)
Thread-0:object对象锁释放后!
分析:
-
线程threadWait执行时synchronized (object)的原因,线程threadNotify线程阻塞,等待threadWait释放对象锁才可以执行
-
当threadWait执行到 object.wait()时,threadWait线程被手动阻塞中断,释放对象锁,因此控制台只会输出之前的
“Thread-0:object对象锁释放前!”
- 因threadWait释放对象锁,线程threadNotify运行,当执行object.notify(); 时,因object只有1个阻塞线程,因此threadWait线程被唤醒,但是由于threadNotify还在执行同步代码块,因此线程threadWait进入阻塞状态,当线程threadNotify执行完成同步代码块,控制台输出
"Thread-1:object对象锁获取前!
Thread-1:object对象锁获取后!(JVM决定唤醒另外线程)"
- threadNotify持有的object对象锁释放,threadWait获取对象锁继续执行,控制台输出
"Thread-0:object对象锁释放后!"
await、signal
await和signal类似wait和notify的关系,await会阻塞当前线程,直到接收到signal后唤醒继续执行,这也是线程的一种通信机制
需求:多线程交替打印字符
测试代码
package com.zhiwei.thread;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class TheadAwait {
public boolean flag = true;
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
public void get() {
try {
Thread.sleep(5000);
lock.lock();
while (!flag) {
condition.await();
}
System.out.println(Thread.currentThread().getName()+":-----------------");
flag = !flag;
condition.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void put() {
try {
Thread.sleep(5000);
lock.lock();
while (flag) {
condition.await();
}
System.out.println(Thread.currentThread().getName()+":*****************");
flag = !flag;
condition.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public static void main(String[] args) {
TheadAwait alternatelyPrint = new TheadAwait();
while (true) {
new Thread(new Runnable() {
@Override
public void run() {
alternatelyPrint.get();
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
alternatelyPrint.put();
}
}).start();
}
}
}
效果:
Callable、Future
Callable: 可理解成线程执行的具体任务,但具有返回值 Future:Callable执行的返回值
作用:在线程执行的过程中可获取到线程执行的状态,例如异步的情况,可通过线程执行后的状态进行响应的控制,而不至于因为线程执行的不确定性造成不可控
注意:future.get() 会造成线程阻塞,一致到有返回值才进行下一步操作
- 测试代码
package com.zhiwei.thread;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
/**
* callable对象是具有返回值的对象,并且将结果返回给Future对象:
* completService可以一次执行一组callable独享方法,并返回最早的结果
*/
public class CallableAndFuture {
public static void main(String[] args) throws Exception {
ExecutorService threadPool = Executors.newSingleThreadExecutor();
Future<String> future = threadPool.submit(new Callable<String>() {
public String call() throws Exception {
Thread.sleep(5000);
return "hello Java World!";
}
});
System.out.println("5s线程返回结果:" + future.get());
threadPool.shutdown();
}
}
效果:5s后