/ThreadDemo

线程相关知识点总结

Primary LanguageJava

尊重原著 一切来自http://www.cnblogs.com/skywang12345/p/3479024.html指导整理
==================================================================================================
说明:
线程共包括以下5种状态。
1. 新建状态(New)     : 线程对象被创建后,就进入了新建状态。例如,Thread thread = new Thread()。
2. 就绪状态(Runnable): 也被称为“可执行状态”。线程对象被创建后,其它线程调用了该对象的start()方法,从而来启动该线程。例如,thread.start()。处于就绪状态的线程,随时可能被CPU调度执行。
3. 运行状态(Running) : 线程获取CPU权限进行执行。需要注意的是,线程只能从就绪状态进入到运行状态。
4. 阻塞状态(Blocked) : 阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。阻塞的情况分三种:
    (01) 等待阻塞 -- 通过调用线程的wait()方法,让线程等待某工作的完成。
    (02) 同步阻塞 -- 线程在获取synchronized同步锁失败(因为锁被其它线程所占用),它会进入同步阻塞状态。
    (03) 其他阻塞 -- 通过调用线程的sleep()或join()或发出了I/O请求时,线程会进入到阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。
5. 死亡状态(Dead)    : 线程执行完了或者因异常退出了run()方法,该线程结束生命周期。


这5种状态涉及到的内容包括
Object类---定义了wait(), notify(), notifyAll()等休眠/唤醒函数。
Thread类---定义了一系列的线程操作函数。sleep()休眠函数, interrupt()中断函数, getName()获取线程名称等。
synchronized---关键字;区分为synchronized代码块和synchronized方法。synchronized的作用是让线程获取对象的同步锁。

==================================================================================================
synchronzied
    一段synchronized的代码被一个线程执行之前,他要先拿到执行这段代码的权限,
    在Java里边就是拿到某个同步对象的锁(一个对象只有一把锁);
    如果这个时候同步对象的锁被其他线程拿走了,他(这个线程)就只能等了(线程阻塞在锁池等待队列中)。
    取到锁后,他就开始执行同步代码(被synchronized修饰的代码);
    线程执行完同步代码后马上就把锁还给同步对象,其他在锁池中等待的某个线程就可以拿到锁执行同步代码了。
    这样就保证了同步代码在统一时刻只有一个线程在执行。


==================================================================================================
线程基础知识和栗子 -- [见com.kuangye.threaddemo.basis对应包下的实例]
==================================================================================================
    start() : 它的作用是启动一个新线程,新线程会执行相应的run()方法。
              start()不能被重复调用。
    run()   : run()就和普通的成员方法一样,可以被重复调用。单独调用run()的话,会在当前线程中执行run(),而并不会启动新线程!
==================================================================================================
    我们将synchronized的基本规则总结为下面3条,并通过实例对它们进行说明。
    第一条: 当一个线程访问“某对象”的“synchronized方法”或者“synchronized代码块”时,其他线程对“该对象”的该“synchronized方法”或者“synchronized代码块”的访问将被阻塞。
    第二条: 当一个线程访问“某对象”的“synchronized方法”或者“synchronized代码块”时,其他线程仍然可以访问“该对象”的非同步代码块。
    第三条: 当一个线程访问“某对象”的“synchronized方法”或者“synchronized代码块”时,其他线程对“该对象”的其他的“synchronized方法”或者“synchronized代码块”的访问将被阻塞。
==================================================================================================
    在Object.java中,定义了wait(), notify()和notifyAll()等接口。
    wait()的作用是让当前线程【进入阻塞状态,同时让释放当前线程它所持有的锁】
    而notify()和notifyAll()的作用,则是唤醒当前对象上的阻塞线程;notify()是唤醒单个线程,而notifyAll()是唤醒所有的线程。

    Object类中关于等待/唤醒的API详细信息如下:
    notify()      -- 唤醒在此对象监视器上等待的单个线程。
    notifyAll()   -- 唤醒在此对象监视器上等待的所有线程。
    wait()                         -- 让当前线程处于“等待(阻塞)状态”,“直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法”,当前线程被唤醒(进入“就绪状态”)。
    wait(long time)                -- 让当前线程处于“等待(阻塞)状态”,“直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法,或者超过指定的时间量”,当前线程被唤醒(进入“就绪状态”)。
    wait(long timeout, int nanos)  -- 让当前线程处于“等待(阻塞)状态”,“直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法,或者其他某个线程中断当前线程,或者已超过某个实际时间量”,当前线程被唤醒(进入“就绪状态”)。

==================================================================================================
    为什么notify(), wait()等函数定义在Object中,而不是Thread中?

        Object中的wait(), notify()等函数,和synchronized一样,会对“对象的同步锁”进行操作。

        wait()会使“当前线程”等待,因为线程进入等待状态,所以线程应该释放它所持有的“同步锁”,否则其它线程获取不到该“同步锁”而无法运行!
        OK,线程调用wait()之后,会释放它锁持有的“同步锁”;而且,根据前面的介绍,我们知道:等待线程可以被notify()或notifyAll()唤醒。
        现在,请思考一个问题:notify()是依据什么唤醒等待线程的?或者说,wait()等待线程和notify()之间是通过什么关联起来的?
        答案是:依据“对象的同步锁”。

        负责唤醒等待线程的那个线程(我们称为“唤醒线程”),它只有在获取“该对象的同步锁”(这里的同步锁必须和等待线程的同步锁是同一个),
        并且调用notify()或notifyAll()方法之后,才能唤醒等待线程。

        【被唤醒不能立刻执行,必须等到唤醒线程释放了“对象的同步锁”之后(一般是唤醒线程的同步代码块执行结束之后),等待线程才能获取到“对象的同步锁”进而继续运行】


==================================================================================================
    yield()的作用是让步/谦让
        它能让当前线程由“运行状态”进入到“就绪状态”,重新竞争 CPU 的调度权
        可能会获取到,也有可能被其他线程获取到

        可以很好的控制多线程,如执行某项复杂的任务时,如果担心占用资源过多,
        可以在完成某个重要的工作后使用 yield 方法让掉当前 CPU 的调度权,等下次获取到再继续执行,
        这样不但能完成自己的重要工作,也能给其他线程一些运行的机会,[避免一个线程长时间占有CPU资源]


        yield()方法不会释放锁  所以已经获取锁的线程 让出来CPU只有可能让不需要该锁的线程可能获取到CPU

==================================================================================================
    yield() 与 wait()的比较?
        wait()是让线程由“运行状态”进入到“阻塞状态”,
        yield()是让线程由“运行状态”进入到“就绪状态”。

        wait()是会线程释放它所持有对象的同步锁,
        yield()方法不会释放锁。
==================================================================================================
    sleep() 与 wait()的比较?
        wait()的作用是让当前线程由“运行状态”进入“等待(阻塞)状态”的同时,也会释放同步锁。
            会将当前线程放入wait set,等待被唤醒,并放弃lock对象上的所有同步声明
            只有通过以下四个方法可以主动唤醒:
                1. notify
                2. notifyAll
                3. Thread.interrupt()
                4. 等待时间过完。
                当线程被唤醒后,线程就从wait set中移除了并且重新获得线程调度能力

        sleep()的作用是也是让当前线程由“运行状态”进入到“休眠(阻塞)状态”的同时,并不会释放锁
==================================================================================================
    join说明
        t1.join(); 则当前线程需要等待t1线程执行结束才能继续执行 (当前线程进行中...t1加入,当前线程等待....t1进行中...t1结束...当前线程继续)
==================================================================================================
    interrupt()说明 见
        interrupt()的作用是中断本线程。
            线程中断自己 是被允许的;其它线程调用该线程的interrupt()方法时,会通过checkAccess()检查权限。这有可能抛出SecurityException异常。

        使得线程是处于阻塞状态:
            1.调用线程的wait()系列方法进入阻塞状态
            2.调用线程的join()系列方法, sleep()系列方法进入阻塞状态

            当且仅当线程在【阻塞状态】时,调用它的interrupt()方法,那么它的“中断标记”会被清除并收到一个InterruptedException异常。

            例如,线程通过wait()进入阻塞状态,此时通过interrupt()中断该线程;之后会立即将该线程的中断标记设为“true”,
            但由于线程处于阻塞状态,该“中断标记”会立即被清除为“false”,同时,会产生一个InterruptedException的异常。

    interrupted() 和 isInterrupted()的区别
        interrupted() 除了返回中断标记之外,它还会清除中断标记(将中断标记设为false);(当且仅当线程在【阻塞状态】时)
        而isInterrupted()仅仅返回中断标记。


    通过中断机制,将阻塞状态的线程打断;这也就是为什么wait和sleep等方法 需要try...catch InterruptedException的异常

==================================================================================================
    过时方法 (使用时容易造成线程死锁)
        suspend : 使线程暂停,但不会释放锁资源;需要等待resume;而准备resume的线程被其他锁锁定时,就会造成死锁
                    (不同于wait和notify机制是因为 它持有锁的情况下进入阻塞状态,而又不像sleep那样有超时机制)
        resume  : 使线程恢复,若之前没有suspend则无效
                    (同上)
        stop    : 停止当前线程 不会保证释放资源
                    (同上)

    代替方案:
        1.中断机制
        2.唤醒机制
        3.volatile标识判断机制

            如何正确的停止一个线程 见StopThreadTest.java
                通过简单修改 volatile变量的值 来指示其目标线程将停止运行的代码



==================================================================================================
    线程优先级介绍
        线程优先级的范围是1~10,默认的优先级是5。“高优先级线程”会优先于“低优先级线程”执行
        java 中有两种线程:用户线程和守护线程。
        可以通过isDaemon()方法来区别它们:如果返回false,则说明该线程是“用户线程”;否则就是“守护线程”。

        每个线程都可以被标记为一个守护进程或非守护进程。
        在一些运行的主线程中创建新的子线程时,子线程的优先级被设置为等于“创建它的主线程的优先级”,
        当且仅当“创建它的主线程是守护线程”时“子线程才会是守护线程”。

        当Java虚拟机启动时,通常有一个单一的非守护线程(该线程通过是通过main()方法启动)。
            JVM会一直运行直到下面的任意一个条件发生,JVM就会终止运行:
            (01) 调用了exit()方法,并且exit()有权限被正常执行。
            (02) 所有的“非守护线程”都死了(即JVM中仅仅只有“守护线程”)。即当只有守护线程运行时,JVM会自动退出。

==================================================================================================
经典生产/消费者模型  [com.kuangye.threaddemo.basis.classic]

==================================================================================================
“JUC原子类”---所谓原子操作,是指操作过程不会被中断,保证数据操作是以原子方式进行的。
==================================================================================================
    1. 基本类型: AtomicInteger, AtomicLong, AtomicBoolean ;
    2. 数组类型: AtomicIntegerArray, AtomicLongArray, AtomicReferenceArray ;
    3. 引用类型: AtomicReference, AtomicStampedRerence, AtomicMarkableReference ;
    4. 对象的属性修改类型: AtomicIntegerFieldUpdater, AtomicLongFieldUpdater, AtomicReferenceFieldUpdater 。

        这些类存在的目的是对相应的数据进行原子操作。