public abstract class AbstractCollection implements Collection {
public String toString() {
Iterator it = iterator();
if (! it.hasNext())
return "[]";
StringBuilder sb = new StringBuilder();
sb.append('[');
for (;;) { //死循环
E e = it.next();
sb.append(e == this ? "(this Collection)" : e);
if (! it.hasNext())
return sb.append(']').toString();
sb.append(',').append(' ');
}
}
}
程序清单 5-6 隐藏在字符串连接中的迭代操作(不要这么做)
@NotThreadSafe
public class HiddenIterator {
@GuardedBy("this")
private final Set set = new HashSet<>();
public synchronized void add(Integer i) { set.add(i); }
public synchronized void remove(Integer i) { set.remove(i);}
/**
* 可能会抛出ConcurrentModificationException,容器的toString()方法对容器进行了迭代
* 真正的原因在于HiddenIterator不是线程安全的。在使用println中的set对象之前必须首先获取HiddenIterator的锁,
* 但在调试代码的过程中通常会忽略这个问题
*/
public void addTenThings() {
Random r = new Random();
for(int i = 0; i < 10; i++)
add(r.nextInt());
System.out.println("Debug: add ten elements to + " + set);
}
}
5.2并发容器
5.2.1ConcurrentMap
程序清单5-7 ConcurrentMap 接口
public interface ConcurrentMap extends Map {
//仅当K没有响应的映射值时才插入
V putIfAbsent(K key, V value);
//仅当K被映射到V时才移除
boolean remove(Object key, Object value);
//仅当K被映射到oldValue时才替换为newValue
boolean replace(K key, V oldValue, V newValue);
//仅当K被映射到某个值时才替换为newValue
V replace(K key, V value);
}
package com.everjiankang.miaosha;
import java.io.File;
import java.io.FileFilter;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingDeque;
public class XiaoFeiQueue {
private final static int BOUND = 10;
private final static int N_CONSUMMERS = 10;
public static void main(String[] args) {
File file = new File("/Users/guchunchao/Desktop/深入理解java虚拟机视频1-6");
File[] files = new File[10];
files[0] = file;
startIndexing(files);
}
public static void startIndexing(File[] files) {
BlockingQueue queue = new LinkedBlockingDeque(BOUND);
FileFilter filter = new FileFilter() {
@Override
public boolean accept(File pathname) {
return true;
}
};
//n个线程爬取文件
for(File file : files) {
new Thread(new FileCrawer(queue, filter, file)).start();
}
//10个线程消费
for(int i = 0; i < N_CONSUMMERS; i++) {
new Thread(new Indexer(queue)).start();
}
}
}
5.3.2 串行线程封闭
5.3.3 双端队列与工作密取
5.4 阻塞方法与中断方法
程序清单5-10 回复中断状态以避免屏蔽中断
public class TaskRunnable implements Runnable {
BlockingQueue queue;
.......
public void run() {
try{
processTask(queue.take());
}catch(Exception e) {
Thread.currentThread().intterrupt(); //恢复被中断的状态
}
}
}
5.5 同步工具类
5.5.1闭锁
程序清单5-11 在计时测试中使用CountDownLatch来启动和停止线程
/**
* 测试n个线程并发执行某个人物时需要的时间
* latch 英[lætʃ] n. 门闩; 弹簧锁;
*/
public class TestHarness {
//任务task就一份逻辑,用nThreads个线程并发执行
public long timeTasks(int nThreads, final Runnable task) throws InterruptedException {
final CountDownLatch startGate = new CountDownLatch(1);
final CountDownLatch endGate = new CountDownLatch(nThreads);
for (int i = 0; i < nThreads; i++) {
Thread t = new Thread() {
@Override
public void run() {
try {
startGate.await(); //在每一个工作线程的开始处放一道栏杆,在主线程中控制开启
try {
task.run();
}finally {
endGate.countDown(); //每个线程运行完一个逻辑,关闭门数量就减一
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
t.start();
}
long start = System.nanoTime();
startGate.countDown(); //启动门使得主线程同时释放所有工作线程
endGate.await(); //结束门使主线程等待最后一个线程执行完成,而不是顺序地等待每个线程执行完成
long end = System.nanoTime();
return end - start;
}
}
5.5.2FutureTask
程序清单5-2 使用FutureTask来提前加载稍后需要的数据
package com.multithread.unit05;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
//程序清单5-2 使用FutureTask来提前加载稍后需要的数据
public class Preloader {
private final FutureTask future = new FutureTask<>(new Callable() {
@Override
public ProjectInfo call() throws Exception {
return loadProjectInfo();
}
});
ProjectInfo loadProjectInfo(){
//TODO 从DB加载产品信息
ProjectInfo info = new ProjectInfo("aaa");
return info;
}
private final Thread thread = new Thread(future);
//在构造函数和静态初始化方法中启动线程不是一种好方法,
//因此提供start方法来启动线程
public void start() {
thread.start();
}
public ProjectInfo get() {
try {
return future.get();
} catch (ExecutionException | InterruptedException e) {
return null;
}
}
public static void main(String[] args) {
Preloader p = new Preloader();
p.start();
System.out.println(p.get());
}
}
@Data
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true)
class ProjectInfo{
private String name;
}
在系统运行后,在线程快照里总是看到线程池的名字为pool-xx,这样导致很不好定位,怎么给线程池一个有意义的名字呢。参照ThreadPoolExecutor类的ThreadFactory,自己实现ThreadFactory接口,重写newThread方法即可。参考代码如下:
public class Named
错误: IE 中"HTML Parsing Error:Unable to modify the parent container element before the child element is closed"
现象: 同事之间几个IE 测试情况下,有的报这个错,有的不报。经查询资料后,可归纳以下原因。
PropertyPlaceholderConfigurer是个bean工厂后置处理器的实现,也就是BeanFactoryPostProcessor接口的一个实现。关于BeanFactoryPostProcessor和BeanPostProcessor类似。我会在其他地方介绍。PropertyPlaceholderConfigurer可以将上下文(配置文件)中的属性值放在另一个单独的标准java P
创建一个类
public class ContextInitListener implements ServletContextListener
使得该类成为一个监听器。用于监听整个容器生命周期的,主要是初始化和销毁的。
类创建后要在web.xml配置文件中增加一个简单的监听器配置,即刚才我们定义的类。
<listener>
<des
http://developer.apple.com/iphone/library/qa/qa2009/qa1649.html
Excerpt:
You are getting this warning because you probably added your Info.plist file to your Copy Bundle
hi,
自己在做工程的时候,遇到批量插入数据的数据修复场景。我的思路是在插入前准备一个临时表,临时表的整理就看当时的选择条件了,临时表就是要插入的数据集,最后再批量插入到数据库中。
WITH tempT AS (
SELECT
item_id AS combo_id,
item_id,
now() AS create_date
FROM
a