Java多线程

Java多线程只有一种构造方式,即创建Thread对象。
实现线程的执行方法有两种方式:
1. 继承Thread,重写Thread的run()方法。
2. 实现Runnable 接口的run()方法。

线程命名

默认命名

Java源码

public Thread(Runnable target) {
    init(null, target, "Thread-" + nextThreadNum(), 0);
}
private static synchronized int nextThreadNum() {
    return threadInitNumber++;
}

如果没有为线程指定一个名字,线程将会以”Thread-“作为前缀与一个自增的数字结合。
在线程start之前,你可以通过Thread.setName(String name)改动线程的名字,一旦线程启动,名字将不可再被修改。

线程的父子关系

  1. 一个线程的创建肯定是由另外一个线程完成的。
  2. 被创建的线程的父线程是创建它的线程。

Threadh和ThreadGroup

构造一个线程时,如果没有显式地指定ThreadGroup,那么它将会和父线程同属于一个ThreadGroup。

Java 1.8内存改动

Java1.8相对于1.7把GC扩展到了方法区。方法区之前也称为持久代,但是Java8之后,为了GC而把持久代改动为元空间。元空间不属于jvm内存的一部分,它直接存在于本机内存中,而常量池移到堆中。

Java 能创建的线程数目和内存堆栈的关系

Java内存大小=堆大小 + 线程数目*栈大小。所以线程数目和内存堆栈为反比关系。虚拟栈内存的线程是私有的,也就是说每一个线程都会占有指定大小的内存。
比如32位的Windows最大内存是2G。

使用TimeUnit替代Thread.sleep

在JDK1.5之后引入了TimeUnit,并且对sleep进行了很好的封装。

TimeUnit.SECONDS.sleep(17);
TimeUnit.HOURS.sleep(17);
TimeUnit.DAYS.sleep(17);
TimeUnit.MILLISECONDS.sleep(88);

Thread.yield

Thread.yield()方法属于一种启发式的方法,其会提醒调度器我愿意放弃当前的CPU资源,如果CPU资源不紧张,则会忽略这种提醒。

设置线程的优先级

public final void setPriority(int newPriority)//为线程设定优先级
public final int getPriority()//获取线程的优先级

设置线程的优先级只是一个hint操作,具体如下:
1. 对与root用户,它会hint操作系统你想要设置的优先级别,否则它会被忽略。
2. 如果CPU比较忙,设置优先级可能会获得更多的CPU时间片,但是闲暇时间优先级的高低几乎不会有如何作用。
不要企图在程序设计中企图使用线程优先级绑定某些特定的业务,或者让业务严重依赖于线程优先级。

public final static int MIN_PRIORITY = 1;
public final static int MAX_PRIORITY = 10;
public final void setPriority(int newPriority) {
    ThreadGroup g;
    checkAccess();
    if (newPriority > MAX_PRIORITY || newPriority < MIN_PRIORITY) {
        throw new IllegalArgumentException();
    }
    if((g = getThreadGroup()) != null) {
        if (newPriority > g.getMaxPriority()) {
            newPriority = g.getMaxPriority();
        }
        setPriority0(priority = newPriority);
    }
}

如果指定优先级大于group的优先级,那么group的优先级会替换掉指定的优先级。
线程默认的优先级和父类的保持一致,一般情况下都是5。

获取线程的ID

public long getId() 获取线程的唯一ID,线程ID在整个JVM中都是唯一的。
获取当前线程Thread.currentThread()

线程interrupt(中断)

interrupt

public void interrupt()
public static boolean interrupted()
public boolean isInterrupted()
很多办法可以使当前的线程进入阻塞状态,比如:Object.wait(),Object.wait(long), Object.wait(long, int),Thread.sleep(long),Thread.sleep(long,int),Thread.join(), Thread.join(long), Thread.join(long, int), InterruptibleChannel的IO操作,Selector的wakeup方法, etc;,而调用当前线程的interrupt方法,就可以打断阻塞。虽然打断阻塞,都是并不意味着该线程的生命周期结束。
一旦一个线程在阻塞时被打断,则会跑出一个InterruptException的异常。

isInterrupted与Interrupted

Thread.isInterrupted()判断当前线程是否可以打断。
注意:如果InterruptedException被捕获,interrupt标识会被重置为false。
Thread.interrupted(),调用这个方法返回interrupt标识之后会直接檫除掉线程的interrupt(默认false)。

线程join方法详解

public final void join() throws InterruptedException
public final synchronized void join(long millis, int nanos) throws InterruptedException
public final synchronized void join(long millis) throws InterruptedException
join某个线程A,会使当前线程B进入等待状态(BLOCKED),知道线程A生命周期结束或者到达给定的时间。

public static void main(String[] args) {
        List<Thread> threads = IntStream.range(1,3).mapToObj(ThreadJoin::create).collect(Collectors.toList());

        threads.forEach(Thread::start);

        for (Thread thread : threads) {
            try {
                thread.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        for (int i = 0; i < 10; i++) {
            System.out.println(Thread.currentThread().getName()+"#"+i);
            shortSleep();
        }
    }

    private static Thread create(int req) {
        return new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                System.out.println(Thread.currentThread().getName()+"#"+i);
                shortSleep();
            }
        }, String.valueOf(req));
    }

    private static void shortSleep() {
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

关闭线程

正常关闭

线程结束生命周期正常结束

捕获中断信号关闭线程

使用volatile开关控制

异常退出

进程假死


本文转载:CSDN博客