一、一个线程的创建肯定是由另一个线程完成的。
二、被创建线程的父线程是创建它的线程。
由源代码。详情看init方法。
三、main线程所在的ThreadGroup称为main。
四、构造一个线程的时候如果没有显式地指定ThreadGroup,那么它将和父线程同属一个ThreadGroup。
在默认设置中,子线程和父线程拥有同样的优先级。
五、Thread和Runnable之间的关系:Thread负责线程本身相关的职责和控制,而Runnable则负责逻辑执行单元的部分(run方法)。
六、Thread与StackSize的关系
stacksize越大则代表着正在线程内方法调用递归的深度就越深,stacksize越小则代表着创建的线程数数量越多。该参数一般情况下不会主动设置,采用默认的值就可以了,默认值是0。
七、线程的创建数量是随着虚拟机栈内存的增多而减少的,也就是一种反比关系。
一个进程内存大小(有最大内存限制)=堆内存+线程数量*栈内存
栈内存与线程数量也是反比关系。
地址空间:在操作系统中,一个进程的最大内存。
ReservedOsMemory:系统保留内存,一般在136MB左右。
ThreadStackSize:虚拟机栈内存(栈内存)
线程数量=(最大地址空间-JVM堆内存-ReservedOsMemory)/ThreadStackSize(XSS)。
八、守护线程
设置守护线程:线程.setDaemon(true) 为false时代表正常线程。
若父线程为守护线程,则子线程也为守护线程。反之亦然。
isDaemon方法判断该线程是否是守护线程。
九、yield方法
yield方法:提醒调度器,愿意放弃当前的CPU资源,如果CPU的资源不紧张,则会忽略这种提醒。调用yield方法会使当前线程从RUNNING状态切换到RUNNABLE状态,一般这个方法不太常用。
十、线程的优先级不能小于1,也不能大于10,如果指定线程优先级大于线程所在group的优先级,那么指定的优先级将会失效,取而代之的是group的最大优先级。线程默认的优先级和它的父类保持一致,一般情况下是5。
十一、不管是Thread的run方法,还是Runnable接口,都是void返回类型,如果想通过某个线程的运行得到结果,就需要自己定义一个返回的接口。
十二、如何关闭一个线程
1:正常关闭
1、线程结束生命周期正常结束。
2、捕获中断信号关闭线程
通过检查线程interrupt的标志来决定是否退出。如果在线程中执行某个可中断方法,则可以通过捕获中断信号来决定是 否退出。
3、使用volatile开关控制
因为线程的interrupt标志很有可能被擦除,或者逻辑单元中不会调用任何可中断方法,所有使用volatile修饰的开关flag关闭线程是一种常用的做法。
2:异常退出
在线程的执行单元(run)中,是不允许抛出checked异常的。可以将checked异常封装成unchecked异常(RuntimeException)抛出进而结束线程的生命周期。
3:进程假死
十三、每个对象都与一个monitor相关联,一个monitor的lock的锁只能被一个线程在同一时间获得,在一个线程尝试获得与对象关联monitor的所有权时会发生如下的几件事情。
1、如果monitor的计数器为0,则意味着改monitor的lock还没有被获得,某个线程获得之后将立即对该计数器加一,从此该线程就是这个monitor的所有者了。
2、如果一个已经拥有该monitor所有权的线程重入,则会导致monitor计数器再次累加。
3、如果monitor已经被其他线程所拥有,则其他线程尝试获取该monitor的所有权时,会被陷入阻塞状态直到monitor计数器变为0,才能尝试获取对monitor的所有权。
monitorexit:
释放对monitor的所有权,想要释放对某个对象关联的monitor的所有权的前提是,你曾经获得了所有权。释放monitor所有权,就是将monitor的计数器减一,如果计数器的结果为0,那就意味着该线程不再拥有对该monitor的所有权,通俗讲就是解锁。与此同时被该monitor block的线程将再次尝试获得对该monitor的所有权。
十四、使用synchronized 注意事项:
1、与monitor关联的对象不能为空。
2、synchronized 作用域太大
上面的代码对整个线程的执行逻辑单元都进行了synchronized同步,从而丧失了并发的能力,synchronized关键字应该尽可能地只作用于共享资源(数据)的读写作用域。
3、不同的monitor企图锁相同的方法
上面的代码构造了五个线程,同时也构造了五个Runnable实例,Runnable作为线程逻辑执行单元传递给Thread,然后你将发现。synchronized根本互斥不了与之对应的作用域,线程之间进行monitor lock的争抢只能发生在于monitor关联的同一个引用上,上面的代码每个线程争抢的monitor关联引用都是彼此独立的(每个线程new一个),因此不可能起到互斥的作用。
(解决办法:声明为static)
4、多个锁的交叉导致死锁
十五、 this monitor class monitor
1、使用synchronized关键字同步类的不同实例方法,争抢的是同一个monitor的lock,而与之关联的引用则是this monitor的实例引用。
2、用synchronized同步某个类的不同静态方法争抢的也是同一个monitor的lock,与该monitor关联的引用是ClassMonitord.class实例。
十六、线程死锁原因
1、交叉锁可导致程序出现死锁。
2、内存不足
3、一问一答式的数据交换
4、数据库锁
5、文件锁
6、死循环引起的死锁(一般称为系统假死)
十七、被唤醒并且被执行的线程是从上次阻塞的位置从下开始运行,也就是从wait()方法后开始执行。所以判断是否进入某一线程的条件 是用while判断,而不是用If判断。
十八、默认情况下,新的线程都会被加入到main线程所在的group中,main线程的group名字同线程名。如同线程存在父子关系一样,ThreadGroup同样也存在父子关系。无论如何,线程都会被加入某个Thread Group之中。
十九、所有线程的优先级都不能大于group的优先级。group的最大优先级不能超过父group的最大优先级。
二十、修改group的优先级:已经加入该group的线程的优先级可以大于group最大优先级。但是,后面加入该group的线程的优先级再不会大于新设置的优先级。
二十一、interrupt一个thread group会导致该group中所有的active线程都被interrupt,也就是说该group中每个线程的interrupt标志都被设置了。
二十二、线程可以设置为守护线程,ThreadGroup也可以设置为守护ThreadGroup,但是若将一个Threadgroup设置为daemon,也并不会影响线程的daemon属性,如果一个ThreadGroup的daemon被设置为true,那么在group中没有active线程的时候该group将自动destroy。
二十三、钩子线程(Hook线程)
JVM进程的退出是由于JVM进程中没有活跃的非守护线程,或者收到了系统中断信号,向JVM程序注入一个Hook线程,在JVM进程退出的时候,Hook线程会启动执行,通过Runtime可以为JVM注入多个Hook线程。
package com.lijiangde.fours;
import java.util.concurrent.TimeUnit;
public class ThreadHook {
public static void main(String[] args) {
// TODO Auto-generated method stub
//为应用程序注入钩子线程
Runtime.getRuntime().addShutdownHook(new Thread(){
public void run()
{
try
{
System.out.println("The hook thread 1 is running");
TimeUnit.SECONDS.sleep(1);
}catch(InterruptedException e)
{
e.printStackTrace();
}
System.out.println("The hook thread 1 will exit");
}
});
//钩子线程可注册多个
Runtime.getRuntime().addShutdownHook(new Thread(){
public void run()
{
try
{
System.out.println("The hook thread 2 is running.");
TimeUnit.SECONDS.sleep(1);
}catch(InterruptedException e)
{
e.printStackTrace();
}
System.out.println("The hook thread 2 will exit.");
}
});
System.out.println("The program will is stopping.");
}
}
二十四、被volatile修饰的实例变量或类变量具备如下两层语义:
1、保证了不同线程之间对共享变量操作时的可见性,也就是说当一个线程修改volatile修饰的变量,另外一个线程会立即看到最新的值。
2、禁止对指令进行重排序操作。
二十五、单例模式 ——Holder方式
public class Singleton {
private byte[] data=new byte[1024];
private Singleton()
{
}
//在静态内部类中持有Singleton的实例,并且可被直接初始化
private static class Holder
{
private static Singleton singleton=new Singleton();
}
//调用getInstance方法,事实上是获得Holder的instance静态属性
public static Singleton getInstance()
{
return Holder.singleton;
}
}