java并发你必须会的编程

1.  Fork/Join框架

 

Fork/Join框架是JDK7提供的一个用于并行执行任务的框架,是一个把大任务切分为若干子任务并行的执行,最终汇总每个小任务后得到大任务结果的框架。我们再通过Fork和Join来理解下Fork/Join框架。Fork就是把一个大任务划分成为若干子任务并行的执行,Join就是合并这些子任务的执行结果,最后得到这个大任务的结果。

使用Fork/Join框架时,首先需要创建一个ForkJoin任务,它提供在任务中执行fork()和join操作的机制。通常情况下,我们不需要直接继承ForkJoinTask,只需要继承它的子类,Fork/Join框架提供了两个子类:RecursiveAction用于没有返回结果的任务;RecursiveTask用于有返回结果的任务。ForkJoinTask需要通过ForkJoinPool来执行。

任务分割出的子任务会添加到当前工作线程所维护的双端队列中,进入队列的头部。当一个工作线程的队列里暂时没有任务时,它会随机从其他工作线程的队列的尾部获取一个任务。(工作窃取算法work-stealing)

import java.util.concurrent.ExecutionException;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
import java.util.concurrent.Future;
import java.util.concurrent.RecursiveTask;


public class CountTask extends RecursiveTask<Integer>{
private static final int THRESHOLD = 10;
private int start;
private int end;

public CountTask(int start,int end){
this.start=start;
this.end=end;
}

protected Integer compute() {
int sum = 0;
boolean canCompute = (end-start) <= THRESHOLD;
        if(canCompute){
            for(int i=start;i<=end;i++){ sum += i; }
        }
        else{
            int middle = (start+end)/2;
            CountTask leftTask = new CountTask(start,middle);
            CountTask rightTask = new CountTask(middle+1,end);
            leftTask.fork();//将任务放入队列并安排异步执行,一个任务应该只调用一次fork()函数,除非已经执行完毕并重新初始化。
            rightTask.fork();//等待计算完成并返回计算结果
            int leftResult = leftTask.join();
            int rightResult = rightTask.join();
            sum = leftResult+rightResult;
        }
        return sum;
}

public static void main(String [] args) throws InterruptedException, ExecutionException{
ForkJoinPool forkJoinPool = new ForkJoinPool();
CountTask task = new CountTask(1,100);
Future<Integer> result = forkJoinPool.submit(task);// RecursiveTask<V> extends ForkJoinTask<V>
System.out.println(result.get());// ForkJoinTask<T> submit(ForkJoinTask<T> task) {

if(task.isCompletedAbnormally()){
         System.out.println(task.getException());
    }
}
}

2. 锁降级

锁降级是指写锁降级成读锁。如果当前线程拥有写锁,然后将其释放,最后获取读锁,这种分段完成的过程不能称之为锁降级。锁降级是指把持住(当前拥有的)写锁,再获取到读锁,最后释放(先前拥有的)写锁的过程。参考下面的示例:

private final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
    private final Lock r = rwl.readLock();
    private final Lock w = rwl.writeLock();
    private volatile static boolean update = false;

    public void processData()
    {
        r.lock();
        if(!update)
        {
            //必须先释放读锁
            r.unlock();
            //锁降级从写锁获取到开始
            w.lock();
            try
            {
                if(!update)
                {
                    //准备数据的流程(略)
                    update = true;
                }
                r.lock();
            }
            finally
            {
                w.unlock();
            }
            //锁降级完成,写锁降级为读锁
        }

        try
        {
            //使用数据的流程(略)
        }
        finally
        {
            r.unlock();
        }
    }

锁降级中的读锁是否有必要呢?答案是必要。主要是为了保证数据的可见性,如果当前线程不获取读锁而是直接释放写锁,假设此刻另一个线程(T)获取了写锁并修改了数据,那么当前线程无法感知线程T的数据更新。如果当前线程获取读锁,即遵循锁降级的步骤,则线程T将会被阻塞,直到当前线程使用数据并释放读锁之后,线程T才能获取写锁进行数据更新。

3 . 实现线程安全的SimpleDateFormat

package ThreadLocalTest;

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class DateUtil {

private static final String DATE_FORMAT = "yyyy-MM-dd HH:mm:ss";
private static ThreadLocal threadLocal = new ThreadLocal(){
  protected synchronized Object initialValue() {
            return new SimpleDateFormat(DATE_FORMAT);
        }
};

    public static DateFormat getDateFormat() {
        return (DateFormat) threadLocal.get();
    }

    public static Date parse(String textDate) throws ParseException {
        return getDateFormat().parse(textDate);
    }
}

<p>System.out.println(DateUtil.getDateFormat().format(newDate()));//输出当前日期</p>

点击打开链接

4.Callable& Future

Callable一般是和ExecutorService配合来使用的

Future就是对于具体的Runnable或者Callable任务的执行结果进行取消、查询是否完成、获取结果。必要时可以通过get方法获取执行结果,该方法会阻塞直到任务返回结果。

也就是说Future提供了三种功能:

1)判断任务是否完成;

2)能够中断任务;

3)能够获取任务执行结果。

import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;


public class CallableAndFuture {

public static void main(String[] args) {
Callable<Integer>callable = new Callable<Integer>() {

@Override
public Integer call() throws Exception {
return new Random().nextInt(50);
}
};

FutureTask<Integer> f = new FutureTask<Integer>(callable);
new Thread(f).start();

try {
Thread.sleep(1000);
System.out.println(f.get());
} catch (InterruptedException e) {
e.printStackTrace();
}catch (ExecutionException e) {
e.printStackTrace();
}

}
}

5. 线程池

ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);  
for (int i = 1; i 10; i++) {  
    final int index = i;  
    fixedThreadPool.execute(new Runnable() {  
         @Override  
         public void run() {  
             String threadName = Thread.currentThread().getName();  
             Log.v("zxy", "线程:"+threadName+",正在执行第" + index + "个任务");  
             try {  
                    Thread.sleep(2000);  
             } catch (InterruptedException e) {  
                    e.printStackTrace();  
             }  
         }  
     });  
 }

点击打开链接

6. 写一个死锁程序

public class Test {

static Object obj1=new Object();
static Object obj2=new Object();

private static void fun1(){
synchronized (obj2) {
synchronized (obj1) {
//业务逻辑
}
}
}

private static void fun2(){
synchronized (obj1) {
synchronized (obj2) {
}
}
}
}
//然后执行的时候只需要开两个线程同时执行fun1和fun2就行了~

一般造成死锁必须同时满足如下4个条件:

1,互斥条件:线程使用的资源必须至少有一个是不能共享的;

2,请求与保持条件:至少有一个线程必须持有一个资源并且正在等待获取一个当前被其它线程持有的资源;

3,非剥夺条件:分配资源不能从相应的线程中被强制剥夺;

4,循环等待条件:第一个线程等待其它线程,后者又在等待第一个线程。

因为要产生死锁,这4个条件必须同时满足,所以要防止死锁的话,只需要破坏其中一个条件即可。

7.  生产者消费者问题

1. 阻塞队列实现:

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.logging.Level;
import java.util.logging.Logger;
 
public class ProducerConsumerPattern {
 
    public static void main(String args[]){
 
     BlockingQueue sharedQueue = new LinkedBlockingQueue();

     Thread prodThread = new Thread(new Producer(sharedQueue));
     Thread consThread = new Thread(new Consumer(sharedQueue));

     prodThread.start();
     consThread.start();
    }
 
}
 
//Producer Class in java
class Producer implements Runnable {
 
    private final BlockingQueue sharedQueue;
 
    public Producer(BlockingQueue sharedQueue) {
        this.sharedQueue = sharedQueue;
    }
 
    @Override
    public void run() {
        for(int i=0; i<10; i++){
            try {
                System.out.println("Produced: " + i);
                sharedQueue.put(i);
            } catch (InterruptedException ex) {
                Logger.getLogger(Producer.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
    }
 
}
 
//Consumer Class in Java
class Consumer implements Runnable{
 
    private final BlockingQueue sharedQueue;
 
    public Consumer (BlockingQueue sharedQueue) {
        this.sharedQueue = sharedQueue;
    }
 
    @Override
    public void run() {
        while(true){
            try {
                System.out.println("Consumed: "+ sharedQueue.take());
            } catch (InterruptedException ex) {
                Logger.getLogger(Consumer.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
    }
 
}

wait\notify实现

public class Dept {

private int capacity;// 容量
private int size;// 数量

public Dept(int capacity) {
this.capacity = capacity;
}

public synchronized void produce(int val) {

try {
int left = val;// left表示“想要生产的数量”
while (left > 0) {
while (size >= capacity)
// 库存已满时,等待“消费者”消费产品。
wait();

// 获取“实际生产的数量”(即库存中新增的数量)
// 如果“库存”+“想要生产的数量”>“总的容量”,则“实际增量”=“总的容量”-“当前容量”。(此时填满仓库)
// 否则“实际增量”=“想要生产的数量”
int inc = (size + left) > capacity ? (capacity - size) : left;
size += inc;
left -= inc;

System.out.printf(
"%s  生产:(%3d) --> 剩余=%3d, 该次生产数量=%3d, 当前仓库数量=%3d\n",
Thread.currentThread().getName(), val, left, inc, size);

notifyAll();// 通知消费者可以消费了

}
} catch (InterruptedException e) {
e.printStackTrace();
}
}

public synchronized void consume(int val) {
try {
// left 表示“客户要消费数量”(有可能消费量太大,库存不够,需多此消费)
int left = val;
while (left > 0) {
// 库存为0时,等待“生产者”生产产品。
while (size <= 0)
wait();
// 获取“实际消费的数量”(即库存中实际减少的数量)
// 如果“库存”<“客户要消费的数量”,则“实际消费量”=“库存”;
// 否则,“实际消费量”=“客户要消费的数量”。
int dec = (size < left) ? size : left;
size -= dec;
left -= dec;
System.out.printf(
"%s 消费(%3d) <-- 需求量=%3d, 实际消费=%3d, 仓库剩余=%3d\n",
Thread.currentThread().getName(), val, left, dec, size);
notifyAll();
}
} catch (InterruptedException e) {
}
}

public String toString() {
return "capacity:" + capacity + ", actual size:" + size;
}
}
public class Producer {

     private Dept dept;

      public Producer(Dept dept) {
          this.dept = dept;
      }

     // 消费产品:新建一个线程向仓库中生产产品。
      public void produce(final int val) {
          new Thread() {
              public void run() {
                  dept.produce(val);
              }
          }.start();
      }
}
public class Customer {

     private Dept dept;

      public Customer(Dept depot) {
          this.dept = depot;
      }

     // 消费产品:新建一个线程从仓库中消费产品。
      public void consume(final int val) {
          new Thread() {
              public void run() {
                  dept.consume(val);
              }
          }.start();
      }
}
public class TestDemo1 {

public static void main(String[] args) throws InterruptedException {

         Dept mDepot = new Dept(100);
         Producer mPro = new Producer(mDepot);
         Customer mCus = new Customer(mDepot);

         mPro.produce(60);

         Thread.sleep(1000);

         mPro.produce(120);
         Thread.sleep(1000);
         mCus.consume(90);
         Thread.sleep(1000);
         mCus.consume(150);
         Thread.sleep(1000);
         mPro.produce(110);
}

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值