Java并发编程之线程

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 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 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 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并发编程之线程)