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)改动线程的名字,一旦线程启动,名字将不可再被修改。
线程的父子关系
- 一个线程的创建肯定是由另外一个线程完成的。
- 被创建的线程的父线程是创建它的线程。
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();
}
}