多线程- 让程序更高效的运行

Java Thread 的一些认识:

  • Java是抢占式线程,一个线程就是进程中单一的顺序控制流,单个进程可以拥有多个并发任务,其底层是切分CPU时间,多线程和多任务往往是使用多处理器系统的最合理方式
  • 进程可以看作一个程序或者一个应用;线程是进程中执行的一个任务,多个线程可以共享资源
  • 一个Java 应用从main 方法开始运行,main 运行在一个线程内,也被称为 “主线程”,Runnable也可以理解为Task (任务)
  • JVM启动后,会创建一些守护线程来进行自身的常规管理(垃圾回收,终结处理),以及一个运行main函数的主线程
  • 随着硬件水平的提高,多线程能使系统的运行效率得到大幅度的提高,同时异步操作也增加复杂度和各种并发问题

 ■ 线程 VS 进程

  • 在一个已有进程中创建一个新线程比创建一个新进程快的多
  • 终止一个线程比终止一个进程快的多
  • 同一个进程内线程间切换比进程间切换更快
  • 线程提供了不同的执行程序间通信的效率,同一个进程中的线程共享同一进程内存和文件,无序调用内核就可以互相通信,而进程间通信必须通过内核

 ■ 同步和异步

  • 同步方法一旦开始,调用者必须等到方法调用返回之后,才能继续后续行为
  • 无先后顺序,一旦开始,方法调用便立即返回,调用者就可以继续后续行为,一般为另一个线程执行

 ■ 阻塞和非阻塞

  • 当一个线程占用临界区资源,其他线程也想要使用该资源就必须等待,等待会导致线程的挂起,也就是阻塞(线程变成阻塞状态)。
    此时若占用资源的线程一直不愿意释放资源,那么其他所有阻塞在该临界区的线程都会被挂起,变成阻塞状态,不能正常工作,直到占用线程释放资源
  • 非阻塞强调没有一个线程可以妨碍其他线程执行,所有线程都会尝试去做下一步工作

 ■ 临界资源与临界区

  • 一般指的是公共共享资源,即可以被多个线程共享使用。但同一时间只能由一个线程去访问和操作临界区的资源,一旦临界区资源被一个线程占用,其他线程也想要使用该资源就必须等待,
    就好比好多人想上大号,但只有一个坑,一个人占了坑,其他人就得排队等待喽
  • 临界区可以认为是一段代码,线程会在该端代码中访问共享资源,因此临界区的界定标准就是是否访问共享(临界)资源(有点类似形成闭包的概念);一次只允许有一个程序(进程/线程)在该临界区中

 ■ 上下文切换

  • CPU通过时间片分配算法来循环执行任务,当前任务执行一个时间片后会切换到下一个任务。但是,在切换前会保存上一个任务的状态,以便下次切换回这个任务时可以重新加载这个任务的状态。所有任务从保存到再加载的过程就是一次上下文切换
  • 多线程性能问题:由于线程有创建和上下文切换的开销,在多线程环境下,这种开销对时间和资源的利用都是一个极大的负担,很可能导致并发任务执行速度还不如串行快
  • 减少上下文切换: 无锁并发编程、CAS算法、减少并发、使用最少线程、协程
      无锁并发编程:避免使用锁,比如数据分段执行(MapReduce)、尽可能使用无状态对象、避免竞争情况等
      CAS算法:java.util.concurrent包中大量使用CAS算法,比如Atomic、AQS等
      减少并发:JAVA8中新引入的LongAdder、DoubleAdder等新类,将CAS算法替换成value分担原则
      使用最少线程:避免创建不必要的线程,当任务很少但线程很多时,会导致大量线程为等待状态
      协程:在单线程里实现多任务的调度,并在单线程里维持多个任务间的切换

  • 补充:需要注意的是,Java的线程是映射到操作系统的原生线程上,因此若要阻塞或唤醒一个线程都需要操作系统的协助,这就意味着要从用户态转换到核心态,因此状态转换是非常耗费处理器时间的

 ■ 竞合条件

  • 竞争条件指多个线程并发访问和操作同一数据且执行结果与线程访问的特定顺序有关
  • 竞争条件发生在当多个线程在读写数据时,其最终的的结果依赖于多个线程指令执行顺序
  • 由于竞争条件的指令顺序操作的不确定性甚至是错误的,可能会造成结果的混乱,例如臭名昭著的i++的原子性问题
  • 值得注意的是即使在单处理器环境下,也可能因为中断的可以在任何地方停止指令的执行的特性,导致类似并发情况下的数据不一致性,解决方案同竞争条件一致:保证指令的执行顺序


■ 类定义

public class Thread implements Runnable {
  /* Make sure registerNatives is the first thing <clinit> does. 
    初始化时调用 Java 本地方法,实现了Runnable接口
  */ private static native void registerNatives(); static { registerNatives(); }

■ 构造器

/**
  * 默认构造器
  * 其中name规则为 "Thread-" + nextThreadNum()
  */
public Thread() {
    init(null, null, "Thread-" + nextThreadNum(), 0);
}
/**
  * 创建一个指定Runnable的线程
  * 其中name规则为 "Thread-" + nextThreadNum()
  */
public Thread(Runnable target) {
    init(null, target, "Thread-" + nextThreadNum(), 0);
}
/**
  * 创建一个指定所属线程组和Runnable的线程
  * 其中name规则为 "Thread-" + nextThreadNum()
  */
public Thread(ThreadGroup group, Runnable target) {
    init(group, target, "Thread-" + nextThreadNum(), 0);
}
/**
  * 创建一个指定name线程
  */
public Thread(String name) {
    init(null, null, name, 0);
}
/**
  * 创建一个指定所属线程组和name的线程
  */
public Thread(ThreadGroup group, String name) {
    init(group, null, name, 0);
}
/**
  * 创建一个指定Runnable和name的线程
  */
public Thread(Runnable target, String name) {
    init(null, target, name, 0);
}
/**
  * Allocates a new {@code Thread} object so that it has {@code target}
  * as its run object, has the specified {@code name} as its name,
  * and belongs to the thread group referred to by {@code group}.
  *     创建一个新的Thread对象,同时满足以下条件:
  *         1.该线程拥有一个指定的Runnable对象用于方法执行
  *         2.该线程具有一个指定的名称
  *         3.该线程属于一个指定的线程组ThreadGroup
  * <p>If there is a security manager, its
  * {@link SecurityManager#checkAccess(ThreadGroup) checkAccess}
  * method is invoked with the ThreadGroup as its argument.
  *     若这里有个安全管理器,则ThreadGroup将调用checkAccess方法进而触发SecurityManager的checkAccess方法
  * <p>In addition, its {@code checkPermission} method is invoked with
  * the {@code RuntimePermission("enableContextClassLoaderOverride")}
  * permission when invoked directly or indirectly by the constructor of a subclass which
  * overrides the {@code getContextClassLoader} or {@code setContextClassLoader} methods.
  *     当enableContextClassLoaderOverride被开启时,checkPermission将被重写子类直接或间接地调用
  * <p>The priority of the newly created thread is set equal to the
  * priority of the thread creating it, that is, the currently running
  * thread. The method {@linkplain #setPriority setPriority} may be
  * used to change the priority to a new value.
  *     新创建的Thread的优先级等同于创建它的线程的优先级,调用setPriority会变更其优先级
  * <p>The newly created thread is initially marked as being a daemon
  * thread if and only if the thread creating it is currently marked
  * as a daemon thread. The method {@linkplain #setDaemon setDaemon}
  * may be used to change whether or not a thread is a daemon.
  *     当且仅当线程创建时被显示地标记为守护线程,新创建的线程才会被初始化为一个守护线程
  *     setDaemon方法可以设置当前线程是否为守护线程
  */
public Thread(ThreadGroup group, Runnable target, String name) {
    init(group, target, name, 0);
}
/**
  * Allocates a new {@code Thread} object so that it has {@code target} as its run object,
  * has the specified {@code name} as its name, and belongs to the thread group referred to
  * by {@code group}, and has the specified <i>stack size</i>.
  *     创建一个新的Thread对象,同时满足以下条件:
  *         1.该线程拥有一个指定的Runnable对象用于方法执行
  *         2.该线程具有一个指定的名称
  *         3.该线程属于一个指定的线程组ThreadGroup
  *         4.该线程拥有一个指定的栈容量
  * <p>This constructor is identical to {@link #Thread(ThreadGroup,Runnable,String)}
  * with the exception of the fact that it allows the thread stack size to be specified. 
  * The stack size is the approximate number of bytes of address space that the virtual machine
  * is to allocate for this thread's stack. <b>The effect of the {@code stackSize} parameter,
  * if any, is highly platform dependent.</b> 
  *     栈容量指的是JVM分配给该线程的栈的地址(内存)空间大小,这个参数的效果高度依赖于JVM运行平台 
  * <p>On some platforms, specifying a higher value for the {@code stackSize} parameter may allow
  * a thread to achieve greater  recursion depth before throwing a {@link StackOverflowError}.
  * Similarly, specifying a lower value may allow a greater number of threads to exist
  * concurrently without throwing an {@link OutOfMemoryError} (or other internal error).
  * The details of the relationship between the value of the <tt>stackSize</tt> parameter
  * and the maximum recursion depth and concurrency level are platform-dependent.
  * <b>On some platforms, the value of the {@code stackSize} parameter
  * may have no effect whatsoever.</b>
  *     在一些平台上,栈容量越高,(会在栈溢出之前)允许线程完成更深的递归(换句话说就是栈空间更深) 
  *     同理,若栈容量越小,(在抛出内存溢出之前)允许同时存在更多的线程数
  *     对于栈容量、最大递归深度和并发水平之间的关系依赖于平台
  * <p>The virtual machine is free to treat the {@code stackSize} parameter as a suggestion. 
  * If the specified value is unreasonably low for the platform,the virtual machine may instead 
  * use some platform-specific minimum value; if the specified value is unreasonably  high, 
  * the virtual machine may instead use some platform-specific maximum. 
  * Likewise, the virtual machine is free to round the specified value up or down as it sees fit
  * (or to ignore it completely).
  *     JVM会将指定的栈容量作为一个参考依据,但当小于平台最小值时会直接使用最小值,最大值同理
  *     同样,JVM会动态调整栈空间的大小以适应程序的运行或者甚至直接就忽视该值的设置
  * <p><i>Due to the platform-dependent nature of the behavior of this constructor, extreme care
  * should be exercised in its use.The thread stack size necessary to perform a given computation 
  * will likely vary from one JRE implementation to another. In light of this variation, 
  * careful tuning of the stack size parameter may be required,and the tuning may need to
  * be repeated for each JRE implementation on which an application is to run.</i>
  *     简单总结一下就是:这个值严重依赖平台,所以要谨慎使用,多做测试验证
  * @param  group
  *         the thread group. If {@code null} and there is a security
  *         manager, the group is determined by {@linkplain
  *         SecurityManager#getThreadGroup SecurityManager.getThreadGroup()}.
  *         If there is not a security manager or {@code
  *         SecurityManager.getThreadGroup()} returns {@code null}, the group
  *         is set to the current thread's thread group.
  *             当线程组为null同时有个安全管理器,该线程组由SecurityManager.getThreadGroup()决定
  *             当没有安全管理器或getThreadGroup为空,该线程组即为创建该线程的线程所属的线程组
  * @param  target
  *         the object whose {@code run} method is invoked when this thread
  *         is started. If {@code null}, this thread's run method is invoked.
  *             若该值为null,将直接调用该线程的run方法(等同于一个空方法)
  * @param  name
  *         the name of the new thread
  * @param  stackSize
  *         the desired stack size for the new thread, or zero to indicate
  *         that this parameter is to be ignored.
  *             当栈容量被设置为0时,JVM就会忽略该值的设置
  * @throws  SecurityException
  *          if the current thread cannot create a thread in the specified thread group
  *             如果当前线程在一个指定的线程组中不能创建一个新的线程时将抛出 安全异常
  * @since 1.4
  */    
public Thread(ThreadGroup group, Runnable target, String name,long stackSize) {
    init(group, target, name, stackSize);
}

■ 重要变量

//线程名,用char来保存(String底层实现就是char)
private char        name[];
//线程优先级
private int         priority;
//不明觉厉
private Thread      threadQ;
//不明觉厉
private long        eetop;
/* Whether or not to single_step this thread. 不明觉厉*/
private boolean     single_step;
/* Whether or not the thread is a daemon thread. 是否是守护线程,默认非守护线程*/
private boolean     daemon = false;
/* JVM state. 是否一出生就领便当,默认false*/
private boolean     stillborn = false;
/* What will be run. Thread的run方法最终会调用target的run方法*/
private Runnable target;
/* The group of this thread. 当前线程的所属线程组*/
private ThreadGroup group;
/* The context ClassLoader for this thread 当前线程的ClassLoader*/
private ClassLoader contextClassLoader;
/* The inherited AccessControlContext of this thread 当前线程继承的AccessControlContext*/
private AccessControlContext inheritedAccessControlContext;
/* For autonumbering anonymous threads. 给匿名线程自动编号,并按编号起名字*/
private static int threadInitNumber;
/* ThreadLocal values pertaining to this thread. This map is maintained by the ThreadLocal class. 
 * 当前线程附属的ThreadLocal,而ThreadLocalMap会被ThreadLocal维护(ThreadLocal会专门分析)
 */
ThreadLocal.ThreadLocalMap threadLocals = null;
/*
 * InheritableThreadLocal values pertaining to this thread. This map is
 * maintained by the InheritableThreadLocal class.
 * 主要作用:为子线程提供从父线程那里继承的值
 * 在创建子线程时,子线程会接收所有可继承的线程局部变量的初始值,以获得父线程所具有的值
 * 创建一个线程时如果保存了所有 InheritableThreadLocal 对象的值,那么这些值也将自动传递给子线程
 * 如果一个子线程调用 InheritableThreadLocal 的 get() ,那么它将与它的父线程看到同一个对象
 */
 ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
/*
 * The requested stack size for this thread, or 0 if the creator did not specify a stack size. 
 * It is up to the VM to do whatever it likes with this number; some VMs will ignore it.
 * 栈容量:当设置为0时,JVM会忽略该值;该值严重依赖于JVM平台,有些VM甚至会直接忽视该值
 * 该值越大,线程栈空间变大,允许的并发线程数就越少;该值越小,线程栈空间变小,允许的并发线程数就越多
 */
private long stackSize;
/* JVM-private state that persists after native thread termination.*/
private long nativeParkEventPointer;
/* Thread ID. 每个线程都有专属ID,但名字可能重复*/
private long tid;
/* For generating thread ID 用于ID生成,每次+1*/
private static long threadSeqNumber;
/* 
 * Java thread status for tools,initialized to indicate thread 'not yet started'
 * 线程状态 0仅表示已创建
 */
private volatile int threadStatus = 0;
/**
  * The argument supplied to the current call to java.util.concurrent.locks.LockSupport.park.
  * Set by (private) java.util.concurrent.locks.LockSupport.setBlocker
  * Accessed using java.util.concurrent.locks.LockSupport.getBlocker
  * 主要是提供给 java.util.concurrent.locks.LockSupport该类使用
  */
volatile Object parkBlocker;
/* The object in which this thread is blocked in an interruptible I/O operation, if any. 
 * The blocker's interrupt method should be invoked after setting this thread's interrupt status.
 * 中断阻塞器:当线程发生IO中断时,需要在线程被设置为中断状态后调用该对象的interrupt方法
 */
private volatile Interruptible blocker;
//阻塞器锁,主要用于处理阻塞情况
private final Object blockerLock = new Object();
 /* The minimum priority that a thread can have. 最小优先级*/
public final static int MIN_PRIORITY = 1;
/* The default priority that is assigned to a thread. 默认优先级*/
public final static int NORM_PRIORITY = 5;
/* For generating thread ID 最大优先级*/
public final static int MAX_PRIORITY = 10;
/* 用于存储堆栈信息 默认是个空的数组*/
private static final StackTraceElement[] EMPTY_STACK_TRACE = new StackTraceElement[0];
private static final RuntimePermission SUBCLASS_IMPLEMENTATION_PERMISSION =
            new RuntimePermission("enableContextClassLoaderOverride");
// null unless explicitly set 线程异常处理器,只对当前线程有效
private volatile UncaughtExceptionHandler uncaughtExceptionHandler;
// null unless explicitly set 默认线程异常处理器,对所有线程有效
private static volatile UncaughtExceptionHandler defaultUncaughtExceptionHandler; 

■ 本地方法

/* 
 * Make sure registerNatives is the first thing <clinit> does. 
 * 确保clinit最先调用该方法:所有该方法是类中的最靠前的一个静态方法
 * clinit:在JVM第一次加载class文件时调用,用于静态变量初始化语句和静态块的执行
 * 所有的类变量初始化语句和类型的静态初始化语句都被Java编译器收集到该方法中
 * 
 * registerNatives方法被native修饰,即是本地方法,将由C/C++去完成,并被编译成了.dll,供JAVA调用
 * 其主要作用是将C/C++中的方法映射到Java中的native方法,实现方法命名的解耦
 */
private static native void registerNatives();
static {
    registerNatives();
}
/** 主动让出CPU资源,当时可能又立即抢到资源 **/
public static native void yield();
/** 休眠一段时间,让出资源但是并不会释放对象锁 **/
public static native void sleep(long millis) throws InterruptedException;
/** 检查 线程是否存活 **/
public final native boolean isAlive();
/** 检查线程是否中断 isInterrupted() 内部使用 **/
private native boolean isInterrupted(boolean ClearInterrupted);
/** 返回当前执行线程 **/
public static native Thread currentThread();
public static native boolean holdsLock(Object obj);
private native void start0();
private native void setPriority0(int newPriority);
private native void stop0(Object o);
private native void suspend0();
private native void resume0();
private native void interrupt0();
private native void setNativeName(String name);

■ 线程初始化

/**
  * Initializes a Thread.
  *     初始化一个线程
  * @param g the Thread group
  * @param target the object whose run() method gets called
  * @param name the name of the new Thread
  * @param stackSize the desired stack size for the new thread, or
  *        zero to indicate that this parameter is to be ignored.
  */
private void init(ThreadGroup g, Runnable target, String name,long stackSize) {
    if (name == null) {
        throw new NullPointerException("name cannot be null");
    }
    //返回当前线程,即创建该hread的线程 currentThread是个本地方法
    Thread parent = currentThread();
    //安全管理器根据Java安全策略文件决定将哪组权限授予类
    //如果想让应用使用安全管理器和安全策略,可在启动JVM时设定-Djava.security.manager选项
    //还可以同时指定安全策略文件
    //如果在应用中启用了Java安全管理器,却没有指定安全策略文件,那么Java安全管理器将使用默认的安全策略
    //它们是由位于目录$JAVA_HOME/jre/lib/security中的java.policy定义的
    SecurityManager security = System.getSecurityManager();
    if (g == null) {
        /* Determine if it's an applet or not */
        /* If there is a security manager, ask the security manager what to do. */
        if (security != null) {
            g = security.getThreadGroup();
        }
        /* If the security doesn't have a strong opinion of the matter use the parent thread group. */
        if (g == null) {
            g = parent.getThreadGroup();
        }
    }
    /* checkAccess regardless of whether or not threadgroup is explicitly passed in. */
    //判断当前运行线程是否有变更其线程组的权限
    g.checkAccess();
    if (security != null) {
        if (isCCLOverridden(getClass())) {
            security.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
        }
    }
    //新建线程数量计数+1 或者说未就绪线程数+1==> nUnstartedThreads++;
    g.addUnstarted();
    this.group = g;
    this.daemon = parent.isDaemon();//若当前运行线程是守护线程,新建线程也是守护线程
    this.priority = parent.getPriority();//默认使用当前运行线程的优先级
    this.name = name.toCharArray();
    //设置contextClassLoader
    if (security == null || isCCLOverridden(parent.getClass()))
        this.contextClassLoader = parent.getContextClassLoader();
    else
        this.contextClassLoader = parent.contextClassLoader;
    this.inheritedAccessControlContext = AccessController.getContext();
    this.target = target;
    setPriority(priority);//若有指定的优先级,使用指定的优先级
    if (parent.inheritableThreadLocals != null)
        this.inheritableThreadLocals =
            ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
    /* Stash the specified stack size in case the VM cares */
    this.stackSize = stackSize;
    /* Set thread ID 线程安全,序列号每次同步+1*/
    tid = nextThreadID();
}
private static synchronized long nextThreadID() {
    return ++threadSeqNumber;
}

■ start 方法

/**
  * Causes this thread to begin execution; the Java Virtual Machine
  * calls the <code>run</code> method of this thread.
  *     线程进入就绪态,随后JVM将会调用这个线程run方法
  *     当获取到CPU时间片时,会立即执行run方法,此时线程会直接变成运行态
  * <p>
  * The result is that two threads are running concurrently: the
  * current thread (which returns from the call to the
  * <code>start</code> method) and the other thread (which executes its
  * <code>run</code> method).
  * <p>
  * It is never legal to start a thread more than once.
  * In particular, a thread may not be restarted once it has completed execution.
  *     一个线程只能被start一次,特别是线程不会在执行完毕后重新start
  *     当线程已经start了,再次执行会抛出IllegalThreadStateException异常
  * @exception  IllegalThreadStateException  if the thread was already started.
  * @see        #run()
  * @see        #stop()
  */
public synchronized void start() {
    /**
      * This method is not invoked for the main method thread or "system"
      * group threads created/set up by the VM. Any new functionality added
      * to this method in the future may have to also be added to the VM.
      * 该方法不会被主线程或系统线程组调用,若未来有新增功能,也会被添加到VM中
      * A zero status value corresponds to state "NEW".
      * 0对应"已创建"状态 -> 用常量或枚举标识多好
      */
    if (threadStatus != 0)
        throw new IllegalThreadStateException();
    /* Notify the group that this thread is about to be started
     * so that it can be added to the group's list of threads
     * and the group's unstarted count can be decremented. */
    //通知所属线程组该线程已经是就绪状态,因而可以被添加到该线程组中
    //同时线程组的未就绪线程数需要-1,对应init中的+1
    group.add(this);
    boolean started = false;
    try {
        //调用本地方法,将内存中的线程状态变更为就绪态
        //同时JVM会立即调用run方法,获取到CPU之后,线程变成运行态并立即执行run方法
        start0();
        started = true;//标记为已开启
    } finally {
        try {
            if (!started) {
                group.threadStartFailed(this);//如果变更失败,要回滚线程和线程组状态
            }
        } catch (Throwable ignore) {
            /* do nothing. If start0 threw a Throwable then
                 it will be passed up the call stack */
            //如果start0出错,会被调用栈直接通过
        }
    }
}
-------------
//start之后会立即调用run方法
Thread t = new Thread( new Runnable() {
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName());
    }
},"roman");
t.start();  //roman

 ■ run 方法

 /**
   * If this thread was constructed using a separate <code>Runnable</code> run object,
   * then that <code>Runnable</code> object's <code>run</code> method is called;
   * otherwise, this method does nothing and returns.
   * Subclasses of <code>Thread</code> should override this method.
   *    若Thread初始化时有指定Runnable就执行其的run方法,否则doNothing
   *    该方法必须被子类实现
   */
@Override
public void run() {
    if (target != null) {
        target.run();
    }
}

 ■ isAlive 方法

/**
  * Tests if this thread is alive. A thread is alive if it has
  * been started and has not yet died.
  *     测试线程是否处于活动状态
  *     活动状态:线程处于正在运行或者准备开始运行状态
  * @return  <code>true</code> if this thread is alive;
  *          <code>false</code> otherwise.
  */
public final native boolean isAlive();

✺ 线程运行 : 模拟电梯运行类

public class LiftOff implements Runnable {
    private int countDown = 10;   //电梯阶层
//    private static int taskCount = 0;
//    private final int id = taskCount++;

    public LiftOff(){
    }

// syn countDown
private synchronized int getCountDown(){ --countDown; return countDown; } // 获取电梯状态 public String status() { return Thread.currentThread().toString()+ "("+ (countDown > 0? countDown: "Liftoff!") + "),"; } @Override public void run(){ while (getCountDown() >0){ System.out.println(status()); Thread.yield(); } } public static void main(String[] args) { // thread's start() Thread thread = new Thread(new LiftOff()); thread.start(); // 调用 run() System.out.println("================Waiting for LiftOff...==========================="); } }
  • 线程都会有自己的名字
  • 获取线程对象的方法: Thread.currentThread()
  • 目标 run() 结束后线程完成
  • JVM线程调度程序决定实际运行哪个处于可运行状态的线程
  • 使用线程池执行处理任务

线程状态流程图:

 ■ sleep 方法

/**
 * Causes the currently executing thread to sleep (temporarily cease execution)
 * for the specified number of milliseconds plus the specified number of nanoseconds, 
 * subject to the precision and accuracy of system timers and schedulers.
 * The thread does not lose ownership of any monitors.
 *      使线程睡眠一段毫秒时间,但线程并不会丢失已有的任何监视器
 */
public static void sleep(long millis, int nanos) throws InterruptedException {
    if (millis < 0) {
        throw new IllegalArgumentException("timeout value is negative");
    }
    if (nanos < 0 || nanos > 999999) {
        throw new IllegalArgumentException("nanosecond timeout value out of range");
    }
    //换算用
    if (nanos >= 500000 || (nanos != 0 && millis == 0)) {
        millis++;
    }
    sleep(millis);
}
/** 我们一般会直接调用native方法,这或许是我们主动使用的最多次的native方法了 **/
public static native void sleep(long millis) throws InterruptedException;

 ■ yield 方法

/**
  * A hint to the scheduler that the current thread is willing to yield
  * its current use of a processor. The scheduler is free to ignore this hint.
  *  暗示线程调度器当前线程将释放自己当前占用的CPU资源
  *  线程调度器会自由选择是否忽视此暗示
  * <p> Yield is a heuristic attempt to improve relative progression
  * between threads that would otherwise over-utilise a CPU. Its use
  * should be combined with detailed profiling and benchmarking to
  * ensure that it actually has the desired effect.
  *  该方法会放弃当前的CPU资源,将它让给其他的任务去占用CPU执行时间
  *  但放弃的时间不确定,可能刚刚放弃又获得CPU时间片
  * <p> It is rarely appropriate to use this method. It may be useful
  * for debugging or testing purposes, where it may help to reproduce
  * bugs due to race conditions. It may also be useful when designing
  * concurrency control constructs such as the ones in the
  * {@link java.util.concurrent.locks} package.
  *  该方法的适合使用场景比较少,主要用于Debug,比如Lock包设计
  */
public static native void yield();

 ■ interrupt 方法

/**
  * Interrupts this thread.
  *     中断一个线程
  * <p> Unless the current thread is interrupting itself, which is always permitted,
  * the {@link #checkAccess() checkAccess} method of this thread is invoked,
  * which may cause a {@link SecurityException} to be thrown.
  *     如果当前线程不是被自己中断,可能会抛出SecurityException异常
  * <p> If this thread is blocked in an invocation of the {@link Object#wait() wait()}, 
  * {@link Object#wait(long) wait(long)}, or {@link Object#wait(long, int) wait(long, int)} 
  * methods of the {@link Object} class, or of the {@link #join()}, {@link #join(long)}, {@link
  * #join(long, int)}, {@link #sleep(long)}, or {@link #sleep(long, int)},methods of this class,
  * then its interrupt status will be cleared and it will receive an {@link InterruptedException}.
  *     若当前线程已被Object.wait()方法、Thread的join()或sleep方法阻塞,
  *     则调用该中断方法会抛出InterruptedException同时中断状态会被清除
  * <p> If this thread is blocked in an I/O operation upon an {@link 
  * java.nio.channels.InterruptibleChannel </code>interruptible channel<code>}
  * then the channel will be closed, the thread's interrupt status will be set,
  * and the thread will receive a {@link java.nio.channels.ClosedByInterruptException}.
  *     若当前线程在InterruptibleChannel上发生IO阻塞,该通道要被关闭并将线程状态设置为中断同时抛出
  * ClosedByInterruptException异常
  * <p> If this thread is blocked in a {@link java.nio.channels.Selector} then the thread's 
  * interrupt status will be set and it will return immediately from the selection operation,
  * possibly with a non-zero value, just as if the selector's {@link
  * java.nio.channels.Selector#wakeup wakeup} method were invoked.
  *     若该线程被选择器阻塞,将线程状态设置为中断同时从选取方法中立即返回
  *     该选取方法通常会返回一个非0值,当wakeup方法正好被调用时
  * <p> If none of the previous conditions hold then this thread's interrupt status will be set. </p>
  *     非上述情况都会将线程状态设置为中断
  * <p> Interrupting a thread that is not alive need not have any effect.
  *     中断一个非活线程不会有啥影响
  * @throws  SecurityException if the current thread cannot modify this thread
  * @revised 6.0
  * @spec JSR-51
  */
public void interrupt() {
    if (this != Thread.currentThread())
        checkAccess();
    synchronized (blockerLock) {
        Interruptible b = blocker;
        if (b != null) {
            // Just to set the interrupt flag
            // 调用interrupt方法仅仅是在当前线程中打了一个停止的标记,并不是真的停止线程!
            interrupt0();           
            b.interrupt(this);
            return;
        }
    }
    interrupt0();
}

 ■ Daemon

  • 分类:在JAVA中分成两种线程:用户线程守护线程
  • 特性:当进程中不存在非守护线程时,则全部的守护线程会自动化销毁
  • 应用: JVM在启动后会生成一系列守护线程,最有名的当属GC(垃圾回收器)
 1 Thread t2 = new Thread(new Runnable() {
 2     @Override
 3     public void run() {
 4         System.out.println("守护线程运行了");
 5         for (int i = 0; i < 500000;i++){
 6             System.out.println("守护线程计数:" + i);
 7         }
 8     }
 9 }, "kira");
10 t2.setDaemon(true);
11 t2.start();
12 Thread.sleep(500);
13 -------------
14 //输出:
15 ......
16 守护线程计数:113755
17 守护线程计数:113756
18 守护线程计数:113757
19 守护线程计数:113758
20 //结束打印:会发现守护线程并没有打印500000次,因为主线程已经结束运行了


■ wait 和 notify 机制

   wait()使线程停止运行,notify()使停止的线程继续运行 

  1. 使用wait()、notify()、notifyAll()需要先对调用对象加锁,即只能在同步方法或同步块中调用这些方法
  2. 调用wait()方法后,线程状态由RUNNING变成WAITING,并将当前线程放入对象的等待队列中
  3. 调用notify()或notifyAll()方法之后,等待线程不会从wait()返回,需要notify()方法所在同步块代码执行完毕而释放锁之后,等待线程才可以获取到该对象锁并从wait()返回
  4. notify()方法将随机选择一个等待线程从等待队列中移到同步队列中;notifyAll()方法会将等待队列中的所有等待线线程全部移到同步队列中,被移动线程状态由WAITING变成BLOCKED
// wait/notify 简单实例
public class NumberPrint implements Runnable {
    private int number;
    public byte[] res;
    public static int count = 5;

    public NumberPrint(int number, byte a[]){
        this.number = number;
        res = a;
    }

    @Override
    public void run() {
        synchronized (res){
            while (count-- > 0){
                try {
                    res.notify();  //唤醒等待res资源的线程,把锁交给线程(该同步锁执行完毕自动释放锁)
                    System.out.println(" " + number);
                    res.wait();  //释放CPU控制权,释放res的锁,本线程阻塞,等待被唤醒
                    System.out.println("----------线程"+Thread.currentThread().getName() + "获得锁,wait()后的代码继续运行:"+ number);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }  //end of while
            return;
        }  //syn
    }

    public static void main(String[] args) {
        final byte[] a = {0};  //以该对象为共享资源
        new Thread(new NumberPrint(1,a),"1").start();
        new Thread(new NumberPrint(2,a),"2").start();
    }
}

 

 

*****各位看客,由于对线程的调度机制还理解比较浅,所以本文会持续更新……  ********

*****特别感谢我的好友kira 对本人的友情源码帮助,谢谢kira 好基友.....*******************

 

posted @ 2017-10-12 16:32 romanjoy 阅读(...) 评论(...) 编辑 收藏