并发入门

来源:互联网 发布:优酷网络剧燃血女神 编辑:程序博客网 时间:2024/06/05 17:50

引例

如下:
    private int a;    private void test() {        ExecutorService pool = Executors.newFixedThreadPool(10);        for(int x = 0;x<10;x++){            pool.execute(new Runnable() {                @Override                public void run() {                    log();                }            });        }    }    private void log(){        a++;        Thread.yield();        a++;        Log.e("TAST","a = "+a+",currentThread ="+ Thread.currentThread());    }

        在上面代码中,正常逻辑下, 每一次输出的应该都是偶数。但涉及多线程时,有些时候会输出奇数。

yield()

        当前线程释放CPU资源,由CPU随机选择一个线程执行——有可能是调用yield()方法的线程。上述代码中调用yield()的主要作用就是调用可能存在的并发问题暴露的机率。

原因

        多个线程访问同一共享性资源,而共享性资源又非原子性操作(如赋值,返回这样的操作是无法打断的,称它们具有原子性,而a++方法是可能被打断的,所以非原子性)。

        上述代码中,多个线程调用了同一个log()方法,这个log()方法就是共享性资源。

synchronized

        修饰方法时:为方法加锁,这样该方法同时只能被一个线程进行操作。同一个对象中,所以sync方法共用同一个锁,一个线程可以多次获取一个对象的锁,因此多个线程也只有一次调用该对象中一个方法。例如t1,t2为两个线程,前者访问sync方法f1,后者访问sync方法f2。如果t1先执行且未执行完毕之前,t2是无法访问到f2的。因为f1与f2的锁一致,t1访问时该锁被锁上了。但t1可以再访问f2。

        可以使用sync对上述代码中的log进行修饰加锁,就会避免并发问题。

Lock

        lock必须被显式的创建、加锁与释放。如,

    private void log(){        lock.lock();//lock是ReentrantLock        try{            a++;            Thread.yield();            a++;            Log.e("TAST","a = "+a+",currentThread ="+ Thread.currentThread());        }finally {            lock.unlock();        }    }

        上述代码中,在finally()中调用lock#unlock()。这就保证了必须会调用到unlock()。

        有一点要注意:如果该方法有返回值的话, 必须放在try中写,用于确保return语句不会过早发生,从而将结果暴露给别的线程

常用方法

        tryLock():尝试获取锁,如果能获取成功就立即返回true,如果不能获取成功就立即返回false。

        tryLock(long,TimeUnit):在指定的时间内如果能获取锁,就返回true,否则返回false。在此过程中,会被intercept掉。

比较

        与sync类似,都是用来进行加锁处理同步问题的。但使用sync关键字,代码更少,并且出错的可能性也会降低。因此通常只在解决特殊问题的时候才使用lock。

        使用lock,使程序拥有更细粒度的控制力。还允许尝试获取锁——结果不一定能获取到。并且在锁内部如果出现异常,还可以在finally中进行清理操作。




0 0
原创粉丝点击