[疯狂Java讲义精粹] 第十一章|多线程
1. 线程概述
0. 一个任务通常就是一个程序, 每个运行中的程序就是一个进程. 当一个程序运行时, 内部可能包含了多个顺序执行流, 每个顺序执行流就是一个线程.
1. 进程是系统进行资源分配和调度的一个独立单位. 三个特征:
为毛先输出"主线程..."啊? 6. 线程通信程序不能控制线程的轮换执行, 但可以通过一些机制保证线程协调运行. 6.1 传统的线程通信 0. Object类提供了wait(), notify()和notifyAll()三个方法实现线程通信, 它们不属于Thread类.wait(): 导致当前线程等待, 直到其他线程调用该同步监视器的notify()或notifyAll()方法来唤醒该线程(也可以指定等待时间). * 调用wait()方法会释放当前线程对同步监视器的锁定. notify(): 唤醒在此同步监视器上等待的单个线程. 如果多个线程都是在此同步监视器上等待, 则随机唤醒一个. 只有当前线程放弃对该监视器的锁定后(用wait()方法), 才可以执行被唤醒的线程. notifyAll(): 唤醒在此同步监视器上等待的所有个线程. 只有当前线程放弃对该监视器的锁定后(用wait()方法), 才可以执行被唤醒的线程.
1. wait(), notify()和notifyAll()方法由同步监视器对象来调用:对synchronized修饰的同步方法, 因为默认的实例(this)是同步监视器本身, 所以可以直接使用这三个方法. 对synchronized修饰的同步代码块
2. wait()方法抛出InterreptedException异常, 像sleep()一样. 6.2 使用Condition控制线程通信 0. 如果不使用synchronized关键字保证同步, 则不能使用wait(), notify()和notifyAll()方法进行通信. 使用Lock对象保证同步时, 用Condition类保持协调.
1. Condition实例被绑定在Lock实例上, 要获得它用Lock对象的newCondition()方法即可.
2. Condition的三个方法:await(): 类似隐式同步监视器上的wait()方法, 导致当前线程等待, 直到其他线程调用该Condition的signal()或signalAll()方法来唤醒该线.signal(): 唤醒在此Lock对象上等待的单个线程. 如果多个线程都是在此Lock对象上等待, 则随机唤醒一个. 只有当前线程放弃对该Lock对象的锁定后(用await()方法), 才可以执行被唤醒的线程. signalAll(): 唤醒在此Lock对象上等待的所有线程. 只有当前线程放弃对该Lock对象的锁定后, 才可以执行被唤醒的线程.
3. 例子.class Account{private ThreadLocal<String> name = new ThreadLocal<>();public Account(String str){this.name.set(str);System.out.println("---" + this.name.get());}public String getName(){return name.get();}public void setName(String str){this.name.set(str);}}class MyTest extends Thread {private Account account;public MyTest(Account account, String name){super(name);this.account = account;}public void run(){for (int i = 0; i < 10; i++){if ( i == 6){account.setName(getName());}System.out.println(account.getName() + "'s i is: " + i);}}}public class ThreadLocalTest{public static void main(String[] args){Account at = new Account("initName");new MyTest(at, "Thread-A").start();new MyTest(at, "Thread-B").start();}}9.2 包装线程不安全的集合0. 像ArrayList, LinkedList, HashSet, TreeSet, HashMap, TreeMap等集合都是线程不安全的. 可以使用Collections提供的静态方法把这些集合包装成线程安全的集合:<T> Collection<T> synchronizedCollection(Collection<T> c): 返回指定collection对应的线程安全的collection. static <T> List<T> synchronizedList(List<T> list): 返回指定List对象对应的线程安全List对象. static <K, V> Map<K, V> synchronizedMap(Map<K, V> m): 返回指定Map对象对应的线程安全Map对象.
static <T> Set<T> synchronizedSet(Set<T> s): 返回指定Set对象对应的线程安全Set对象.
static <K, V> SortedMap<K, V> synchronizedSortedMap(SortedMap<K, V> m): 返回指定SortedMap对象对应的线程安全SortedMap对象.
static <T> SortedSet<T> synchronizedSortedSet(SortedSet<T> s): 返回指定SortedSet对象对应的线程安全SortedSet对象.
1. 如果需要把某个集合包装成线程安全的集合, 则应该在创建之后立即包装. 9.3 线程安全的集合0. java.util.concurrent包提供了大量支持高效并发访问的集合接口和实现类.
1. 以Concurrent开头的集合类: ConcurrentHashMap, ConcurrentSkipListMap, ConcurrentSkipListSet等.支持多个线程并发写入访问(安全), 但读取操作不必锁定. 采用复杂算法保证了永远不会锁住整个集合.
2. CopyOnWrite开头的集合类: CopyOnWriteArrayList, CopyOnWriteArraySet等.CopyOnWriteArraySet的底层封装了CopyOnWriteArrayList, 所以实现机制类似.
线程对CopyOnWriteArrayList集合执行读取操作时, 线程直接读取集合本身, 无需加锁与阻塞. 执行写操作时, 该集合在底层复制一份新的数组, 对新数组进行写入操作. 对CopyOnWriteArrayList写入时频繁复制数组, 性能差, 但读取时性能好, 所以CopyOnWriteArrayList适合用在读取操作远远大于写入操作的场景中, 如缓存.