zoukankan      html  css  js  c++  java
  • 多线程编程核心技术日记

    第一章

    1. isInterrupted()interrupt()的区别

        public boolean isInterrupted() {
            return isInterrupted(false);
        }
    
        public static boolean interrupted() {
            return currentThread().isInterrupted(true);
        }
    
        /**
         * Tests if some Thread has been interrupted.  The interrupted state
         * is reset or not based on the value of ClearInterrupted that is
         * passed.
         */
        private native boolean isInterrupted(boolean ClearInterrupted);

    interrupted() 判断当前线程是否有中断标记,并清除中断标记。(jdk的命名也可读性也不好)

    2. Thread.yield()方法

        使当前线程从执行状态(运行状态)变为可执行态(就绪状态)。

     yield()的作用是放弃当前的cpu资源,将它让给其他的任务去占用cpu执行时间,但放弃的时间不确定,有可能刚刚放弃马上就获得了时间片。

    3. 线程的优先级setPriority(int newPriority)

        优先级高的分配的CPU资源会多点

    4. 守护线程

    5. 停止线程

       可以用interrupt()和renturn结合使用

     



    第二章

    1.synchronized 同步语句

    synchronized可以重入不可以继承

    synchronized同步方法 和 synchronized(this)  用this对象作为“对象监视器”来实现同步功能。

    static synchronized同步方法 和 synchronized(class)  用class类作为“对象监视器”来实现同步功能。

    当我们用synchronized(**)来同步时,要注意避免用字符串来作为“对象监视器”(详情见102页)

     2.volatile

    volatile修饰变量,保证在多线程中变量的可见性(这句话可以理解为在一个方法里面每次去取volatile修饰的变量时都是在主内存去取而不是在私有栈去取)

    下面2个图,一个是非volatile修饰,一个是有volatile修饰(很好理解吧)

     下面的代码我觉得是最能解释volatile的可见性。

     isRunning初始化为true

    如果isRunning没有被volatile修饰,当另一个线程修改isRunning为false时,下图还一直循环(因为isRunning取的还是方法栈里面的变量值)

    如果isRunning被volatile修饰,当另一个线程修改isRunning为false时,下图会打印“线程被停止了!”(因为isRunning取的还是主内存的值)

     

    但是要记住,volatile不能保证同步,意思是不能保证原子性。

     



     第三章(线程间通信)

    1.线程是独立的,如果让多个线程之间有联系可以用wait()和notify()来通信,

    当线程要用wait()和notify(),必须要先获取对象锁,才能调用对象的await()和notify()方法。否则会出现java.lang.IllegalMonitorStateException异常。

    2.当一个线程执行wait()之后,线程会停止运行,只能等待其他线程去notify().  (这个不是绝对的,见下面解释)

    当另一个线程执行notify()时,当前线程并不会马上释放锁,只是随机挑选出其中一个wait()的线程,对其发送notify通知,等notify线程执行完同步方法后释放锁。

    await状态的线程才能获得锁去执行代码。

    3.当线程呈wati()状态时,调用线程的interrupt()方法时会出现interruptedException异常

    4.带有参数的awati(long)方法,指如果在long时间内没有被其他线程notify唤醒时,会自己自动唤醒。

    5.wait()方法只能由其他线程的notify去唤醒吗,NO!

    那在什么情况下wait()方法会被唤醒呢,当调用wait()的对象是Thread时,如果这个线程对象在wait之前isAlive是true的时候,wait()会阻塞,

    如果阻塞期间这个线程对象isAlive变为false的时候,wait()会别唤醒。看看下面代码。

    当把Thread.sleep(2000);注释代码打开的时候,wait()会被唤醒。

    public class MainThread  
    {
        public static void main(String[] args) throws InterruptedException
        {
            SubThread sub = new SubThread();
            System.out.println("MainThread----子线程状态=" +sub.getState() + "    isAlive=" + sub.isAlive());
            sub.start();
            Thread.sleep(1000);
            synchronized(sub)
            {
                try{
                    System.out.println("MainThread----子线程状态=" +sub.getState() + "   isAlive=" + sub.isAlive());
                    sub.wait();
                    System.out.println("MainThread----子线程状态=" +sub.getState() + "   isAlive=" + sub.isAlive());
                    System.out.println("MainThread----执行结束");
                }
                catch(InterruptedException e){
                    System.out.println("e" + e);
                }
            }  
        }
        
        public static class SubThread extends Thread  
        {  
            @Override
            public void run()  
            {
                try {
                    System.out.println("SubThread------开始  状态" +getState() + "        isAlive=" + isAlive());
    //                Thread.sleep(2000);
                    System.out.println("SubThread--------结束");
                } catch (Exception ex) {}
            }  
        }
    }  

     6. 线程的join方法就是上面的原理,(注意:join是线程的方法,wait和notify是Object的方法)

    join方法:使所属的线程对象X正常执行run方法,而是当前线程Z进行无限期的阻塞,等待线程X执行完run方法销毁后再执行Z以后的代码。

    在join过程中,如果当前线程Z被中断,则Z线程出现异常。

    join和sleep的区别:因为join的原理是通过先锁住线程后调用wait方法实现的,所以它释放了线程的锁,而sleep不会释放锁

    如下图,b.join的时候通过wait就释放了监听对象b。

     



    第四章(ReentrantLock)

    1.要实现同步除了可以用synchronized外还可以用RenntrantLock去实现;

    用法就是lock.lock()和lock.unlock(); 其实最底层实现就是通过LockSupport的pack()和unpark(Thread)方法。

    (知识点:当一个A线程park阻塞时,别的线程B除了可以通过unpark(A)去唤醒A,而且可以通过调用A.interrupt()去唤醒A并且不会出现InterruptedException异常)

    2.RenntrantLock类里通过组合AbstractQueuedSynchronizer(AQS)去实现同步。

    RenntrantLock类里有2个AQS(公平和不公平)可提供使用,通过构造器去决定使用那个具体的AQS,默认是不公平的。

    2个AQS区别就是

    公平AQS去lock时要判断队列里面是否有等待的线程,如果有的话就加入到队列最后,

    非公平AQS去lock的时候不用判断队列里是否有线程等待,直接去竞争。如果失败了就加入到队列中去以后就是公平了。

    下面是2个AQS分别获取lock的过程;

    FairSync.lock()

     
        final void lock() {
            acquire(1);
        }
    
        public final void acquire(int arg) {
        //tryAcquire尝试获取锁,如果失败就addWaiter添加到线程队列,
        //acquireQueued会再次去获取锁,如果失败会通过LockSupport.park阻塞 等待队列前一个线程通过unpark去唤醒它
        //selfInterrupt的作用是(以后再说)
            if (!tryAcquire(arg) &&
                acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
                selfInterrupt();
        }
    protected final boolean tryAcquire(int acquires) {
                final Thread current = Thread.currentThread();
                int c = getState();
                if (c == 0) {
                    if (!hasQueuedPredecessors() &&
                        compareAndSetState(0, acquires)) {
                        setExclusiveOwnerThread(current);
                        return true;
                    }
                }
                else if (current == getExclusiveOwnerThread()) {
                    int nextc = c + acquires;
                    if (nextc < 0)
                        throw new Error("Maximum lock count exceeded");
                    setState(nextc);
                    return true;
                }
                return false;
            }
        }

    NonfairSync.lock()

            final void lock() {
                if (compareAndSetState(0, 1))
                    setExclusiveOwnerThread(Thread.currentThread());
                else
                    acquire(1);
            }
            /**
             * 其中非公平锁acquire过程中就tryAcquire跟公平锁不一样
             * 就是上面去掉黄色代码
             */

     详情细节可以参考http://www.cnblogs.com/skywang12345/p/3496147.html

    http://www.cnblogs.com/waterystone/p/4920797.html

    3.AQS原理简单描述

    AQS有个Node节点类,有头有尾形成链表结构wait queue

    那这个链表里面的数据是什么呢,就是当线程去通过lock.lock()时没有获取到锁时,把这个没有获取到锁的线程加入到链表尾部。

    初始化的时候有4个线程0,1,2,3 当第一个线程0获取到锁。 链表结构组成是    (head)new node()-->等待线程1-->等待线程2-->(tail)等待线程3.

    当线程0 调用unlock时,会唤醒头部的下个节点head.next() 即等待线程1。当线程1获取到锁的时候 会把自己设置为头部,同时清理它之前的头部节点即上面的new node().

    这时链表结构变成:(head)线程1-->等待线程2-->(tail)等待线程3.

    同时AQS里面有ConditionObject类,那这个类是干嘛的呢, 这个类不仅有类似object.wait 和 notify的作用。

    同时还有指定唤醒哪个线程的作用(一个lock可以有多个condition,一个condition.signal()只能唤醒跟他相同condition.await()的线程,不同condition之间不能唤醒)

    ConditionObject类里也有个链表结构condition queue

    当获取到锁的线程A调用condition.await()时会创建一个节点加入到condition queue里面;会释放锁同时线程会阻塞。

    如果这个时候另外一个线程B获取锁并通过condition.signal()会立刻唤醒A吗,答案是不一定。

    因为这时如果wait queue里面有别的等待线程C,D..的话的.就不会马上唤醒A,因为condition.signal()会把线程A加入到wait queue的尾部

    4.ReentrantReadWriteLock介绍

    ReentrantReadWriteLock是读写锁,作用是

    2个线程分别是写锁与写锁,互斥

    2个线程分别是读锁与写锁,互斥

    2个线程分别是写锁与读锁,互斥(这里要注意下,写入线程获取锁的同时可以再获取读取锁)

    2个线程分别是读锁与读锁,异步,非互斥。

    独立锁ReentrantLock 通过一个state字段区别锁是否被占用了,那读写锁怎么弄呢?,读锁和写锁应该由2个字段来判断占用的锁是读还是写吧。

    显然现在一个state就不够用了。于是在ReentrantReadWrilteLock里面将这个字段一分为二,高位16位表示共享锁的数量,低位16位表示独占锁的数量(或者重入数量)

    exclusiveCount是指写锁的数量,sharedCount是读锁的数量。

    这里有位运算符,

    x>>>y  表示 x向右移动多少位

    x<<y  表示 x向左移动多少位 

    x & y  表示 x二进制和y二进制 相同的位才合并,比如00010001 & 11111001 = 00010001 

            /*
             * Read vs write count extraction constants and functions.
             * Lock state is logically divided into two unsigned shorts:
             * The lower one representing the exclusive (writer) lock hold count,
             * and the upper the shared (reader) hold count.
             */
    
            static final int SHARED_SHIFT   = 16;
            static final int SHARED_UNIT    = (1 << SHARED_SHIFT);
            static final int MAX_COUNT      = (1 << SHARED_SHIFT) - 1;
            static final int EXCLUSIVE_MASK = (1 << SHARED_SHIFT) - 1;
    
            /** Returns the number of shared holds represented in count  */
            static int sharedCount(int c)    { return c >>> SHARED_SHIFT; }
            /** Returns the number of exclusive holds represented in count  */
            static int exclusiveCount(int c) { return c & EXCLUSIVE_MASK; }
    c >>> 16 右移16位, int 是32位,右移16位就是取前16位的值,
    c & 2的16次 - 1 相对于c & 01111111 11111111 就是取后16位的值,
  • 相关阅读:
    腾讯云分配子域名
    回调函数
    caution
    科普知识图谱
    SQLServer 日期函数大全
    如何进行库存管理?
    SSIS高级转换任务—执行SQL语句
    运行SSIS包的几种方式
    SSIS+CDC 增量抽取数据
    Notepad++中没有Plugin Manager怎么办
  • 原文地址:https://www.cnblogs.com/shapeOfMyHeart/p/6667093.html
Copyright © 2011-2022 走看看