Java并发编程(1)

1.volatile

volatile是轻量级的synchronized,保证线程共享变量的可见性。可见性的意思为当一个线程修改一个共享变量时,另外一个线程能够读到这个修改的值。它不会引起上下文切换和调度。底层原理,在对volatile修饰的变量进行写操作时,jvm会向处理器发送前缀为lock的指令,将这个变量写回到系统主内存,其他处理器通过嗅探发现自己的的缓存行对应的主内存地址被修改时,将其设置为无效状态,重新从系统内存读取。

2.synchronized

synchronized实现同步的基础:java的每一个对象都可以作为锁。a.普通同步方法,锁是当前实例对象。b.静态同步方法,锁是当前类的Class对象。c.同步方法块。锁是Synchonized括号内配置的对象。JVM基于进入和退出Monitor监视器来实现同步。每一个对象都有一个monitor与之关联,线程对object(synchronized修饰的)访问,必须先获取object的监视器。对象的内存分布:对象头,对象实例,对齐填充。在对象头中,mark word里储存的数据随着锁标记位的变化而变化,当锁标志位10时,为重量级锁,mark word里有指向monitor的指针。

Monitor是基于C++ObjectMonitor类实现的,主要成员:_owner: 指向持有ObjectMonitor对象的线程。_waitSet::存放处于wait状态的线程队列。_EntryList:存放处于等待锁的block状态的线程队列。

锁的状态有:(hashcode,01)无锁状态;(线程id,01)偏向锁状态(总是同一个线程获取,不需要进行加锁和解锁操作。需要判断markword里存储是否是当前线程,如果成功,则获取锁。失败则看锁标识不是1则,cas竞争锁,为1则偏向锁指向当前的线程);(锁记录,00)轻量级锁状态(会在当前线程创建用于储存锁记录的的空间,将对象头的mark word复制到锁记录,然后尝试使用cas将对象头指向锁记录的指针,成功则获取锁,失败自旋。)和(10)重量级锁状态。

        偏向锁 不需要加锁和解锁的消耗,使用于一个线程访问同步块。轻量级锁,线程不会阻塞,但得不到锁的线程会自旋消耗cpu,同步代码块执行速度非常块。重量级锁,线程不用自旋,但会阻塞,同步代码执行速度长,适用于追求吞吐量。

3.cas

        cas利用处理器提供的CMPXCHG指定实现的。判断内存中的值是否等于预期值,如果是则更改为新值,这个过程是原子的。

4.线程状态

        NEW,线程被构建,没有调start()方法。

        RUNABLE,运行状态(操作系统中就绪和运行)。

        BLOCKED,阻塞状态,线程被锁阻塞。

        WAITING,等待状态,该状态需要其他线程做出一些动作(通知或中断)。

        TIME_WAITING,超时等待状态,该状态可以在指定时间内返回。

        TERMINATED,中止状态,该状态表明线程以停止运行。

        Daemon守护线程(与用户线程的结束而结束),其Daemon属性需要在线程启动前设置。

5. 打断

            Thread.interrupt() 给线程设置一个中断标志位,interrupte对于sleep,wait状态的线程进行interrupt并抛出InterruptedException异常,同时擦除中断标志位。

            Thread.interrupted() 检查标志位返回true同时会清除标志位。

            Thread.isInterrupted() 测试线程是否已经被中断。

            suspend() 调用后,线程不会释放已经占有的资源(比如锁),引发死锁问题。stop()方法在终结一个线程时,不能保证线程资源正常释放。

6.Lock接口

6.1  重入锁ReentrantLock

        重入(线程在获取锁之后能够再次获取锁,而不会被阻塞);支持公平和非公平(等待时间最长的线程优先获取锁);可中断(正在获取锁的线程可以被中断);条件变量;获取锁超时。使用方式:

ReentrantLock lock = new ReentrantLock(); //默认非公平锁
//放在try块之外,防止获取锁时发生异常调用unlock()
lock.lock();
try {
   //执行逻辑
}finally {
   lock.unlock();
}

        ReentrantLock的内部类 ConditionObject。每一个ConditionObject对象包含一个等待队列。

class ConditionObject {
    //等待队列的首节点,该节点的waitStatus=-2,nextWaiter指向下一个节点
    Node firstWaiter;
    //等待队列的尾节点,只有一个尾节点
    Node lastWaiter;

    await();//当前线程进入等待状态直到被通知或中断
    awaitUninterruptibly();//当前线程进入等待状态直到被通知,对打断不敏感
    long awaitNanos(long nanosTimeout);//当前线程进入等待状态直到被通知、中断、时间超时
    boolean awaitUntil(Date deadline);//当前线程进入等待状态直到被通知、中断、特定时间
    void signal();//唤醒一个等待在Condition的线程
    void signalAll();//唤醒所有个等待在Condition的线程
    
}
ReentrantLock reentrantLock = new ReentrantLock();
Condition condition = reentrantLock.newCondition();

6.2  ReadWriteLock

  读写锁支持重入、公平非公平。写锁是排他锁。

  读写锁状态的确定,当前的同步状态值S:写状态S&0X0000FFFF将高位16抹去(写+1,S+1);      读状态等于S>>>16(读+1,S+(1<<16==10000))。

  当一个线程已经获取了读锁,当前线程要获取写锁则需要进入等待状态。

  当前线程获取了写锁,则其他读线程cas自旋获取锁。

  当前线程可以先获取写锁,再获取读锁,然后释放(先获取读锁,再获取写锁会阻塞)。锁降级(写锁降级读锁):先行持有写锁的情况下,再获取读锁,随后先释放写锁的过程。

ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
        ReentrantReadWriteLock.ReadLock readLock = lock.readLock();
        ReentrantReadWriteLock.WriteLock writeLock = lock.writeLock();
        
        //先获取写锁
        writeLock.lock();
        try{
            //修改操作
            
            //获取读锁
            //如果没有读锁的获取,直接释放写锁。同时另一个线程(写线程)进入修该了值,则该线程无 
            //法感知到
            //有了读锁的获取。会阻塞其他线程(写线程),等读锁释放之后,其他线程才可以获取写锁
            readLock.lock();
        }finally {
            //释放写锁
            writeLock.unlock();
        }
        try{
            //读取操作
        }finally {
            //释放读锁,其他写线程可以获取锁
            readLock.unlock();
        }

7.队列同步器 AbstractQueuedSynchronizer

        AbstractQueuedSynchronizer 有一个属性state,标识当前同步状态。

        同步队列来完成同步状态的管理。当前线程获取同步状态失败时,同步器会将当前线程及等待状态信息加入同步队列。同步器拥有首节点head和尾节点tail。下面是节点类:

class Node {

        //标识当前节点是共享节点,还是独占节点
        static final Node SHARED = new Node();
        static final Node EXCLUSIVE = null;
        
        //在同步器中 首节点head的prev必为空,尾节点tail的next必为空 && head节点不储存线程实例
        volatile Node prev;
        volatile Node next;
        
        //节点的后继节点,由Condition对象的await()调用的线程。    
        Node nextWaiter;
        
        //等待队列,会被移动到同步队列中。 当前节点储存的线程(WAITING或TIME_WAITING)
        volatile Thread thread;
        
        // 0:为初始状态;1:同步队列中等待的线程等待超时或被中断,需要从同步队列中取消;
        // -1:当前节点的下一个节点处于等待状态; -2 当前节点在等待队列中,其他线程调Condition.
        // signal()方法后该节点会从等待队列中转移到同步队列中;
        // -3 下一次共享式同步状态获取将会无条件地传播下去。
        volatile int waitStatus;
}

          独占锁的获取与释放    

//独占锁操作
void acquire(int arg);//获取同步状态成功。失败则加入同步队列
void acquireInterruptibly(int arg);获取同步状态成功。失败则加入同步队列。会抛异常
boolean tryAcquireNanos(int arg, long nanosTimeout);//在超时时间内,获取则成功。
boolean release(int arg)//释放同步状态。激活后驱线程
//共享锁操作
void acquireShared(int arg);//同一时间会有多个线程,获取同步状态。
void acquireSharedInterruptibly(int arg);
boolean tryAcquireSharedNanos(int arg, long nanosTimeout);
boolean releaseShared(int arg)

                1.一个线程获取锁失败,会通过(cas机制)compareAndSetTail()方法添加到同步队列的尾部。并自旋,头节点才能唤醒后驱节点或线程被打断。移出队列时,前提是头节点,且成功获取锁。释放锁时,会唤醒后驱节点。

8.阻塞队列

        阻塞队列。支持阻塞的插入方法,队列满时,队列会阻塞插入元素的线程,直到队列不满。

 支持阻塞的移除方法,队列空时,队列会阻塞移除方法或获取元素的方法的线程,直到队列有元素。

        队列常见方法:添加方法: add(e),抛出异常;offer(e),返回特殊值;put(e),阻塞线程。

移除方法:remove(),抛出异常;poll(),返回特殊值;take(),阻塞线程。

检查方法:element(),peek()都会返回队首元素,但不会移除元素,区别队列空时,前一个报异常,后一个方法返回null。

        1.ArrayBlockingQueue 是用数组实现的有界阻塞队列,用单参构造函数(没有无参构造哈桉树)初始化队列大小,符合FIFO原则。

        2.LinkedBlockingQueue 是一个用链表实现的有界阻塞队列,无参构造函数初始化队列大小(Integer.MAX_VALUE)。

        3.PriorityBlockingQueue是一个支持优先级的无界阻塞队列,默认采取自然顺序。但不能保证同优先级元素的顺序。无参构造函数初始化队列大小(10)。

        4.DelayQueue是一个支持延时获取元素的无界阻塞队列。队列中的元素必须实现Delayed接口,只有在延时期满时才能从队列中提取元素。

        5.SynchronousQueue是一个不储存元素的阻塞队列。每一个put操作必须等待一个take操作,否则不能继续添加元素。

        6.LinkedTransferQueue是由链表构成的无界阻塞TransferQueue队列。

        transfer方法,如果有消费者使用take()或poll()方法,transfer方法会将生产者元素立即传输给消费者。如果没有消费者,transfer方法会将元素放在队列tail节点,等待该元素被消费了,才返回。

        tryTransfer()方法用于试探生产者传入的元素是否能直接传给消费者。如果没有消费者接受元素,返回false。transfer方法必须等待消费者消费了才返回。

[1]: Java 并发编程的艺术 方腾飞 魏鹏 程晓明 著

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值