线程六种状态
- New:尚未启动的线程的线程状态(new Thread)
- Runnable:可运行线程的线程状态,等待cpu调度(调用star)
- Blokcked: 线程阻塞等待监视器锁定的线程专状态,处于synchronized同步代码块或方法中被阻塞。
- Waiting: 线程等待的线程状态。
不带timeout参数的方式调用Object.wait, thread.jion, LockSupport.park - Timed Waiting: 具有指定等待时间的等待线程的线程状态,下列超时的方式:Thread.sleep, Object.wait, Thread.join, LockSupport.parkNanos, LockSupport.parkUntil
- Terminated: 终止线程的线程状态, 线程正常完成执行或者出现异常
public class ScannerTest {
public static void main(String[] arg) throws InterruptedException {
test1();
};
public static void test1() throws InterruptedException{
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("线程1运行");
}
});
System.out.println("1调用前状态"+ thread1.getState().toString());
Thread.sleep(1000L);
thread1.start();
System.out.println("2调用后状态"+ thread1.getState().toString());
Thread.sleep(2000L);
System.out.println("2s后的状态" + thread1.getState().toString());
}
}
/**
1调用前状态NEW
2调用后状态RUNNABLE
线程1运行
2s后的状态TERMINATED
**/
public static void test2() throws InterruptedException {
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(5000L);
System.out.println("线程2运行" + Thread.currentThread().getState());
} catch (Exception e) {
e.printStackTrace();
}
}
});
thread2.start();
System.out.println("调用后状态:" + thread2.getState().toString());
Thread.sleep(2000L);
System.out.println("等待2s后状态:" + thread2.getState().toString());
}
/*
调用后状态:RUNNABLE
等待2s后状态:TIMED_WAITING
线程2运行RUNNABLE
*/
public static void test3() throws InterruptedException {
Thread thread3 = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("抢锁之前状态" + Thread.currentThread().getState());
synchronized (ThreadTest.class) {
System.out.println("拿到锁继续执行" + Thread.currentThread().getState());
}
}
});
synchronized(ThreadTest.class) {
thread3.start();
Thread.sleep(2000L);
System.out.println("调用等待2s状态" + thread3.getState().toString());
Thread.sleep(4000L);
}
}
/*
抢锁之前状态RUNNABLE
调用等待2s状态BLOCKED
拿到锁继续执行RUNNABLE
*/
终止线程
1.过时的stop方法
如果线程里面的程序没有执行完,会被终止点,后面的不在继续执行
public class StopThread {
public static void main(String[] args) throws InterruptedException {
MyThread thread = new MyThread();
thread.start();
Thread.sleep(1000);
thread.stop();
while(thread.isAlive()) {} // 确保线程被终止了,
//打印结果
thread.print();
}
}
class MyThread extends Thread{
private int i=0,j=0;
@Override
public void run(){
synchronized(this) {
++i;
try {
Thread.sleep(5000);
}catch(InterruptedException e) {
e.printStackTrace();
}
++j;
}
System.out.println("释放");
}
public void print() {
System.out.println("i=" + i + "j="+j);
}
}
// i=1j=0
2.interrupt终止
把上面代码的thread.stop(); 换成 thread.interrupt();
会报错, 可以看到虽然终止了程序但是后面依然执行了,可以再异常里面做其他处理
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
释放
at com.Hello.ScannerTest.MyThread.run(StopThread.java:23)
i=1j=1
- 用标志位的方式中断线程
在线程外面用一个标志来判断线程是否可执行
public class StopTh {
public volatile static boolean flag = true;
public static void main(String[] args) throws InterruptedException {
new Thread(()->{
try {
while(flag) {
System.out.println("运行");
Thread.sleep(1000L);
}
}catch(InterruptedException e) {
e.printStackTrace();
}
}).start();
Thread.sleep(3000L);
flag = false;
System.out.println("运行结束");
}
}
/*运行
运行
运行
运行结束
*/
线程封闭
多线程访问共享可变数据时,涉及到线程间数据同步问题
并不是所有时候,都要用到共享数据,若数据都被封闭在各自的线程之中,就不需要同步,这种通过将数据封闭在线程中而避免使用同步的技术称为线程封闭
ThreadLocal
使用ThreadLocal 就算一个线程改变了值,但是对其他线程获取这个值原来的数据 没有任何影响
public class ThreadLocalTest {
public static ThreadLocal<String> value = new ThreadLocal<>();
public static void main(String[] arg0) {
new Thread(new Runnable() {
@Override
public void run () {
value.set("11111");
try {
Thread.sleep(1000L);
}catch(InterruptedException e) {
e.printStackTrace();
}
System.out.println(value.get());
}
}).start();
new Thread(new Runnable() {
@Override
public void run () {
try {
Thread.sleep(1000L);
System.out.println(value.get());
}catch(InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
}
// 11111
// null
栈封闭
局部变量的固有属性之一就是封闭在线程中
他们位于线程的栈中,其他线程无法访问这个栈
线程池
线程池管理器:用于创建并管理线程池,包括创建线程池,销毁线程池,添加新任务,
工作线程:线程池中线程,可以虚幻的执行任务,在没有任务时处于等待状态;
任务接口:每个任务必须实现的接口,以供工作线程调度任务的执行,他主要规定了任务的入口,任务完成后的收尾工作,任务的执行状态等;
任务队列:用于存放没有处理的任务,提供一种缓冲机制;
线程池类的结构:
接口:Executor 最上层的接口,之定义了execute
接口:ExecutorService 继承了Exexutor ,拓展了Callable,Future,关闭方法;
接口:ScheduledExecutorService 继承ExecutorService,增加了定时任务相关的方法,
实现类:ThreadPoolExecutor 基础,标准的线程池实现
实现类: ScheduledThreadPoolExecutor 继承了ThreadPoolExecutor,实现了ScheduledExecutorService中相关定时任务的方法
Runnale 和Callable 一些区别
public class RunnableTest {
static ThreadPoolExecutor pool =
new ThreadPoolExecutor(1, 2, 5, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
public static void main(String args[])throws ExecutionException,InterruptedException {
Future run_future = pool.submit(new MyRunnable());
System.err.println("run_future:" + run_future.get());
Future call_futter =pool.submit(new MyCallable());
System.err.println("call_future:" + call_futter.get());
}
}
class MyCallable implements Callable<Integer>{
@Override
public Integer call()throws Exception{
return 1;
}
}
class MyRunnable implements Runnable{
@Override
public void run() {
return;
}
}
/*
run_future:null
call_future:1
*/
ThreadPoolExecutor
public class threadPool {
public static void main(String[] arg) throws Exception {
test();
}
private static void test() throws Exception{
ThreadPoolExecutor threadPool = new ThreadPoolExecutor(
5, //核心线程数,超时不会被清空
10, // 最大线程数10
5, // 保持的时间,超时 没执行的任务被清空
TimeUnit.SECONDS,
new LinkedBlockingQueue<Runnable>(5), // 等待队列容量为5
// 最多容纳15个任务,查出的会被拒绝执行
new RejectedExecutionHandler() {
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
System.err.println("有任务被拒绝执行了");
}
}
);
testCommon(threadPool);
}
public static void testCommon(ThreadPoolExecutor threadPool) throws Exception{
for(int i=0;i<30; i++) {
int n = i;
threadPool.submit(new Runnable() {
@Override
public void run () {
try {
System.out.println("任务"+ n +"开始执行");
Thread.sleep(3000L);
System.out.println("任务"+ n +"执行完成");
}catch(Exception e) {
e.printStackTrace();
}
}
});
System.out.println("任务" + i + "提交");
}
while(true) {
Thread.sleep(1000);
System.out.println("》》》线程数量"+threadPool.getPoolSize());
System.out.println(">>>任务队列" + threadPool.getQueue().size());
}
}
}
/*
任务0提交
任务0开始执行
任务1提交
任务2提交
任务1开始执行
任务3提交
任务2开始执行
任务4提交
任务5提交
任务6提交
任务7提交
任务3开始执行
任务4开始执行
任务8提交
任务9提交
任务10提交
任务11提交
任务12提交
任务10开始执行
任务13提交
任务11开始执行
任务12开始执行
任务14提交
有任务被拒绝执行了
任务15提交
任务13开始执行
任务16提交
有任务被拒绝执行了
有任务被拒绝执行了
有任务被拒绝执行了
有任务被拒绝执行了
有任务被拒绝执行了
任务17提交
任务14开始执行
任务18提交
任务19提交
任务20提交
任务21提交
有任务被拒绝执行了
有任务被拒绝执行了
任务22提交
任务23提交
任务24提交
任务25提交
任务26提交
有任务被拒绝执行了
有任务被拒绝执行了
有任务被拒绝执行了
有任务被拒绝执行了
有任务被拒绝执行了
任务27提交
有任务被拒绝执行了
任务28提交
有任务被拒绝执行了
任务29提交
》》》线程数量10
>>>任务队列5
》》》线程数量10
>>>任务队列5
任务1执行完成
任务0执行完成
任务2执行完成
任务7开始执行
任务3执行完成
任务5开始执行
任务6开始执行
任务8开始执行
任务13执行完成
任务4执行完成
任务14执行完成
任务9开始执行
任务12执行完成
任务10执行完成
任务11执行完成
》》》线程数量10
>>>任务队列0
》》》线程数量10
>>>任务队列0
》》》线程数量10
>>>任务队列0
任务7执行完成
任务5执行完成
任务8执行完成
任务6执行完成
任务9执行完成
》》》线程数量10
>>>任务队列0
*/
SynchronousQueue
SynchronousQueue不是一个真正的队列,因为它不会为队列中元素维护存储空间。 它维护一组线程,这些线程在等待着把元素加入或移出队列。put() 往queue放进去一个element以后就一直wait直到有其他thread进来把这个element取走。take() 取出并且remove掉queue里的element(认为是在queue里的。。。),取不到东西他会一直等。
public static void test3() throws InterruptedException{
SynchronousQueue<String> queue = new SynchronousQueue<String>();
new Thread(new Runnable() {
@Override
public void run () {
try {
System.out.println("加入之前");
queue.put("新数据1");
System.out.println("加入之后");
}catch(InterruptedException e) {
e.printStackTrace();
}
}
}).start();
Thread.currentThread().sleep(100L);
new Thread(new Runnable() {
@Override
public void run() {
try{
Thread.sleep(3000);
System.out.println("取出数据之前");
String takedValue = queue.take();
System.out.println("取出来的数据: "+takedValue);
System.out.println("取出数据之后");
}catch(InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
/*
加入之前
取出数据之前
取出来的数据: 新数据1
取出数据之后
加入之后
*/
Executors工具类
newFixedThreadPool(int nThreads)创建一个固定大小,任务队列容量无界的线程池,核心线程数=最大线程数
newCachedThreadPool 创建的是一个大小无界的缓冲线程池,他的任务队列是一个同步队列,任务加入到池中,如果池中有空闲线程,则用空闲线程执行,如果无 则创建新线程执行,池中的线程空袭七年超过60s将被销毁释放,线程数随任务的多少而变化,适用于执行耗时较小的异步任务,池的核心线程数为0,最大线程数=Integet.MAX_VALUE
newSingleThreadExecutor() 只有一个线程来执行无边界任务队列的单一线程池,该线程池确保任务按加入的顺序一个一个的依次执行,当唯一的线程因任务异常终止时,将创建一个新的线程来继续执行后续的任务,与newFixedThreadPool的区别在于,单一线程池的池大小在newSingleThreadExecutor方法中编码,不能在改变的
newScheduledThreadPool(int corePoolSize) 能定时执行任务的的线程池,该池的核心线程数由参数指定,最大线程为Integer.MAX_VALUE
如果确定合适数量的线程
计算型任务:cpu数量的1-2倍
IO型任务:相对比计算型任务,需多一些线程,要根据具体的IO阻塞时长进行考量决定,也可以考虑根据需要在一个最小数量和最大数量间自动增减线程数
如tomcat中默认的最大线程数为200
线程消息通知
suspend和resume,
suspend挂起目标线程,通过resume可以恢复线程执行
已被弃用,主要是容易写出锁死的代码
正常使用:
public class Demo {
public static String ice = null;
public static void main(String[] arg) throws Exception {
Demo demo = new Demo();
demo.test1();
}
public void test1() throws Exception{
Thread thread = new Thread(()->{
if(ice==null) {
System.out.println("没有数据:"+ice);
Thread.currentThread().suspend(); // 挂起线程
}
System.out.println("有数据:"+ice);
});
thread.start();
Thread.sleep(1000);
ice = "1";
thread.resume(); // 通知有数据了
System.out.println("发送通知");
}
}
/*
没有数据:null
发送通知
有数据:1
*/
异常使用:
suspend和resume会出现一种问题,当线程自己拿了锁,并挂起来了,其他线程再去拿锁的话去通知前一个线程会出现抢不到锁的情况
public class Demo {
public static String ice = null;
public static void main(String[] arg) throws Exception {
Demo demo = new Demo();
demo.test1();
}
public void test1() throws Exception{
Thread thread = new Thread(new Runnable(){
@Override
public void run() {
if(ice==null) {
System.out.println("没有数据:"+ice);
synchronized(Demo.class) {
Thread.currentThread().suspend();
}
}
System.out.println("有数据:"+ice);
}
});
thread.start();
Thread.sleep(1000L);
ice = "1";
synchronized(Demo.class) {
System.out.println("发送通知");
thread.resume(); // 通知有数据了
}
}
}
/*
没有数据:null
*/
还有一种情况是线程里面在6s中的时候执行suspend,但是在3s的时候其他线程就已经调用resume通知了的话,挂起的线程也是接收不到消息的
wait/notify机制
wait 方法导致当前线程等待,加入该对象的等待集合中,并且放弃当前持有的对象锁,
notify/notifyAll方法唤醒一个或所有正在等待这个对象锁的线程
注意1:虽然会wait自动解锁,但是对顺序有要求,如果notify被调用之后,才开始wait方法的调用,线程会永远处于waiting状态
注意2:这些方法只能由同一对象锁的持有者线程调用,也就是卸载同步快里面,否则会抛出lllegalMStateException异常
wait/notify只能在synchronized关键字中使用
public void test2() {
try {
wait();
}catch(InterruptedException e) {
e.printStackTrace();
}
}
/*
Exception in thread "main" java.lang.IllegalMonitorStateException
at java.lang.Object.wait(Native Method)
at java.lang.Object.wait(Unknown Source)
at com.Hello.ScannerTest.Demo.test2(Demo.java:11)
at com.Hello.ScannerTest.Demo.main(Demo.java:7)
*/
public void test2() {
try {
synchronized(this) {
wait();
}
}catch(InterruptedException e) {
e.printStackTrace();
}
}
// 无异常
正常用法
public void test3() throws Exception {
new Thread(()->{
if(ice==null) {
synchronized(this){
try {
System.out.println("没用数据:"+ice);
this.wait();
System.out.println("拿到数据:"+ice);
}catch(InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
Thread.sleep(1000L);
ice = "1";
synchronized(this) {
System.out.println("拿到锁,通知");
this.notifyAll();
}
}
/*
没用数据:null
拿到锁,通知
拿到数据:1
*/
注意:此处使用的是lambda表达式,里面的this会指向父级,所以这里两个synchronized里面的this都是在同一个作用域下的,所以这里的this会通知到,没问题,也就是上面所说的需要注意的第二点,方法只能在同一个线程线程锁的对象里面调用
错误示范:
public void test3() throws Exception {
new Thread(new Runnable(){
@Override
public void run () {
if(ice==null) {
synchronized(this){
try {
System.out.println("没用数据:"+ice);
this.wait();
System.out.println("拿到数据:"+ice);
}catch(InterruptedException e) {
e.printStackTrace();
}
}
}
}
}).start();
Thread.sleep(1000L);
ice = "1";
synchronized(this) {
System.out.println("拿到锁,通知");
this.notifyAll();
}
}
/*
没用数据:null
拿到锁,通知
*/
需要改成这样
public class Demo {
public static String ice = null;
public static void main(String[] arg) throws Exception {
Demo demo = new Demo();
demo.test3();
}
public void test3() throws Exception {
new Thread(new Runnable(){
@Override
public void run () {
if(ice==null) {
synchronized(Demo.class){
try {
System.out.println("没用数据:"+ice);
Demo.class.wait();
System.out.println("拿到数据:"+ice);
}catch(InterruptedException e) {
e.printStackTrace();
}
}
}
}
}).start();
Thread.sleep(1000L);
ice = "1";
synchronized(Demo.class) {
System.out.println("拿到锁,通知");
Demo.class.notifyAll();
}
}
}
/*
没用数据:null
拿到锁,通知
拿到数据:1
*/
park/ unpark机制
线程调用park则等待“许可”, unpark为线程提供许可
调用unpark之后再调用park线程会直接运行
提前调用的unpark不叠加,连续多次调用unpark之后,第一次调用park后会拿到“许可”,直接运行,后续调用会进入等待
package com.Hello.ScannerTest;
import java.util.concurrent.locks.LockSupport;
public class ParkUnparkDemo {
public static void main(String args[]) throws Exception {
ParkUnparkDemo demo = new ParkUnparkDemo();
demo.test1();
}
public static Object ice = null;
public void test1() throws Exception{
Thread consume = new Thread(() -> {
if(ice == null) {
System.out.println("准备挂起");
LockSupport.park();
}
System.out.println("完成---");
});
consume.start();
Thread.sleep(3000);
ice = new Object();
LockSupport.unpark(consume);
System.out.println("解锁--");
}
}
/*
准备挂起
解锁--
完成---
*/
注意: 上面的代码用的if语句来判断,时候进入等待状态,这样的做法有问题,官方建议应该在循环中检查等待条件, 因为处于等待状态的线程可能会收到错误警报和唤醒,如果不在循环中检测等待条件,程序会在没有满足条件下推出,
为唤醒: 是指线程并非因为notify, notifyall, unpark等api调用而意外唤醒,是更底层原因导致的
如上面代码
Thread consume = new Thread(() -> {
if(ice == null) {
System.out.println("准备挂起");
LockSupport.park();
}
System.out.println("完成---");
});
/*
如果ice不满足条件, Locksupport.park()突然被唤醒,那么就会执行下面的 “完成---”,
*/
while(ice == null) {
System.out.println("准备挂起");
LockSupport.park();
}
当if改成while后,即使被唤醒了,还是不满足条件,程序继续被挂起,等待着
网友评论