最近部门领导出了些 Java 并发相关的题目,让我们业余做,提高技术。


我做了题目,第一个提交答案,没想到误导了一帮兄弟。哈哈,原来我才是最大的坑!当然,坑了同事,不能坑大家。


坑 1

启动多个线程为了等待任务结果,使用了 Thread 类的 join 方法,源码如下

long s1 = System.currentfor (int i = 0; i < 100; i++) {    Thread t = new Thread();    t.start();    t.join();}

问题就出在了第 5 行,启动完线程,就等待结果,阻塞了后面线程的启动。



坑 2

ThreadPoolExecutor 使用第一个构造函数

  //缓冲任务队列  private ArrayBlockingQueue<Runnable> taskQueue = new ArrayBlockingQueue<>(5);
 private static final int corePoolSize = 2; //核心线程数  private static final int maximumPoolSize = 5; //最大线程数  private static final int keepAliveTime = 0//当前线程数大于核心线程数时,多余空闲线程在终止前等待新任务的最长时间
 private ThreadPoolExecutor executor = new ThreadPoolExecutor(corePoolSize,      maximumPoolSize, keepAliveTime, TimeUnit.SECONDS, taskQueue);

这个构造函数默认 AbortPolicy 拒绝策略,然后领导的电脑执行报错,任务被拒绝。在生产环境一定要考虑清楚线程池的拒绝策略,尤其是对任务的丢弃,会不会造成很大影响,任务提交状态和执行状态一定要有监控。不然最后就是失控状态...


遂,加了任务提交失败的记录与重新提交。

private List<FactorialCalculator> rejectedTasks = new ArrayList<>();
try {      Future<Long> future = executor.submit(task);      futures.add(future)catch (RejectedExecutionException e) {      System.out.println("task cal factorial " + i + "! task be rejected");      rejectedTasks.add(task);}
List<Future<Long>> futures = new ArrayList<>(10);Iterator<FactorialCalculator> iterator = rejectedTasks.iterator();while (iterator.hasNext()) {  FactorialCalculator task = iterator.next();  Thread.sleep(delay);  try {    Future<Long> future = executor.submit(task);    futures.add(future);    iterator.remove();    System.out.println("rejected task : " + task + "重新提交成功");  } catch (RejectedExecutionException e) {  }}



坑 3、

类似坑 1,实际在串行,还以为在并发。

for (int i = 1; i <= num; i++) {  FactorialCalculator task = new FactorialCalculator(i);  Future<Long> future = executor.submit(task);  result += future.get();}



坑 4、

使用了  AtomicLongFieldUpdater 的 compareAndSet 方法,以为此方法会自旋到更新成功,其实是想用 incrementAndGet 方法。使用 compareAndSet 方法需要自己控制自旋更新值。

private volatile long counter = 0L;
private static AtomicLongFieldUpdater<Counter> updater = AtomicLongFieldUpdater.newUpdater(Counter.class, "counter");
/** * 增加1 */public void increase() {  updater.incrementAndGet(this);}
/** * 增加指定数值 */public void increase(long delta) {  while (!updater.compareAndSet(this, this.counter, this.counter + delta)) {  }}


有句玩笑话是:解决并发问题最好的办法就是避免写并发代码。


可见并发代码,极容易出错,而且代码有了问题,自己很难看出。有时候结果是正确的,发现性能很差,原来代码是在串行执行,还另启线程浪费资源。有时候大部分时间结果是正确的,但总会偶尔来几个问题,诡异的出现又诡异的消失,很难追踪定位。


这也许就是并发编程的魅力。



PS:

期待被灰度到公众号的「问答」功能,听听大家的声音。


更多相关文章

  1. 模板方法模式在开源代码中应用
  2. 接了烂代码的项目,怎么玩好?
  3. 组合模式在开源代码中的应用
  4. 享元模式在开源代码中的应用
  5. 多线程三分钟就可以入个门了!
  6. 外观模式在开源代码中的应用
  7. 装饰器模式在开源代码中的应用
  8. 适配器模式在开源代码中的应用
  9. 建造者模式和原型模式在开源代码中的应用

随机推荐

  1. Android ZoomControls的使用
  2. ADT下载地址(含各版本)(转)
  3. 安装Android SDK时遇到Failed to rename
  4. Android与JS互调
  5. android studio 版本修改无效解决方案
  6. Android天气提醒程序源码
  7. [Android]Android Studio设置debug的keys
  8. Android(安卓)4.0 Service Framework
  9. Android设置竖屏
  10. android Style应用