- 可以把单线程程序当做在问题域求解的单一实体,每次只能做一件事情。因为只有一个实体,所以永远不用担心诸如“两个实体试图同时使用同一个资源”。
- 并发就可以同时做多件事情了,但是,两个或多个线程彼此互相干涉的问题也就出现了。
不正确地访问资源
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* Created by Panda on 2018/6/4.
*/
//
abstract class IntGenerator{
private volatile boolean canceled=false;;
public abstract int next();
public void cancel(){canceled=true;}
public boolean isCanceled(){return canceled;}
}
public class EvenChencker implements Runnable{
private IntGenerator intGenerator;
private final int id;
public EvenChencker(IntGenerator intGenerator, int id) {
this.intGenerator = intGenerator;
this.id = id;
}
@Override
public void run() {
while (!intGenerator.isCanceled()){
int val=intGenerator.next();
if(val%2!=0){
System.out.println(val+" not even");
intGenerator.cancel();
}
}
}
public static void test(IntGenerator intGenerator,int count){
System.out.println("press control-C to exit");
ExecutorService executorService = Executors.newCachedThreadPool();
for (int i = 0; i <count ; i++) {
executorService.execute(new EvenChencker(intGenerator,i));
}
executorService.shutdown();
}
public static void test(IntGenerator generator){
test(generator,10);
}
}
/**
* Created by Panda on 2018/6/4.
*/
public class EvenGenerator extends IntGenerator {
private int currentEvenValue=0;
@Override
public int next() {
++currentEvenValue; //Danger point here!!!!
++currentEvenValue;
return currentEvenValue;
}
public static void main(String[] args) {
EvenChencker.test(new EvenGenerator());
}
}
- 这个程序最终将失败,因为各个EvenChecker任务在EvenGenerator处于“不恰当的”状态时,仍能够访问其中的信息。递增不是原子操作。
解决共享资源竞争
- 基本上所有的并发模式在解决线程冲突问题的时候,都是采用序列化访问资源的方案。这意味着在给定时刻只允许一个任务访问共享资源。通常这是通过在代码前面加上一条锁语句来实现的,这就使得在一段时间内只有一个任务可以运行这段代码。因为锁语句产出了一种互相排斥的效果,所以这种机制常常称为互斥量。
- 如果正在写一个变量,它可能接下来将被另一个线程读取,或者正在读取一个上一次已经被另一个线程写过的变量,那么必须使用同步,并且,读写线程都必须用相同的监视器锁同步。
- 当使用synchronized关键字时,需要写的代码量更少,并且用户错误出现的可能性也会降低,因此通常只有在解决特殊问题时,才使用显示的Lock对象。
- synchronized关键字不能尝试着获取锁且最终获取锁会失败,或者尝试着获取锁一段时间,然后放弃它,要实现这些,必须使用concurrent类库。
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
/**
* Created by Panda on 2018/6/5.
*/
public class AttemptLocking {
private ReentrantLock lock = new ReentrantLock();
public void untimed(){
boolean captured=lock.tryLock();
try{
System.out.println("tryLock(): "+captured);
}finally {
if(captured)
lock.unlock();
}
}
public void timed(){
boolean captured=false;
try{
captured=lock.tryLock(2, TimeUnit.SECONDS);
}catch (InterruptedException e){
throw new RuntimeException();
}
try{
System.out.println("tryLock(2,TimeUnit.SECONDS");
}finally {
if(captured)
lock.unlock();
}
}
public static void main(String[] args) {
final AttemptLocking attemptLocking = new AttemptLocking();
attemptLocking.untimed();
attemptLocking.timed();
new Thread(){
{setDaemon(true);}
public void run(){
attemptLocking.lock.lock();
System.out.println("acquired");
}
}.start();
Thread.yield();
attemptLocking.untimed();
attemptLocking.timed();
}
}
原子性与易变性
- 原子操作是不能被线程调度机制中断的操作;一旦操作开始,那么它一定可以在可能发生的“上下文切换”之前(切换到其他线程执行)执行完毕。依赖于原子性是很棘手且很危险的。
/**
* Created by Panda on 2018/6/5.
*/
//原子类 Atomic 类被设计用来构建java.util.concurrent中的类
public class AtomicIntegerTest implements Runnable {
private AtomicInteger atomicInteger = new AtomicInteger(0);
public int getValue(){return atomicInteger.get();}
private void evenIncreament(){atomicInteger.addAndGet(2);}
@Override
public void run() {
while (true){
evenIncreament();
}
}
public static void main(String[] args) {
new Timer().schedule(new TimerTask(){
public void run(){
System.out.println("Aborting");
System.exit(0);
}
},5000);
ExecutorService executorService = Executors.newCachedThreadPool();
AtomicIntegerTest atomicIntegerTest = new AtomicIntegerTest();
executorService.execute(atomicIntegerTest);
while(true){
int val=atomicIntegerTest.getValue();
if(val%2!=0){
System.out.println(val);
System.exit(0);
}
}
}
}
临界区
- 只是希望防止多个线程同时访问方法内部的部分代码而不是防止访问整个方法。通过这种方法分离出来的代码段被称为临界区。
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
/**
* Created by Panda on 2018/6/5.
*/
//比较两种同步控制的方法
class Pair{
private int x,y;
public Pair(int x, int y) {
this.x = x;
this.y = y;
}
public Pair(){this(0,0);}
public int getX() {
return x;
}
public int getY() {
return y;
}
public void incrementX(){x++;}
public void incrementY(){y++;}
public String toString(){return "x: "+x+",y: "+y;}
public class PairValuesNotEqualException extends RuntimeException{
public PairValuesNotEqualException() {
super("pair values not equal: "+Pair.this);
}
}
public void checkState(){
if(x!=y) throw new PairValuesNotEqualException();
}
}
abstract class PairManage{
AtomicInteger atomicInteger=new AtomicInteger(0);
protected Pair pair = new Pair();
private List<Pair> pairs= Collections.synchronizedList(new ArrayList<Pair>());
public synchronized Pair getPair(){
return new Pair(pair.getX(),pair.getY());
}
protected void store(Pair pair){
pairs.add(pair);
try{
TimeUnit.MILLISECONDS.sleep(50);
}catch (InterruptedException e){
}
}
public abstract void increment();
}
class PairManager1 extends PairManage{
public synchronized void increment(){
pair.incrementX();
pair.incrementY();
store(getPair());
}
}
class PairManager2 extends PairManage{
@Override
public void increment() {
Pair temp;
synchronized (this){
pair.incrementX();
pair.incrementY();
temp=getPair();
}
store(pair);
}
}
class PairManipulator implements Runnable{
private PairManage pairManage;
public PairManipulator(PairManage pairManage) {
this.pairManage = pairManage;
}
@Override
public void run() {
while (true)
pairManage.increment();
}
public String toString(){
return "Pair: "+pairManage.getPair()+"checkCounter: "+pairManage.atomicInteger.get();
}
}
class PairCheck implements Runnable{
private PairManage pairManage;
public PairCheck(PairManage pairManage) {
this.pairManage = pairManage;
}
@Override
public void run() {
while (true){
pairManage.atomicInteger.incrementAndGet();
pairManage.getPair().checkState();
}
}
}
public class CriticalSection {
static void testApproaches(PairManage pairManage1,PairManage pairManage2){
ExecutorService executorService = Executors.newCachedThreadPool();
PairManipulator pairManipulator1=new PairManipulator(pairManage1);
PairManipulator pairManipulator2=new PairManipulator(pairManage2);
PairCheck pairCheck1=new PairCheck(pairManage1);
PairCheck pairCheck2=new PairCheck(pairManage2);
executorService.execute(pairManipulator1);
executorService.execute(pairManipulator2);
executorService.execute(pairCheck1);
executorService.execute(pairCheck2);
try{
TimeUnit.MILLISECONDS.sleep(500);
}catch (InterruptedException e){
System.out.println("Sleep interrupted");
}
System.out.println("pm1; "+pairManipulator1+"\npm2: "+pairManipulator2);
System.exit(0);
}
public static void main(String[] args) {
PairManage pairManage = new PairManager1();
PairManage pairManage1=new PairManager2();
testApproaches(pairManage,pairManage1);
}
}
线程本地存储
- 线程本地存储是一种自动化机制,可以为使用相同变量的每个不同的线程都创建不同的存储。
import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
/**
* Created by Panda on 2018/6/5.
*/
class Accessor implements Runnable{
private final int id;
public Accessor(int id) {
this.id = id;
}
@Override
public void run() {
while (!Thread.currentThread().isInterrupted()){
ThreadLocalVariableHolder.increment();
System.out.println(this);
Thread.yield();
}
}
public String toString(){
return "#"+id+": "+ThreadLocalVariableHolder.get();
}
}
public class ThreadLocalVariableHolder {
private static ThreadLocal<Integer> value=new ThreadLocal<Integer>(){
private Random random=new Random(47);
protected synchronized Integer initialValue(){
return random.nextInt(10000);
}
};
public static void increment(){
value.set(value.get()+1);
}
public static int get(){return value.get();}
public static void main(String[] args) throws Exception{
ExecutorService executorService = Executors.newCachedThreadPool();
for (int i = 0; i <5 ; i++) {
executorService.execute(new Accessor(i));
}
TimeUnit.SECONDS.sleep(3);
executorService.shutdownNow();
}
}
ThreadLocal对象通常当作静态域存储。在创建ThreadLocal时,只能通过get()和set()方法来访问该对象的内容,其中,get()方法将返回与其线程相关联的对象的副本,而set()会将参数插入到为其线程存储的对象中,并返回存储中原有的对象。increment()和get()方法都不是synchronized的,因为ThreadLocal保证不会出现竞争条件。