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位的值,
  • 相关阅读:
    UVa532 Dungeon Master 三维迷宫
    6.4.2 走迷宫
    UVA 439 Knight Moves
    UVa784 Maze Exploration
    UVa657 The die is cast
    UVa572 Oil Deposits DFS求连通块
    UVa10562 Undraw the Trees
    UVa839 Not so Mobile
    327
    UVa699 The Falling Leaves
  • 原文地址:https://www.cnblogs.com/shapeOfMyHeart/p/6667093.html
Copyright © 2011-2022 走看看