一、synchronized
1、synchronized可以对对象,代码块、方法等加锁。对静态对象、字符串,锁定引用变化等会加锁失败。
2、调用问题。
(1)同步和非同步方法可以同时调用。
(2)一个同步方法可以调用另外一个同步方法,一个线程已经拥有某个对象的锁,再次申请的时候仍然会得到该对象的锁.,也就是说synchronized获得的锁是可重入的。
(3) 子类可以调用父类的同步方法
3、对写操作加锁,对读操作不加锁会导致脏读。
4、程序在执行过程中,如果出现异常,默认情况锁会被释放。在第一个线程中抛出异常,其他线程就会进入同步代码区,有可能会访问到异常产生时的数据。要加入try catch捕获异常。
5、采用细粒度的锁,可以使线程争用时间变短,从而提高效率。同步代码块中的语句越少越好。
注:可以使用AtomXXXX保证原子性
二、ReentrantLock
1、public class ReentrantLock implements Lock, java.io.Serializable
ReentrantLock实现Lock的接口
2、使用tryLock进行尝试锁定,不管锁定与否,方法都将继续执行, 可以根据tryLock的返回值来判定是否锁定, 也可以指定tryLock的时间,由于tryLock(time)抛出异常,所以要注意unclock的处理,必须放到finally中。
3、public ReentrantLock(true) //实现 公平锁
4、lockInterruptibly() //当获取锁时线程被打断会抛出 InterruptedException 。
5、tryLock (long timeout, TimeUnit unit), 如果获取了锁定立即返回true,如果别的线程正持有锁,会等待参数给定的时间,在等待的过程中,如果获取了锁定,就返回true,如果等待超时,返回false;
三、生产者,消费者
public class MyContainer<T> {
final private LinkedList<T> lists = new LinkedList<>();
final private int MAX = 10; // 最多10个元素
private int count = 0;
public synchronized void put(T t) {
while (lists.size() >= MAX) { // 在线程被唤醒的前后都持续检查条件是否被满足
try {
this.wait(); // 永远在多线程间共享的对象(在生产者消费者模型里即缓冲区队列)上使用wait。
} catch (InterruptedException e) {
e.printStackTrace();
}
}
lists.add(t);
++count;
this.notifyAll(); // 通知消费者线程进行消费
}
public synchronized T get() {
T t = null;
while (lists.size() <= 0) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
t = lists.removeFirst();
count--;
this.notifyAll(); // 通知生产者进行生产
return t;
}
public static void main(String[] args) {
MyContainer<String> c = new MyContainer<>();
// 启动生产者线程
for (int i = 0; i < 2; i++) {
new Thread(() -> {
for (int j = 0; j < 25; j++)
c.put(Thread.currentThread().getName() + " " + j);
}, "生产者生产了" + i).start();
}
// 启动消费者线程
for (int i = 0; i < 10; i++) {
new Thread(() -> {
for (int j = 0; j < 5; j++)
System.out.println(c.get());
}, "消费者" + i).start();
}
}
}
四、ThreadLocal
1、ThreadLocal是使用空间换时间,synchronized是使用时间换空间。
2、ThreadLocal通过set和get设置和获取当前线程的值,与其他线程无关。通过ThreadLocalMap保存数据。
五、并发容器
ConcurrentHashMap:分段锁,不会锁住整张表。
ConcurrentSkipListMap:高并发且排序。
CopyOnWriteArrayList:适合写少读多的环境。
CountDownLatch:多线程下实现类似计数的功能。
BlockingQueue:通常用于一个线程生产对象,另外一个线程消费这些对象的场景。
DelayQueue:用于放置实现了Delayed接口的对象,其中的对象只能在其到期时才能从队列中取走。每次从队列里取出来都是最先要过期的元素。
TransferQueue:先找到head 节点,如果 head 节点是匹配的操作,就直接赋值,如果不是,添加到队列中。
六、线程池
1、自定义构造方法,
ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue)。
线程池参数,
- corePoolSize:核心线程数。核心线程会一直存活,即使没有任务需要执行,当线程数小于核心线程数时,即使有线程空闲,线程池也会优先创建新线程处理。设置allowCoreThreadTimeout=true(默认false)时,核心线程会超时关闭。queueCapacity:任务队列容量(阻塞队列)当核心线程数达到最大时,新任务会放在队列中排队等待执行
- maxPoolSize:最大线程数。 当线程数>=corePoolSize,且任务队列已满时。线程池会创建新线程来处理任务, 当线程数=maxPoolSize,且任务队列已满时,线程池会拒绝处理任务而抛出异常
- keepAliveTime:线程空闲时间。 当线程空闲时间达到keepAliveTime时,线程会退出,直到线程数量=corePoolSize
rejectedExecutionHandler:任务拒绝处理器
两种情况会拒绝处理任务:1.当线程数已经达到maxPoolSize,切队列已满,会拒绝新任务。
2.当线程池被调用shutdown()后,会等待线程池里的任务执行完毕,再shutdown。如果在调用shutdown()和线程池真正shutdown之间提交任务,会拒绝新任务
线程池会调用rejectedExecutionHandler来处理这个任务。如果没有设置默认是AbortPolicy,会抛出异常
拒绝策略(直接丢弃,丢弃队列中最老的任务,抛异常,将任务分给线程来执行),
- ThreadPoolExecutor类有几个内部实现类来处理这类情况
- AbortPolicy 丢弃任务,抛运行时异常
- CallerRunsPolicy 执行任务
- DiscardPolicy 忽视,什么都不会发生
- DiscardOldestPolicy 从队列中踢出最先进入队列(最后一个执行)的任务
- 实现RejectedExecutionHandler接口,可自定义处理器
2、newFixedThreadPool构造方法。
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
3、newCachedThreadPool构造方法
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
4、newSingleThreadExecutor构造方法
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
5、newScheduledThreadPool构造方法
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue());
}
6、newWorkStealingPool构造方法
public static ExecutorService newWorkStealingPool() {
return new ForkJoinPool
(Runtime.getRuntime().availableProcessors(),
ForkJoinPool.defaultForkJoinWorkerThreadFactory,
null, true);
}
7、ForkJoinPool.execute(ForkJoinTask<?> task) 分任务执行
ForkJoinTask<V>已知直接子类:CountedCompleter , RecursiveAction , RecursiveTask