您的位置:首页 > 其它

并发入门

2016-05-22 18:12 148 查看

引例

如下:
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中进行清理操作。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: