JDK同步控制工具,JAVA高并发程序设计

来源:互联网 发布:mac number 编辑:程序博客网 时间:2024/06/06 14:07

同步控制是并发程序的重要手段之一,我们平常用过最多的Synchronized就是其中一种简单的方法,此外还有Object.wait(),Object.notify()等方法。在JDK之中,还有其他好用的工具。

重入锁_ReentrantLock:

一个可重入的互斥锁Lock,它具有与使用synchronized方法和语句所访问的隐式监视器锁相同的一些基本行为和语义,但功能更强大。ReentrantLock将由最近成功获得锁,并且还没有释放该锁的线程所拥有。当锁没有被另一个线程所拥有时,调用lock的线程将成功获取该锁并返回。如果当前线程已经拥有该锁,此方法将立即返回。可以使用isHeldByCurrentThread()getHoldCount()方法来检查此情况是否发生。举个栗子:

public class Demo_1 implements Runnable{    public static ReentrantLock lock = new ReentrantLock();    public static int i = 0;    @Override    public void run() {        for (int j = 0; j < 1000000; j++) {            lock.lock();            try {                i++;            } catch (Exception e) {                e.printStackTrace();            }finally {                lock.unlock();            }        }    }    public static void main(String[] args) throws Exception {        Demo_1 A = new Demo_1();        Thread t1 = new Thread(A);        Thread t2 = new Thread(A);        t1.start();t2.start();        t1.join();t2.join();    }}

使用重入锁,在并发时,保护i的安全性。重入锁还有一个特点,跟它的名字一样,可以反复进如入:

for (int j = 0; j < 1000000; j++) {            lock.lock();            lock.lock();            try {                i++;            } catch (Exception e) {                e.printStackTrace();            }finally {                lock.unlock();                lock.unlock();            }        }

跟synchronized相比,重入锁有显示操作,必须手动指定何时加锁和释放锁,对逻辑控制的灵活性要好于synchronized,唯一注意的就是必须释放锁。另外,重入锁还可以提供中断功能。使用synchronized时,一个线程在等待锁,最后结果就两种,要么获得锁,要么一直等待下去,这种情况对于实际业务需求来讲,并不友好。比如你朝思暮想的女朋友来看你了,在这种干柴烈火,风花雪月,月黑风高的时候,你正准备干点什么,女朋友亲戚来了,这时候你难道要一直等下去吗,那肯定不行,当然是停下来,然后开始学习这里写图片描述

public class Demo_2 implements Runnable {    public static ReentrantLock rLockA = new ReentrantLock();    public static ReentrantLock rLockB = new ReentrantLock();    int lock;    public Demo_2(int lock) {        this.lock = lock;    }    @Override    public void run() {        try {            if (lock == 1) {                rLockA.lockInterruptibly();                System.out.println("占用lock="+lock+":"+System.currentTimeMillis());                try {                    Thread.sleep(500);                } catch (Exception e) {                    e.printStackTrace();                }                rLockB.lockInterruptibly();            } else {                System.out.println("占用lock="+lock+":"+System.currentTimeMillis());                rLockB.lockInterruptibly();                try {                    Thread.sleep(500);                } catch (Exception e) {                    e.printStackTrace();                }                rLockA.lockInterruptibly();            }        } catch (Exception e) {            // TODO: handle exception        } finally {            if (rLockA.isHeldByCurrentThread())                rLockA.unlock();            if (rLockB.isHeldByCurrentThread())                rLockB.unlock();            System.out.println(Thread.currentThread().getId() + " :线程退出 \b lock="+lock);        }    }    public static void main(String[] args) throws Exception {        Demo_2 demoA = new Demo_2(1);        Demo_2 demoB = new Demo_2(2);        Thread rA = new Thread(demoA);        Thread rB = new Thread(demoB);        rA.start();rB.start();        Thread.sleep(1000);        rB.interrupt();    }}

当rA和rB同时执行,rA先占用lockA,再占用lockB,而rB先占用lock2,再占用lockA,这样就形成了相互等待。在这里,我们使用了 lockInterruptibly() 这个方法,这是一个可以对中断,休眠的线程进行响应的锁申请操作。

这里写图片描述

在上面的列子中,lockInterruptibly() 的优先作用是响应中断,就好比有个特工深入敌后,随时执行外面给的命令,因为外面的人进不来。最后我们调用了interrupt 方法,来中断rB线程,那么rB就会放弃lockA锁的申请,同时释放lockB的锁,这样rA就可以继续执行下去了。

除了这种等待外部通知外,还提供了一种其他方法:限时等待。顾名思义,就是在指定时间内,线程会一直等待锁的获取,超出时间就不再等待了。这里我们用 tryLock() 来实现:

public class Demo_3 implements Runnable {    public static ReentrantLock rLock = new ReentrantLock();    public Demo_3() {        System.out.println("初始化:"+Thread.currentThread().getId());    }    @Override    public void run() {        try {            if (rLock.tryLock(5, TimeUnit.SECONDS)) {                Thread.sleep(6000);                System.out.println("执行完成:"+Thread.currentThread().getId());            } else {                System.out.println("获取锁失败");            }        } catch (Exception e) {            e.printStackTrace();        } finally {            if (rLock.isHeldByCurrentThread()) {                rLock.unlock();            }        }    }    public static void main(String[] args) {        Demo_3 demo = new Demo_3();        Thread t1 = new Thread(demo);        Thread t2 = new Thread(demo);        t1.start();        t2.start();    }}

tryLock() 的两个参数也很好理解,一个是等待时长,另一个就是时间单位。如果不带参数,就表示不等待,如果当前锁没有被其他线程占用,就直接返回true,反之直接返回false。

Condition 条件:

使用Synchronized的时候,我们可以用wait()和notify()方法来达到控制线程的目的。同样的在重入锁中,也有类似的方法,await(),awaitUninterruptibly()和singal()。

  • await():当前线程等待,同时释放锁,其他线程使用singal()或者singalAll()方法时,重新获得锁并继续执行。当线程中断时,能跳出等待。
  • awaitUninterruptibly():和await()功能差不多,但是它无法等待过程中响应中断。
  • singal():唤醒另一个在等待中的线程。
阅读全文
0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 做了漂唇起泡了怎么办 漂唇之后起泡了怎么办 漂唇后起了水泡怎么办 嘴唇起泡,弄破了怎么办 九个月的宝宝上火了怎么办 8岁儿童嘴唇起泡怎么办 宝宝嘴皮上火起泡了怎么办 上嘴唇起泡肿了怎么办 上嘴唇突然肿了怎么办? 醒来上嘴唇肿了怎么办 嘴巴突然肿了怎么办呢 下嘴唇肿起来了怎么办 上嘴唇肿了起泡怎么办 上火下嘴唇肿了怎么办 上火嘴唇都肿了怎么办 嘴唇起泡后肿了怎么办 嘴唇上有白点颗粒状怎么办 嘴唇缺了一块红怎么办 人得钩端螺旋体怎么办 脖子上有鸡皮肤怎么办 不结婚老了以后怎么办 丁克族老了怎么办知乎 2个月宝宝咳嗽怎么办 干活累的手疼怎么办 脸上长白色的癣怎么办 全身起红斑很痒怎么办 宝宝脖子红烂了怎么办 背上长红斑很痒怎么办 身上起风疙瘩很痒怎么办 身上起小包很痒怎么办 浑身起红包很痒怎么办 手太粗糙怎么办小窍门 小腿长疙瘩很痒怎么办 腿过敏起红疙瘩怎么办 肚子上起红疙瘩很痒怎么办 小蚂蚁咬了肿了怎么办 锦鲤鱼尾巴烂了怎么办 泰迪身上长白毛怎么办 鱼身上有红斑了怎么办 新买锦鲤不吃食怎么办 鱼身上有红血丝怎么办