文档章节

深入理解java线程

greki
 greki
发布于 2017/06/19 18:35
字数 6647
阅读 1615
收藏 0

1.线程基本概念

java线程经常要讲到从java-->jvm-->内核,3个层面去理解相关逻辑。

1.1.进程(processor)和线程(thread)

    容易混的是进程/线程,进程到线程是一个慢慢发展的过程,如linux早期版本没有提供应用多线程支持,后面出现轻量级进程(LWP:Light-weight process)对多线程应用提供了很好的支持,如 Native POSIX Thread Library(NPTL)。

    单从linux看就一种类型的进程,那就是task_struct,linux其实也没有线程的概念, 只是将那些与其他进程共享资源的进程称之为线程。

进程:操作系统进行资源分配和调度的基本单位,从内核观点看,进程的目的是担当分配系统资源(CPU时间,内存等)的实体。top/或者任务管理器可以看的进程的相关资源使用情况。

线程:线程是操作系统进程中能够并发执行的实体,是处理器调度和分派的基本单位。

 

1.2.JVM线程调度

JVM线程调度:依赖JVM内部实现,主要是Native thread scheduling(后面我们也主要说这种),是依赖操作系统的,所以java也不能完全是跨平台独立的,对线程调度处理非常敏感的业务开发必须关注底层操作系统的线程调度差异,所以理解线程的时候,一个线程是java线程对象,一个是调度器的线程(jvm)。

Green Thread Schedule 或者叫用户级线程(User Level Thread,ULT):操作系统内核不知道应用线程的存在。

Native thread scheduling 或者 内核级线程(Kernel Level Thread ,KLT):它们是依赖于内核的,即无论是用户进程中的线程,还是系统进程中的线程,它们的创建、撤消、切换都由内核实现。

 

延伸开要理解下:用户态和内核态(特权级别区别)/用户线程、轻量级进程、内核线程。

 

参考:

http://blog.csdn.net/hzrandd/article/details/50577653。http://blog.csdn.net/gatieme/article/details/51482122

 

1.3.线程状态

java线程的状态只有(不要和操作系统进程的状态混淆)。

public enum State {
    /**
     * Thread state for a thread which has not yet started.
     */
    NEW,

    /**
     * Thread state for a runnable thread.  A thread in the runnable
     * state is executing in the Java virtual machine but it may
     * be waiting for other resources from the operating system
     * such as processor.
     */
    RUNNABLE,

    /**
     * Thread state for a thread blocked waiting for a monitor lock.
     * A thread in the blocked state is waiting for a monitor lock
     * to enter a synchronized block/method or
     * reenter a synchronized block/method after calling
     * {@link Object#wait() Object.wait}.
     */
    BLOCKED,

    /**
     * Thread state for a waiting thread.
     * A thread is in the waiting state due to calling one of the
     * following methods:
     * <ul>
     *   <li>{@link Object#wait() Object.wait} with no timeout</li>
     *   <li>{@link #join() Thread.join} with no timeout</li>
     *   <li>{@link LockSupport#park() LockSupport.park}</li>
     * </ul>
     *
     * <p>A thread in the waiting state is waiting for another thread to
     * perform a particular action.
     *
     * For example, a thread that has called <tt>Object.wait()</tt>
     * on an object is waiting for another thread to call
     * <tt>Object.notify()</tt> or <tt>Object.notifyAll()</tt> on
     * that object. A thread that has called <tt>Thread.join()</tt>
     * is waiting for a specified thread to terminate.
     */
    WAITING,

    /**
     * Thread state for a waiting thread with a specified waiting time.
     * A thread is in the timed waiting state due to calling one of
     * the following methods with a specified positive waiting time:
     * <ul>
     *   <li>{@link #sleep Thread.sleep}</li>
     *   <li>{@link Object#wait(long) Object.wait} with timeout</li>
     *   <li>{@link #join(long) Thread.join} with timeout</li>
     *   <li>{@link LockSupport#parkNanos LockSupport.parkNanos}</li>
     *   <li>{@link LockSupport#parkUntil LockSupport.parkUntil}</li>
     * </ul>
     */
    TIMED_WAITING,

    /**
     * Thread state for a terminated thread.
     * The thread has completed execution.
     */
    TERMINATED;
}

说明

  • NEW 状态是指线程刚创建,尚未启动,不会出现在Dump中。

  • RUNNABLE 状态是线程正在正常运行中, 当然可能会有某种耗时计算/IO等待的操作/CPU时间片切换等, 这个状态下发生的等待一般是其他系统资源, 而不是锁, Sleep等,主要不同是runable里面有2个状态,可以理解为就是JVM调用系统线程的状态。

  • BLOCKED  受阻塞并等待监视器锁。这个状态下, 是在多个线程有同步操作的场景, 比如正在等待另一个线程的synchronized 块的执行释放, 或者可重入的 synchronized块里别人调用wait() 方法, 也就是这里是线程在等待进入临界区

  • WAITING  无限期等待另一个线程执行特定操作。这个状态下是指线程拥有了某个锁之后, 调用了他的wait方法, 等待其他线程/锁拥有者调用 notify / notifyAll 一遍该线程可以继续下一步操作, 这里要区分 BLOCKED 和 WATING 的区别, 一个是在临界点外面等待进入, 一个是在临界点里面wait等待别人notify, 线程调用了join方法 join了另外的线程的时候, 也会进入WAITING状态, 等待被他join的线程执行结束

  • TIMED_WAITING  有时限的等待另一个线程的特定操作。这个状态就是有限的(时间限制)的WAITING, 一般出现在调用wait(long), join(long)等情况下, 另外一个线程sleep后, 也会进入TIMED_WAITING状态

  • TERMINATED 这个状态下表示 该线程的run方法已经执行完毕了, 基本上就等于死亡了(当时如果线程被持久持有, 可能不会被回收)

理解:

  1. block

    • BLOCKED是指线程正在等待获取锁;WAITING是指线程正在等待其他线程发来的通知(notify),收到通知后,可能会顺序向后执行(RUNNABLE),也可能会再次获取锁,进而被阻塞住(BLOCKED)。 blocked 状态,对应为草根期,从未牛逼过,一直再尝试获取成功的钥匙.
  2. waiting:

    • 当方法wait()被执行后,锁被自动释放,但执行完notify()方法,锁却不自动释放.
    • wait()会立刻释放sycronized(obj)中的obj锁,以便其他线程可以执行obj.nodify().但是nodify()不会立刻立刻释放sycronized(obj)中的obj锁,必须要等nodify()所在线程执行完sycronized(obj)块中的所有代码才会释放这把锁.
    • wait进入阻塞,就会释放当前锁,不然notify的时候也要持有同一把锁,它怎么获取得到;wait()的意思就是放弃已经持有的锁然后等待.waiting 状态,对应没落贵族,曾经牛逼过,现在在等待一个契机.

 

1.4.线程状态和jstack输出

  • NEW、TERMINATED在jstack里看不到,可通过thread对象(普通对象)打印。
  • RUNNABLE:在运行中。
  • BLOCKED:java.lang.Thread.State: BLOCKED (on object monitor)
  • WAITING:java.lang.Thread.State: WAITING (on object monitor)
  •                 java.lang.Thread.State: WAITING (parking)---locksupport.park(),这个会有点不一样,如果用jvisualvm看的,这个是驻留状态(parking)。
  • TIMED_WAITING :TIMED_WAITING (sleeping)

 

  • locked <地址> 目标:使用synchronized申请对象锁成功,监视器的拥有者。
  • waiting to lock <地址> 目标:使用synchronized申请对象锁未成功,在迚入区等待。
  • waiting on <地址> 目标:使用synchronized申请对象锁成功后,释放锁幵在等待区等待。
  • parking to wait for <地址> 目标

 

详细参考:状态和Jstack日志的对应关系

              JVM 内部运行线程介绍 

               http://blog.leanote.com/post/anglema/总结-多线程-线程状态

1.5.线程内存模型

    Java内存模型(JMM)规定了jvm有主内存,主内存是多个线程共享的,每个线程都有自己的工作内存,工作内存存储了主存的某些对象的副本,当然线程的工作内存大小是有限制的。

当线程操作某个对象时,执行顺序如下:
 (1) 从主存复制变量到当前工作内存 (read and load)
 (2) 执行代码,改变共享变量值(工作内存) (use and assign)
 (3) 用工作内存数据刷新主存相关内容 (store and write)

可见性:当一个共享变量在多个线程的工作内存中都有副本时,如果一个线程修改了这个共享变量,那么其他线程应该能够看到这个被修改后的值。

有序性:线程在引用变量时不能直接从主内存中引用,如果线程工作内存中没有该变量,则会从主内存中拷贝一个副本到工作内存中,这个过程为read-load,完成后线程会引用该副本。当同一线程再度引用该字段时,有可能重新从主存中获取变量副本(read-load-use),也有可能直接引用原来的副本 (use),也就是说 read,load,use顺序可以由JVM实现系统决定。

参考:http://blog.csdn.net/haolongabc/article/details/7249098

 

1.6.线程切换

线程在runable和非runalbe状态切换时候,进行线程上下文的保存和恢复。线程上下文一般包括(context:cpu的寄存器和程序计数器的在某个一个时间点的数据)。

线程切换时除了context的切换开销还有,调度CPU的时间开销和CPU缓存内容失效的开销。

1.7.线程安全

一段操作共享数据的代码能够保证在同一时间内被多个线程执行而仍然保持其正确性的,达到预期效果的,就称为线程安全的。

多线程同时操作如下资源可能存在线程安全问题:

对象:由全局变量及静态变量。

资源:数据库、redis、文件、第三方接口等等。

 

1.8.线程优先级及优先级反转

        现代操作系统,为了对所有的线程进行有差别的时间调度,我们对所有的线程分配了优先级。打个比方,调度队列有32个线程,每个线程的优先级也是1到32。这些优先级对于线程来说有什么意义呢?那就是,高优先级可以获得较多的时间片运行机会。

        优先级反转Priority inversion解决低优先级只持有高优先级需要的锁对象,而低优先级的获取运行时间很短,就会导致锁的时间非常就。不错的解决方案就是提升持有锁的线程优先级,如果需要锁的优先级比较高。

    Priority inversion can greatly delay the execution of a higher-priority thread. For example, suppose you have three threads with priorities of 3, 4, and 9. Priority 3 thread is running and the other threads are blocked. Assume that the priority 3 thread grabs a lock, and the priority 4 thread unblocks. The priority 4 thread becomes the currently running thread. Because the priority 9 thread requires the lock, it continues to wait until the priority 3 thread releases the lock. However, the priority 3 thread cannot release the lock until the priority 4 thread blocks or terminates. As a result, the priority 9 thread delays its execution.

A JVM's thread scheduler usually solves the priority inversion problem through priority inheritance: The thread scheduler silently raises the priority of the thread holding the lock when a higher-priority thread requests the lock. As a result, both the thread holding the lock and the thread waiting for the lock temporarily have equal priorities. Using the previous example, the priority 3 thread (holding the lock) would temporarily become a priority 9 thread as soon as the priority 9 thread attempts to acquire the lock and is blocked. As a result, the priority 9 thread (holding the lock) would become the currently running thread (even when the priority 4 thread unblocks). The priority 9 thread would finish its execution and release the lock, allowing the waiting priority 9 thread to acquire the lock and continue execution. The priority 4 thread would lack the chance to become the currently running thread. Once the thread with its silently raised priority releases the lock, the thread scheduler restores the thread's priority to its original priority. Therefore, the thread scheduler would restore the priority 9 thread previously holding the lock to priority 3, once it releases the lock. Thus, priority inheritance ensures that a lower-priority thread holding a lock is not preempted by a thread whose priority exceeds the lock-holding thread's priority but is less than the priority of the thread waiting for the lock to release.

 

1.9.参考资料

进程、线程与处理器的调度

java-101--understanding-java-threads--part-3--thread-scheduling-and-wait-notify

programming-java-threads-in-the-real-world

Linux 线程模型的比较:LinuxThreads 和 NPTL

 

2.线程的源码

如果要说thread对象和其他对象有什么区别,thread创建的时候JVM会为分配两个调用栈(call stack),一个跟踪java代码间的调用关系,一个跟踪java代码于native 代码的调用关系。

2.1.ThreadLocal

通过thread对象的threadlocals属性实现,参考http://www.cnblogs.com/dolphin0520/p/3920407.html

Java Doc:

1.该类提供了线程局部 (thread-local) 变量。

2.这些变量不同于它们的普通对应物,因为访问某个变量(通过其 get 或 set 方法)的每个线程都有自己的局部变量,它独立于变量的初始化副本。

3.ThreadLocal 实例通常是类中的 private static 字段,它们希望将状态与某一个线程(例如,用户 ID 或事务 ID)相关联。

应用场景:

线程会话:可以做一些线程会话场景的处理,比如一个处理线程上下文;工具类防止冲突,每个线程一个副本等

 

内存泄漏风险:

废弃threadlocal的引起,占用的内存会在3中情况下清理(不用产生很多ThreadLocal对象一般没问题):

1. thread结束,那么与之相关的threadlocal value会被清理 

2.GC后,thread.threadlocals(map) threshold超过最大值时,会清理

3GC后,thread.threadlocals(map) 添加新的Entry时,hash算法没有命中既有Entry时,会清理

 

2.2.InheritableThreadLocal

实现父线程线程变量子线程访问,通过thread对象的inheritableThreadLocals属性实现,通过继承和覆盖ThreadLocal的getMap,CreateMap方法实现,参考:http://www.bbsmax.com/A/kPzOGj9ozx/;

当前线程在创建新线程对象的时候,进行了线程变量复制,实现了线程变量的copy;

Thread thread = new MyThread()

private void init(ThreadGroup g, Runnable target, String name,
                  long stackSize, AccessControlContext acc) {
                  …..
                   Thread parent = currentThread();
                  ….
		 if (parent.inheritableThreadLocals != null)
                                  this.inheritableThreadLocals =ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);


}

 

2.3.contextClassLoader

为了解决:如jdk中的jdbc API 和具体数据库厂商的实现类SPI的类加载问题,dbc API的类是由BootStrap,实现类是通过Ext或者App加载的jdbc API调用问题。

Thread源码看,ContextClassLoader就是Thread的一个属性,没什么复杂的,复杂在外面调用关系,如何通过线程持有classloader来使用,比如:

java.sql.DriverManager

private static Connection getConnection(
    String url, java.util.Properties info, Class<?> caller) throws SQLException {
    /*
     * When callerCl is null, we should check the application's
     * (which is invoking this class indirectly)
     * classloader, so that the JDBC driver class outside rt.jar
     * can be loaded from here.
     */
    ClassLoader callerCL = caller != null ? caller.getClassLoader() : null;
    synchronized(DriverManager.class) {
        // synchronize loading of the correct classloader.
        if (callerCL == null) {
            callerCL = Thread.currentThread().getContextClassLoader();
        }
    }

BootStrap加载的类调用DriverManager到getConnection的时候,其实是在之前AppClassloader加载的类里,创建线程的时候把AppClassloader放到ContextClassLoader,再来获取这个AppClassloader加载的类,进行具体调用。

 

2.4.ThreadID

Thread的静态变量,控制ID生成,生成后放在对象属性tid上,jvm应用内唯一。

/* For generating thread ID */
private static long threadSeqNumber;

private static synchronized long nextThreadID() {
    return ++threadSeqNumber;
}
 /** Thread ID */  
 private long tid;

 

2.5.group(ThreadGroup)

threadGroup线程组,主要是用于管理组内线程,每个线程都属于一个threadGroup,内部维护在线程组的树形结构。

ThreadGroup tg1 = new ThreadGroup ("A");
ThreadGroup tg2 = new ThreadGroup (tg1, "B");

---

        Thread parent = currentThread();
        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();
            }
        }

常用方法:

threadGroup.list()方法打印线程组里的线程信息和子线程组信息(但是不会递归一层层全部打印)

threadGroup.setMaxPriority,设置线程组和所子线程组的最大优先级(递归调用),只对后面加进线程组的线程有效,已在组内的线程无效,在thread.setPriority(int newPriority) 会用到。

threadGroup.interrupt:该组和子组的所有线程中断操作(递归调用)。

enumerate: 拷贝组内所有active线程到指定数组,可以用来了解组内所有线程的状态。

参考:http://www.javaworld.com/article/2074481/java-concurrency/java-101--understanding-java-threads--part-4---thread-groups--volatility--and-threa.html

 

2.6.Thread用于调试和监控几个方法

dumpStack:仅用于调试,打印Stack trace。

activeCount:返回当前线程组的所有的线程数(包括子线程组里的)。

native boolean holdsLock(Object obj):判断线程是否持有对象锁(monitor lock on the specified object)。

2.7.runable和thread区别

所有线程的启动通过Thread.start调用native方法实现,主要区别在接口和类的区别,注意构造函数。

public Thread(ThreadGroup group, Runnable target)

java只能一个父类,有了Runable接口,可以很好把一个类处理转成线程处理。

 

3.同步处理

3.1.关键字:synchronized

JVM实现逻辑:

1 获得同步锁
2 清空工作内存
3 从主存拷贝变量副本到工作内存
4 对这些变量计算
5 将变量从工作内存写回到主存
6 释放锁

3.2.关键字:volatile

volatile解决内存可见性

volatile变量,都不拷贝副本到工作内存,任何修改都及时写在主存

3.3.object的wait notify notifyAll

wait(): Causes the current thread to wait until another thread invokes the notify() method or the notifyAll() method for this object.

notify(): Wakes up a single thread that is waiting on this object's monitor.

notifyAll(): Wakes up all threads that are waiting on this object's monitor.

必须在synchronized方法或synchronized块内使用。

需要注意常见问题

notify丢失:多个线程进入了wait,而只notify一次。那么另外个等待线程就会一直wait。

解决:这个可以用notifyAll()解决,但是假设有N个线程在条件队列中等待,调用notifyall会唤醒所有线程,然后这N个线程竞争同一个锁,最多只有一个线程能够得到锁,于是其它线程又回到挂起状态。这意味每一次唤醒操作可能带来大量的上下文切换(如果N比较大的话),同时有大量的竞争锁的请求。这对于频繁的唤醒操作而言性能上可能是一种灾难。。

spurious wakeup虚假唤醒:白文就是本来在A条件下,你是要wait的,结果wait被唤醒,在条件A下,变成往下执行。

wait方法可以分为三个操作:

(1)释放锁并阻塞

(2)等待条件cond发生

(3)获取通知后,竞争获取锁

 

假设此时有线程A,C买票,线程A调用wait方法进入等待队列,线程C买票时发现线程B在退票,获取锁失败,线程C阻塞,进入阻塞队列,线程B退票时,余票数量+1(满足条件2 等待条件发生),线程B调用notify方法后,线程C马上竞争获取到锁,购票成功后余票为0,而线程A此时正处于wait方法醒来过程中的第三步(竞争获取锁获取锁),当线程C释放锁,线程A获取锁后,会执行购买的操作,而此时是没有余票的。

另外防止,spurious wakeup虚假唤醒,一般建议自己程序把wait放在条件循环内。

参考:http://www.javaworld.com/article/2071214/java-concurrency/java-101--understanding-java-threads--part-3--thread-scheduling-and-wait-notify.html?page=3

 

4.中断处理(Interrupt)

4.1.中断场景举例

  • 点击某个桌面应用中的取消按钮时;
  • 某个操作超过了一定的执行时间限制需要中止时;
  • 多个线程做相同的事情,只要一个线程成功其它线程都可以取消时;
  • 一组线程中的一个或多个出现错误导致整组都无法继续时;
  • 当一个应用或服务需要停止时。

4.2.中断原理

就是外部调用某个线程t的Interrupt(),通知t你可以中断了,t怎么做看t自己。

  1. 中断暂理解成【被设置中断】更合适,只是给某个线程t对象设置了一个属性【中断状态】。(PS:interrupt 不同于stop,状态正是是native实现)。
  2. 线程t自己检测【中断状态】,自己做对应的处理。

线程t被设置中断时的处理:

  1. 线程t在WAITING、TIMED_WAITING状态,会抛出InterruptedException(JVM底层实现,是不是立即抛出?BLOCKED状态不会响应中断请求抛出InterruptedException)。
  2. 线程t自己在运行时判断,if (interrupted()/isInterrupted())判断后做相关的中断后处理;
  3. 其他BlockingQueue#put、BlockingQueue#take、可中断IO,我理解都是基于上面1,2实现。
public class InterruptTest {

    private static Object lockObj = new Object();

    public static void main(String[] args) throws InterruptedException {

        Thread waitThread = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (lockObj) {
                    System.out.println(buildThreadString() + " is running,get lock");
                    try {
                        lockObj.wait();
                    } catch (InterruptedException e) {
                        System.err.println(buildThreadString() + " is interrupted. ");
                    }
                }
            }
        }, "waitThread");

        Thread timedWaitingThread = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(200000L);
                } catch (InterruptedException e) {
                    System.err.println(buildThreadString() + " is interrupted. ");
                }
            }
        }, "timedWaitingThread");

        waitThread.start();
        Thread.sleep(1000L);
        doInterrupte(waitThread);


        timedWaitingThread.start();
        Thread.sleep(1000L);
        doInterrupte(timedWaitingThread);

        //通过状态测试
        Thread testInterrupteState = new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    //do something
                    for (int i = 0; i < 10000000; i++) {
                        Math.atan2((double) 222231.3242f, (double) 21312321.32f);
                    }
                    //check 中断
                    if (Thread.interrupted()) {

                        System.out.println(buildThreadString() + " 中断");
                        break;
                    }
                }
            }
        }, "testInterrupteState");
        testInterrupteState.start();

        Thread.sleep(1000L);
        doInterrupte(testInterrupteState);

    }

    private static void doInterrupte(Thread thred) {
        System.out.println(System.currentTimeMillis() + ":" + thred.getName() + ":" + thred.getState() + " interrupt.");
        thred.interrupt();
    }

    private static String buildThreadString() {
        return System.currentTimeMillis() + ":" + Thread.currentThread().getName();
    }
}

1497516158424:waitThread is running,get lock
1497516159427:waitThread is interrupted. 
1497516159427:waitThread:WAITING interrupt.

1497516160432:timedWaitingThread:TIMED_WAITING interrupt.
1497516160433:timedWaitingThread is interrupted. 

1497516161437:testInterrupteState:RUNNABLE interrupt.
1497516161873:testInterrupteState 中断

 

 

4.3.中断相关方法

  • public static boolean interrupted

        测试当前线程是否已经中断。线程的中断状态 由该方法清除(状态=false)。但这个方法的命名极不直观,很容易造成误解,需要特别注意

  • public boolean isInterrupted()

        测试线程是否已经中断。线程的中断状态不受该方法的影响。

  • public void interrupt()

        中断线程(状态=true)

4.4.Thread.interrupt VS Thread.stop

    Thread.stop方法已经不推荐使用了。而在某些方面Thread.stop与中断机制有着相似之处。如当线程在等待内置锁或IO时,stop跟interrupt一样,不会中止这些操作;当catch住stop导致的异常时,程序也可以继续执行,虽然stop本意是要停止线程,这么做会让程序行为变得更加混乱

 

    最重要区别就是中断需要程序自己去检测然后做相应的处理,而Thread.stop会直接在代码执行过程中抛出ThreadDeath错误,这是一个java.lang.Error的子类。

参考:

interrupte中断详解

http://www.infoq.com/cn/articles/java-interrupt-mechanism/

 

5.Thread其他几个方法

5.1.suspend()

    使线程暂停,但是不会释放类似锁这样的资源。

    因为它具有固有的死锁倾向。调用suspend()方法的时候,目标线程会停下来。如果目标线程挂起时在保护关键系统资源的监视器上保持有锁,则在目标线程重新开始以前,其他线程都不能访问该资源。除非被挂起的线程恢复运行。对任何其他线程来说,如果想恢复目标线程,同时又试图使用任何一个锁定的资源,就会造成死锁。

5.2.resume()

  使线程恢复,如果之前没有使用suspend暂停线程,则不起作用。

5.3.join() 

    join()方法使当前线程停下来等待,直至另一个调用join方法的线程终止。值得注意的是,线程的在被激活后不一定马上就运行,而是进入到可运行线程的队列中。但是join()可以通过interrupt()方法打断线程的暂停状态,从而使线程立刻抛出InterruptedException。 

/**
 * Waits at most {@code millis} milliseconds for this thread to
 * die. A timeout of {@code 0} means to wait forever.
 *
 * <p> This implementation uses a loop of {@code this.wait} calls
 * conditioned on {@code this.isAlive}. As a thread terminates the
 * {@code this.notifyAll} method is invoked. It is recommended that
 * applications not use {@code wait}, {@code notify}, or
 * {@code notifyAll} on {@code Thread} instances.
 *
 * @param  millis
 *         the time to wait in milliseconds
 *
 * @throws  IllegalArgumentException
 *          if the value of {@code millis} is negative
 *
 * @throws  InterruptedException
 *          if any thread has interrupted the current thread. The
 *          <i>interrupted status</i> of the current thread is
 *          cleared when this exception is thrown.
 */
public final synchronized void join(long millis)
throws InterruptedException {
    long base = System.currentTimeMillis();
    long now = 0;

    if (millis < 0) {
        throw new IllegalArgumentException("timeout value is negative");
    }

    if (millis == 0) {
        while (isAlive()) {
            wait(0);
        }
    } else {
        while (isAlive()) {
            long delay = millis - now;
            if (delay <= 0) {
                break;
            }
            wait(delay);
            now = System.currentTimeMillis() - base;
        }
    }
}

jion是通过线程对象的.wait()实现,方法注释里线程结算,触发了线程对象的notifyall。

参考:https://www.zhihu.com/question/44621343/answer/97640972

5.4.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.
 *
 * <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.
 *
 * <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.
 */

     java 8的文档,yield只是给jvm调度器一个提示放弃当前线程运行,有没有用看jvm,另外一般用于调试和多线程环境模拟,也被用在了并发包里。

   而网上说 ,“让同等优先权的线程运行,如果没有同等优先权的线程,那么Yield()方法将不会起作用”。

自己测了下,跟优先级没有关系,都有效果。

5.5. 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.
 */

threadSuspend:state:TIMED_WAITING:isAlive:true
ThreadParking:state:WAITING:isAlive:true
ThreadWait:state:WAITING:isAlive:true
ThreadIO:state:RUNNABLE:isAlive:true
threadBlocked2:state:BLOCKED:isAlive:true
threadBlocked1:state:TIMED_WAITING:isAlive:true

 

线程方法名称 是否释放同步锁 是否需要在同步的代码块中调用 方法是否已废弃 是否可以被中断
sleep()
wait()
join() -
suspend() -
resume() - -
stop() -

 

6.VM源码分析之Object.wait/notify实现

参考:http://www.jianshu.com/p/f4454164c017

 

7.LockSupport

LockSupport用来创建锁和其他同步类的基本线程阻塞原语,虽然一般多线程不太会用到,但是是一个提供锁机制的工具类,如ReentrantLock- >Sync extends AbstractQueuedSynchronizer-->LockSupport

  • LockSupport.park()和unpark(),与object.wait()和notify()的区别?
  • LockSupport.park(Object blocker)传递的blocker对象做什么用?
  • LockSupport能响应Thread.interrupt()事件不?会抛出InterruptedException异常?

7.1.LockSupport的park/unpark和Object的wait/notify不同:

  • 面向的对象不同,LockSupport操作线程对象;
  • 跟Object的wait/notify不同,LockSupport的park/unpark不需要获取对象的监视器;
  • 实现的机制不同,因此两者没有交集。
  • 但是都能响应interrupte,但不会抛出InterruptedException,也不会设置interrupte 状态。
  • park的特性,不用担心自己的park/unpark的时序问题,否则,如果park必须要在unpark之前,那么给编程带来很大的麻烦!!!,如wait/notify的麻烦

7.2.parkBlocker

        是用于记录线程是被谁阻塞,用来记录线程被阻塞时被谁阻塞的。用于线程监控和分析工具来定位原因的。主要调用了LockSupport的getBlocker方法。

        偏移量就算Thread这个类里面变量parkBlocker在内存中的偏移量。因为parkBlocker就是在线程处于阻塞的情况下才会被赋值。线程都已经阻塞了,如果直接调用线程内的方法,线程是不会回应调用的。

参考:

  • https://my.oschina.net/readjava/blog/282882
  • http://docs.oracle.com/javase/6/docs/technotes/guides/concurrency/threadPrimitiveDeprecation.html
  • http://www.importnew.com/20428.html
  • http://www.cnblogs.com/leesf456/p/5347293.html

 

 

© 著作权归作者所有

greki
粉丝 105
博文 109
码字总数 45236
作品 0
杭州
技术主管
私信 提问
读《深入理解Java虚拟机》- 笔记08

《深入理解Java虚拟机:JVM高级特性与最佳实践》第2版 第10章 早期(编译期)优化 59. 语法糖 在计算机语言中添加某种语法,对语言的功能没有影响,但是方便开发人员使用。 泛型是一种语法糖...

阿历Ali
2018/08/18
0
0
Java并发编程:深入剖析ThreadLocal

想必很多朋友对ThreadLocal并不陌生,今天我们就来一起探讨下ThreadLocal的使用方法和实现原理。首先,本文先谈一下对ThreadLocal的理解,然后根据ThreadLocal类的源码分析了其实现原理和使用...

海子_枫子
2015/07/04
577
0
Java并发(1)- 聊聊Java内存模型

引言 在计算机系统的发展过程中,由于CPU的运算速度和计算机存储速度之间巨大的差距。为了解决CPU的运算速度和计算机存储速度之间巨大的差距,设计人员在CPU和计算机存储之间加入了高速缓存来...

knock_小新
2018/07/18
0
0
JVM 虚拟机(对象创建,类加载器,执行引擎等),

1.揭开 Java 对象创建的奥秘? 2.class 文件结构详解? 3.详解 Java 类的加载过程? > Java 对象创建,class 文件结构 Java对象模型 。Java对象保存在堆内存中。在内存中,一个Java对象包含三...

desaco
2018/08/29
0
0
ThreadLocal使用分析

前言   ThreadLocal从名字上直译为本地线程,看上去像是线程的一种子类,如果你这样理解说明你理解错了。   在上一篇深入理解Android中的消息机制,理解Hander MessageQueue Looper三者之...

huangandroid
2018/08/26
0
0

没有更多内容

加载失败,请刷新页面

加载更多

代理模式之JDK动态代理 — “JDK Dynamic Proxy“

动态代理的原理是什么? 所谓的动态代理,他是一个代理机制,代理机制可以看作是对调用目标的一个包装,这样我们对目标代码的调用不是直接发生的,而是通过代理完成,通过代理可以有效的让调...

code-ortaerc
今天
4
0
学习记录(day05-标签操作、属性绑定、语句控制、数据绑定、事件绑定、案例用户登录)

[TOC] 1.1.1标签操作v-text&v-html v-text:会把data中绑定的数据值原样输出。 v-html:会把data中值输出,且会自动解析html代码 <!--可以将指定的内容显示到标签体中--><标签 v-text=""></......

庭前云落
今天
7
0
VMware vSphere的两种RDM磁盘

在VMware vSphere vCenter中创建虚拟机时,可以添加一种叫RDM的磁盘。 RDM - Raw Device Mapping,原始设备映射,那么,RDM磁盘是不是就可以称作为“原始设备映射磁盘”呢?这也是一种可以热...

大别阿郎
今天
10
0
【AngularJS学习笔记】02 小杂烩及学习总结

本文转载于:专业的前端网站☞【AngularJS学习笔记】02 小杂烩及学习总结 表格示例 <div ng-app="myApp" ng-controller="customersCtrl"> <table> <tr ng-repeat="x in names | orderBy ......

前端老手
昨天
14
0
Linux 内核的五大创新

在科技行业,创新这个词几乎和革命一样到处泛滥,所以很难将那些夸张的东西与真正令人振奋的东西区分开来。Linux内核被称为创新,但它又被称为现代计算中最大的奇迹,一个微观世界中的庞然大...

阮鹏
昨天
18
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部