参考之前的总结: 多线程-创建线程的三种方式
对比三种方式:
第1种方式无法继承其他类,第2,3种可以继承其他类;
第2,3种方式多线程可以共享同一个target对象,多个线程处理同一个资源;
一般使用第2,3种方式创建线程。
1.新建(new) 2.就绪(start) 3.运行(获得cpu资源) 4.阻塞(sleep,IO阻塞等)4.死亡(执行完成,Exception/Error)
join() :Thread对象调用,线程A调用join(),其他线程被阻塞,直到线程A执行完为止。
setDaemon(true) : Thread对象调用,设置成后台线程; 当所有前台线程都死亡,后台线程自动死亡。
多个线程访问同一个数据时(线程调度的不确定性),很容易出现线程安全问题。解决办法:引入同步监视器,任意时刻只能有一个线程获得对同步监视器的锁定,当同步代码块执行结束后,该线程释放对该同步监视器的锁定。
“加锁”—>”修改共享资源”->”释放锁”
// obj就是同步监视器
synchronized(obj){
同步代码块
}
使用synchronized修饰某个方法,则该方法称为同步方法,同步方法的同步监视器是this;
只需对会改变竞争资源的方法进行同步。
任何线程在进入同步代码块或同步方法前,必须获得同步监视器的锁定。
释放同步监视器的锁定的时机:
Lock对共享资源的独占访问,每次只能一个线程对Lock对象加锁。
Lock,ReadWriteLock接口,对应的实现类ReentrantLock(可重入锁),ReentrantReadWriteLock。
class A{
private final ReentrantLock lock = ReentrantLock();
public void method(){
// 加锁
lock.lock();
try{
// 修改共享资源
}finally{
// 释放锁
lock.unlock();
}
}
}
两个线程互相等待对方释放同步监视器,就发生了死锁。
在系统启动时,创建大量空闲的线程,将一个Runnable对象或Callable对象传给线程池,线程池会启动一个线程执行对应的run()或call(),当run()或call()执行完成后,该线程返回到线程池成为空闲状态,等待下个Runnable对象或Callable对象。
Executors工厂类,提供如下静态方法创建不同的线程池:
// 具有缓存功能的线程池,系统根据需要创建线程,这些线程会被缓存在线程池中
newCachedThreadPool()
// 固定数量,可重用的线程池
newFixedThreadPool(int nThreads)
// 单线程的线程池
newSingleThreadExecutor()
// 指定线程数量,并可指定延迟时间才执行线程任务
newScheduleThreadPool(int corePoolSize)
// 单线程,并可指定延迟时间才执行线程任务
newSingleThreadScheduleExecutor()
前三个静态方法return ExecutorService
后两个静态方法return ScheduledExecutorService
ExecutorService代表线程池,提供3个方法:
// 将一个Runnable对象提交给线程池,Future返回null
Future> submit(Runnable task)
// 将一个Runnable对象提交给线程池,Future返回指定结果result
Future submit(Runnable task, T result)
// 将一个Callable对象提交给线程池,Future返回Callable对象中call()方法
Future submit(Callable task)
步骤:
线程局部变量,把数据放在ThreadLocal中,就可以为每个线程创建一个该变量的副本,避免并发访问的线程安全问题;
private ThreadLocal threadLocal = new ThreadLocal<>();
// 当前线程副本中的值
T get();
// 删除
void remove();
// 设置当前线程副本中的值
void set(T value);
与同步机制的区别:
同步机制是为了同步多个线程对共享资源的并发访问,是多线程间通信的有限方式;
ThreadLocal隔离多个线程的数据共享,避免多个线程间对共享资源的竞争,不需要对多个线程进行同步。
通过Collections包装成线程安全集合:
ArrayList, LinkedList, HashSet, TreeSet, HashMap, TreeMap等都是线程不安全;如果多个线程对这些集合读,写时,会破坏这些集合数据的完整性。
Collections提供静态方法将这些集合包装成线程安全的集合。
synchronizedCollection(Collection<T> c)
synchronizedList<List<T> list>
synchronizedMap(Map<K, V> map)
synchronizedSet(Set<T> set)
HashMap m = Collections.synchronizedMap(new HashMap);
线程安全集合:
ConcurrentHashMap, ConcurrentSkipListMap, ConcurrentSkipListSet, ConcurrentLinkedQueue, ConcurrentLinkedDeque
CopyOnWriteArrayList
CopyOnWriteArraySet