Java并发之线程状态及创建(1)

2019-11-20  本文已影响0人  小马蛋

一、状态解读

Java线程状态迁移.png

1、New 初始状态

创建线程后,未运行

2、Runnable 可运行状态

可能在运行也可能在等待CPU时间片
同时也包含了操作系统线程的Ready 和 Running

着重说一下Thread.yield()
Thread.yield()方法作用是:暂停当前正在执行的线程对象(及放弃当前拥有的cup资源),并执行其他线程。

yield()做的是让当前运行线程回到可运行状态,以允许具有相同优先级的其他线程获得运行机会。因此,使用yield()的
目的是让相同优先级的线程之间能适当的轮转执行。但是,实际中无法保证yield()达到让步目的,因为让步的线程还有可能被
线程调度程序再次选中。

结论:yield()从未导致线程转到等待/睡眠/阻塞状态。在大多数情况下,yield()将导致线程从运行状态转到可运行状态,但有可能没有效果。

3、Blocked 阻塞

等待获取一个Synchronized锁,如果抢占到锁就会离开此状态。

4、Waiting 等待

等待其它线程显示的唤醒,否则一直等待下去。

进入 退出
没有设置Time out参数的Object.wait() Object.notify() 或 Object.notifyAll()
没有设置Time out参数的Thread.join() 被调用的线程执行完毕
LockSupport.park() LockSupport.unpark(Thread thread)

5、Timed Waiting 超时等待

进入 退出
Object.wait(Long) Object.notify() 或 Object.notifyAll()
Thread.join(Long) 超时退出 或 被调用的线程执行完毕
Thread.sleep(Long) 超时退出
LockSupport.parkNanos(Long) LockSupport.unpark()
LockSupport.parkUntil(Long) LockSupport.unpark()

6、Terminated 终止状态

当线程的run()方法执行完毕或者主线程的main()方法完毕,就终止了,也许在这一刻,该线程还是存活的,但是它就要被终止了。

在一个终止的线程上调用start()方法会抛出异常java.lang.IllegalThreadStateException,线程一旦被终止,就无法再次运行。

二、创建

Java线程的创建分为四种方式:

1)继承Tread类

    public class MyThread extends Thread {
        public void run() {
            System.out.println("我是继承于Thread的线程类");
        }
    }
    
    ==================分割线====================

    @Slf4j
    public class JunitTest {
        @Test
        public void test() throws InterruptedException {
            MyThread thread = new MyThread();

            thread.start();
    
            Thread.sleep(3000);
        }
    }

继承Thread类,重写其run()方法,然后通过start()方法调用即可,如果直接调用run()方法,那么就相当于调用同步方法,只有调用start()才是启动线程的正解。

2)实现Runnable接口

    public class MyThread implements Runnable {
        @Override
        public void run() {
            System.out.println("我是实现Runnable接口的线程类");
        }
    }

    ==================分割线====================

    @Slf4j
    public class JunitTest {
        @Test
        public void test() throws InterruptedException {
            MyThread myThread = new MyThread();
            
            // 传入MyThread实例
            Thread thread = new Thread(myThread);
    
            thread.start();
            
            Thread.sleep(3000);
        }
    }

实现Runnable接口,重写其run()方法。
限于第一种方式,受继承的限制,第二种比第一种方式能好一些

3)实现Callable接口

    public class MyThread implements Callable<String> {
    
        @Override
        public String call() throws Exception {
            System.out.println("我是实现Callable接口的线程类");
            return "ok";
        }
    }

    ==================分割线====================

    @Test
    public void test() throws Exception {
        Callable<String> callable = new MyThread();
    
        FutureTask<String> futureTask = new FutureTask<>(callable);
    
        Thread thread = new Thread(futureTask);
    
        thread.start();
    
        Thread.sleep(3000);
    }

覆写call()方法,且支持返回值!

4)通过线程池

可以通过Executors来创建几种不同类型的线程池。Executors是一个线程池工厂,提供了创建线程池的很多方法。

1)SingleThreadPoolExecutor

只有一个线程的线程池。多个任务被提交到该线程池,先被缓存到队列(队列长度为Integer.MAX_VALUE),按照FIFO先进先出的原则进行任务处理。

2)FixedThreadPool

固定大小的线程池,内部存储的线程数量是固定的,和上面的处理流程极为类似,但是该线程池可以处理多个任务,多个任务被提交到线程池,按照以下过程进行处理:

1)如果线程的数量未达到设置的数量,线程池会创建线程来处理任务。

2)如果线程池里的线程数量达到了设置的数量,则取出空闲的线程执行任务

3)如果没有空闲线程,则将任务缓存到队列(队列长度为Integer.MAX_VALUE),当有空闲线程时,按照FIFO的原则进行任务处理

严格按照 1 -> 2 -> 3的过程进行处理。

3)CachedThreadPool

创建带缓存的线程池,这种线程池可以回收空闲线程,当任务到达后,直接使用空闲线程,没有空闲线程时,新建线程。线程池核心长度为0,最大长度为Integer.MAX_VALUE
用户可以传入线程池的核心线程数(最小线程数),最大线程数量,保持时间,时间单位,阻塞队列这些参数,最大线程数设置为jvm可用的cpu数为宜。

4)ScheduledThreadPool

一个可定期或者延时执行任务的线程池

5) WorkStealingPool

创建拥有足够线程数的线程池来维持相应的并行级别,通过使用队列来减少竞争,默认为CPU的数量。它会通过工作窃取的方式,使得多核CPU不会闲置,总会有活着的线程让CPU去运行。

所谓工作窃取,指的是闲置的线程去处理本不属于它的任务。

在实际项目里,我们很少成规模的手动创建线程,一般都是使用线程池(池技术)将线程的创建、销毁、保留及其它管理工作交给线程池,用户无需知道其内部细节,即可使用。

上一篇下一篇

猜你喜欢

热点阅读