并发编程基础小结-共享受限资源
2018-01-03 本文已影响17人
r09er
在多线程中,最容易出现的问题就是资源冲突,多个线程试图同时操作一个对象或方法,就会导致很多意料之外的问题出现.
解决共享资源竞争
你永远不知道一个线程何时在运行.想象一下,你正在去夹起桌上的最后一块食物,当你将要夹起的时候,这块食物突然消失了,因为你的线程被挂起了,而另一个人进入并吃掉了他.这正是编写并发程序时需要处理的问题.防止这种冲突的方法就是当资源被一个任务使用时,在其加上锁.
基本上所有的并发模式在解决线程冲突问题的时候,都是采用序列化访问共享资源的方案,意味着同一时刻只允许一个任务访问共享资源.
Java平台提供的方案
- 1 synchronized关键字是Java为防止资源冲突提供的内置支持,当执行被synchronized关键字修饰的代码片段时,它将检查锁是否可用,然后获取锁,执行代码,释放锁.保证在一定时间段内只有一个线程操作该方法.
- i.对象锁synchronized(Object obj),持有不同锁之间的方法不会被阻塞.
如果使用synchronized(this),效果和方法锁是一样的,都会将整个类的所有同步方法都锁住直到释放锁
- ii.方法锁修饰方法,synchronized method()
如果使用synchronized修饰方法,会将整个类的所有同步方法都锁住,因为使用的是当前类作为锁,只有当前一个方法调用完毕并释放了锁才能被调用
- 2 使用显示Lock对象,与內建的锁形式相比,代码缺乏优雅性,但是如果操作出现了错误,可以在使用try-finally后将系统维护在正确的状态下.
Lock的使用一般都会配合try-finally,在finally中调用lock.unlock()释放锁.
示例代码
synchronized示例
定义多线程的执行方法,如果遇到不是偶数的情况就会停止
抽象类,定义公共方法
public abstract class IntGenerator {
private volatile boolean canceled = false;
public abstract int next();
public void cancel(){
canceled = true;
}
public boolean isCanceled() {
return canceled;
}
}
为了保证可视性canceled标记是volatile的(它修饰的变量每次被线程访问时都要从主存中重新读取该变量的值,并且当变量的值发生变化时会强迫线程刷新到主存中去),这就保证了canceled标记在被调用了一次之后就会马上变为true
多线程方法实现
public class EvenChecker implements Runnable {
private IntGenerator generator;
private final int id;
public EvenChecker(IntGenerator generator, int id) {
this.generator = generator;
this.id = id;
}
@Override
public void run() {
while (!generator.isCanceled()) {
int val = generator.next();
if (val % 2 != 0) {
System.out.println(val + " 不是偶数");
generator.cancel();
}
}
}
public static void test(IntGenerator gp, int count) {
System.out.println("Press Control-C to exit");
ExecutorService executorService = Executors.newCachedThreadPool();
for (int i = 0; i < count; i++) {
executorService.execute(new EvenChecker(gp, i));
}
executorService.shutdown();
}
加了方法锁的方法
public class SynchronizedGenerator extends IntGenerator{
private int currentEvenValue=0;
@Override
public synchronized int next() {
++currentEvenValue;
Thread.yield();
++currentEvenValue;
return currentEvenValue;
}
}
调用
public static void main(String[] args) {
EvenChecker.test(new SynchronizedGenerator());
}
去掉synchronized后再观察输出
Lock示例
public class MutexEvenGenerator extends IntGenerator{
private int currentEventValue = 0;
private Lock lock = new ReentrantLock();
@Override
public int next() {
lock.lock();
try {
++currentEventValue;
Thread.yield();
++currentEventValue;
return currentEventValue;
}finally {
lock.unlock();
}
}
}
参考资料:Java编程思想