美文网首页js css html
Java并发编程之线程

Java并发编程之线程

作者: 宏势 | 来源:发表于2022-11-01 10:20 被阅读0次

    Java多线程是为了更好利用CPU资源,提升系统吞吐率,在一些适合的场合,用多线程可以避免阻塞。

    一、线程简介

    简单main函数查看线程信息(JDK11)

    public class PrintThread {
        public static void main(String[] args) {
            ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
            ThreadInfo[] threadInfos = threadMXBean.dumpAllThreads(false, false);
            for (ThreadInfo threadInfo : threadInfos) {
                System.out.println("["+threadInfo.getThreadId()+"] " + threadInfo.getThreadName());
                //可以直接输出threadInfo 打印详细信息
            }
        }
    }
    

    输出:

    [1] main                                         #主线程main,程序入口
    [2] Reference Handler                            #清除Reference线程
    [3] Finalizer                                    #调用对象finalize方法的线程
    [4] Signal Dispatcher                            #分发处理发给JVM信号的线程
    [12] Notification Thread.                        #JDK11才有
    [13] Common-Cleaner                              #JDK11才有
    
    

    相关虚拟机接口类MXBean,可查看JDK中java.lang.management包,比如

    线程支持优先级设置setPriority(int), 范围1~10,默认优先级是5,优先级越高分配的时间片越多。有些操作系统会忽略对线程优先级的设定,不推荐使用

    Daemon守护线程是一种支持型线程,负责后台调度或者支持工作。当虚拟机不存在非Daemon进程的时候,虚拟机将会退出。通过setDaemon(true)设置,只能在启动之前设置,否则抛异常。

    线程状态(6种)

    状态名称 说明
    NEW 初始状态,线程被构建,但还没有调用start方法
    RUNNABLE 运行状态,Java线程把操作系统的就绪和运行两种状态笼统地称作"运行中"
    BLOCKED 阻塞状态,表示线程阻塞于锁
    WAITING 等待状态,进入该状态表示当前线程需要等待其它线程通知或者中断
    TIME_WAITING 超时等待状态,不同于WAITING,它可以在指定的时间自行返回
    TERMINATED 终止状态,表示当前线程已经执行完毕

    参考java.lang.Thread 中枚举类State

    线程状态变迁图

    thread.state.png
    • wait/notify/notifyAll Object对象的方法,必须跟synchronized搭配使用;且使用同一个锁对象;wait会释放锁,notify/notifyAll不会释放锁,等待线程真正唤醒要等到唤醒线程释放锁,并获取到锁,才真正唤醒;
    • join 等待线程执行结束,Thread对象的方法,内部实现原理也是wait方式
    public final void join() throws InterruptedException {
            join(0);
    }
    public final synchronized void join(long millis)
        throws InterruptedException {
            ...
            if (millis == 0) {
                while (isAlive()) {
                    wait(0);
                }
            } else {
               ...
            }
       }
    
    • LockSuppport.park()/unpark(Thread) 支持唤醒指定线程
    public class ParkThread extends Thread{
        @Override
        public void run() {
            System.out.println("doing....");
            LockSupport.park(); //进入waiting
            System.out.println("unpack continue doing...");
        }
    
        public static void main(String[] args) throws InterruptedException {
            ParkThread parkThread = new ParkThread();
            parkThread.start();
            Thread.sleep(5000); //休眠5s
            LockSupport.unpark(parkThread); //唤醒
        }
    }
    
    • Thread.sleep(long) 休眠不释放锁, 时间到了自动唤醒

    二、线程启动/停止/中断

    • 线程启动支持两种方式:(1)继承Thread (2)实现Runnable接口
    class MyThread extends Thread{
         public void run(){
                ...
         }
    }
    MyThread p = new MyThread();
    p.start();
    
    class MyJob implements Runable{
         public void run(){
             ...
         }
    }
    new Thread(new MyJob()).start();
    
    • 优雅停止线程方式:自定义标志位和中断标志位
    public class StopThread extends Thread{
        private volatile boolean exit = false;
        private long count = 0;
        @Override
        public void run() {
            while (!exit && !Thread.currentThread().isInterrupted()){
                count ++;
            }
            System.out.println(Thread.currentThread().getName()+ " count:" + count);
        }
        public void cancel(){
            this.exit = true;
        }
    
        public static void main(String[] args) throws InterruptedException {
           StopThread stopThread1 = new StopThread();
           stopThread1.setName("FlagThread");
           stopThread1.start();
    
           StopThread stopThread2 = new StopThread();
           stopThread2.setName("InterruptedThread");
           stopThread2.start();
    
           Thread.sleep(20);
           stopThread1.cancel(); //自定义标志位
           Thread.sleep(10);
           stopThread2.interrupt();//中断标志位
        }
    }
    

    中断其实是一个标志位,当线程执行interrupt()方法,如果线程处于Runnable状态时,isInterrupted() 返回true,如果线程处于阻塞阶段,线程会抛InterruptedException,并重置中断标志位,所以isInterrupted() 返回false

    线程stop()方法可以停止线程,但不推荐使用,它是一个被废弃的方法,无法保证资源的完全释放。

    线程暂停suspend()和恢复resume() 方法也是被废弃的方法,suspend() 不释放锁,容器导致死锁。

    三、线程通讯

    线程间通讯主要有共享变量消息传递

    1.volatile

    public class VolatileTest{
        
        static volatile boolean flag = false; //定义共享变量,volatile修饰线程及时感知
    
        public static void main(String[] args) throws InterruptedException{
            List<String> list = new ArrayList<>();
            
            Thread threadA = new Thread(()->{
                    for(int i = 1; i <= 10; i++){
                        list.add("hello");
                        System.out.println("线程A添加元素,size="+list.size());
                        //Thread.sleep(1000);
                        if(list.size()==5){ //线程A添加到第五个元素通知B线程
                                flag = true;
                        }
                    }
            });
            Thread threadB = new Thread(()->{
                    while(true){
                        if(flag){
                            System.out.println("线程B收到通知执行自己业务");
                            break;
                        }
                    }
            });
            threadB.start();
            Thread.sleep(1000); //确保先启动threadB
            threadA.start();
        }
    }
    

    2.wait()/notify()

    public class WaitNotifyTest{
        
        public static void main(String[] args) throws InterruptedException{
            Object lock = new Object(); 
            List<String> list = new ArrayList<>();
            
            Thread notifyThread = new Thread(()->{
                    synchronized(lock){
                        for(int i = 1; i <= 10; i++){
                list.add("hello");
                System.out.println("NotityThread添加元素,size="+list.size());
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                if(list.size()==5){ //添加到第五个元素通知WaitThread
                    lock.notify();
                }
              }
                    }
            });
            Thread waitThread = new Thread(()->{
                    synchronized(lock){
                if(list.size()!=5){
                     try {
                        lock.wait();
                 } catch (InterruptedException e) {
                        e.printStackTrace();
                 }
                }
                        System.out.println("WaitThread收到通知执行自己业务");
            }
            });
            waitThread.start();
            Thread.sleep(1000); //确保先启动WaitThread
            notifyThread.start();
            
        }
    }
    

    NotityThread线程虽然调用notify()唤醒,依然是要走完自己业务,WaitThread线程才开始真正执行;因为notify()并没有释放锁,NotifyThread线程运行结束释放锁,WaitThread线程获取锁才真正唤醒进入Runnable状态

    3. ReentrantLock + Condition

    public class NotifyThread{
    
        public static void main(String[] args) throws InterruptedException{
            ReentrantLock lock = new ReentrantLock();
            Condition condition = lock.newCondition();
            List<String> list = new ArrayList<>();
    
            Thread notifyThread = new Thread(()->{
                lock.lock();
                try{
                    for(int i = 1; i <= 10; i++){
                        list.add("hello");
                        System.out.println("NotityThread添加元素,size="+list.size());
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        if(list.size()==5){ //添加到第五个元素通知WaitThread
                            condition.signal();
                        }
                    }
                }finally {
                    lock.unlock();
                }
            });
            Thread waitThread = new Thread(()->{
                lock.lock();
                try{
                    if(list.size()!=5){
                        try {
                            condition.await();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    System.out.println("WaitThread收到通知执行自己业务");
                }finally {
                    lock.unlock();
                }
            });
            waitThread.start();
            Thread.sleep(1000); //确保先启动WaitThread
            notifyThread.start();
    
        }
    }
    

    这种方法跟Object的wait()/notify() 一样,WaitThread线程要等到NotifyThread线程释放锁,才能真正唤醒。

    queue.png

    相关文章

      网友评论

        本文标题:Java并发编程之线程

        本文链接:https://www.haomeiwen.com/subject/iahozrtx.html