1、Runnable, Callable
1. Runnable 及 Thread
class Thread implements Runnable
2. Callable
future模式:Future对象用来存放该线程的返回值以及状态
future.isDone() // 无阻塞
future.get() // 阻塞
ExecutorService threadPool = Executors.newFixedThreadPool(3);
//submit方法有多重参数版本,及支持callable也能够支持runnable接口类型.
Future future = threadPool.submit(new MyCallable());
future.isDone(); // return true / false, 无阻塞
future.get(); // return 返回值,阻塞直到该线程运行结束
2、ExecutorService, ThreadPoolExecutor
ExecutorService threadPool = null;
// threadPool = Executors.newCachedThreadPool();
// threadPool = Executors.newSingleThreadExecutor();
// threadPool = Executors.newFixedThreadPool(3);
// 以上类似的线城池创建方法,内部都是通过ThreadPoolExecutor实现的
// corePoolSize = 1 // 最多维持1个空闲核心线程数
// maximumPoolSize = 128 // 128 * 8M = 1G // 最多开启128个线程,约开销1G内存
// keepAliveTime = 60 // 60秒即每1分钟检查一次空闲线程
threadPool = new ThreadPoolExecutor(1, 128, 60L, TimeUnit.SECONDS, new SynchronousQueue<>());
3、ThreadLocal
绑定当前线程,为每个使用ThreadLocal的线程提供独立的变量副本。
实现:每个Thread都持有一个TreadLocalMap类型的变量
threadLocall.set(T value); // 操作当前Thread的TreadLocalMap,
// key存放ThreadLocal, value存放目标变量
threadLocall.get(); // 返回目标变量
4、原子类(AtomicInteger、AtomicBoolean……)
// CAS无锁算法,(Compare and swap)比较与交换,是一种乐观锁机制
/**
* Atomically sets the value to the given updated value
* if the current value {@code ==} the expected value
/
AtomicInteger.compareAndSet(int expect, int update) //返回值为boolean
5、volatile
volatile能保证被修饰的变量对所有线程可见,因此在使用volatile修饰的变量时不会发生上下文切换和线程调度等操作。但volatile只能保证变量对各个线程的可见性,不能保证原子性,并且在线程安全的情况下加volatile会牺牲性能。
当变量被volatile关键字修饰后,线程执行引擎就会去主内存中去读取变量值,同时主内存会把改变的变量值更新到线程工作内存当中
6、synchronized, wait, notify
wait / notify是Object类提供的方法,即所有对象都有这两个方法。使用时注意:
1.wait和notify必须配合synchronized关键字使用
2.wait方法释放锁,notify方法不释放锁。
/**
* 生产者生产出来的产品交给店员
*/
public synchronized void produce() {
if(this.product >= MAX_PRODUCT) {
try {
wait();
System.out.println("产品已满,请稍候再生产");
} catch(InterruptedException e) {
e.printStackTrace();
}
return;
}
this.product++;
log.info("生产者生产第" + this.product + "个产品.");
notifyAll(); // 通知等待区的消费者可以取出产品了
}
/**
* 消费者从店员取产品
*/
public synchronized void consume() {
if(this.product <= MIN_PRODUCT) {
try {
wait();
System.out.println("缺货,稍候再取");
} catch (InterruptedException e) {
e.printStackTrace();
}
return;
}
log.info("消费者取走了第" + this.product + "个产品.");
this.product--;
notifyAll(); // 通知等待去的生产者可以生产产品了
}
7、ReentrantLock
可重入锁,即已经持有锁的线程可以再次持有,并且要释放对等的次数后才真正释放该锁
// 可重入锁
ReentrantLock lock = new ReentrantLock();
try {
lock.lock(); // 加锁
// lock.tryLock(1, TimeUnit.SECONDS); // 设置超时时间
// lock.lockInterruptibly(); // 可打断锁
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock(); // 释放锁
}
// 可重入读写锁 (两者都有lock,unlock方法。写写,写读互斥;读读不互斥)
ReentrantReadWriteLock rw = new ReentrantReadWriteLock();
ReentrantReadWriteLock.ReadLock r = rw.readLock();
ReentrantReadWriteLock.WriteLock w = rw.writeLock();
8、ConcurrentHashMap
JDK1.7:
static final class Segment<K,V> extends ReentrantLock implements Serializable
分段锁Segment,不会像 HashTable 那样不管是 put 还是 get 操作都需要做同步处理,每当一个线程占用锁访问一个 Segment 时,不会影响到其他的 Segment。
因此ConcurrentHashMap性能由于HashTable
JDK1.8:
废弃分段锁,采用CAS + synchronized + volatile
9、COW容器
CopyOnWrite容器即写时复制的容器。通俗的理解是当我们往一个容器添加元素的时候,不直接往容器添加,而是先将当前容器的引用指向新的容器。这样做的好处是我们可以对CopyOnWrite容器进行并发的读,而不需要加锁,因为当前容器不会添加任何元素。所以CopyOnWrite容器也是一种读写分离的思想,读和写在不同的容器。
当第一个写请求被容器收到后,容器复制出一个副本出来,线程一去这个副本修改数据,其它线程也要修改容器数据,那就只能在副本容器外排队等候,因为容器的写操作是加锁的。
CopyOnWrite容器特别适用于读多写少的场景