阅读 1435

JAVA并发不得不知的线程-Thread

随着计算机单核到多核的发展,程序追求的并行处理,实时性需求变得更好的实现。线程能充分利用当代多核处理器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;
    }复制代码

我们通过流程图了解线程状态的转化image.png

线程状态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重新调度线程。可以让优先级比当前线程高的线程获得执行的机会。
文章分类
后端
文章标签