标签:实现 记录 style 情况 arraylist count nts zed 访问
线程之间可以共享内存,他们可以访问和操作相同的对象,比如:
public class ShareMemoryDemo { private static int shared = 0; private static void increShared(){ shared++; } static class ChildThread extends Thread{ List<String> list; public ChildThread(List<String> list){ this.list = list; } @Override public void run() { increShared(); list.add(Thread.currentThread().getName()); } } public static void main(String[] args) throws InterruptedException { List<String> list = new ArrayList<>(); Thread thread1 = new ChildThread(list); Thread thread2 = new ChildThread(list); thread1.start(); thread2.start(); thread1.join(); thread2.join(); System.out.println(shared); System.out.println(list); } }
大部分情况下,会输出期望的值:2 [Thread-0, Thread-1],当多个线程操作相同的变量时,可能会出现一些意料之外的结果,包括竟态条件和内存可见性。
1、竟态条件(多个线程访问和操作同一个对象时,最终执行结果与执行时序有关,结果可能正确也可能不正确)
public class CounterThread extends Thread { private static int counter = 0; @Override public void run() { for (int i = 0; i < 1000; i++) { counter++; } } public static void main(String[] args) throws InterruptedException { int num = 1000; Thread[] threads = new Thread[num]; for (int i = 0; i < num; i++) { threads[i] = new CounterThread(); threads[i].start(); } for (int i = 0; i < num; i++) { threads[i].join(); } System.out.println(counter); } }
预期结果是100万,但实际执行,每次结果都不一样,经常是99万多,因为counter++不是原子操作,可以使用synchronized关键字、显式锁、原子变量等方法解决。
2、内存可见性(某个线程对共享变量的修改,另一个线程不一定马上就能看到,甚至永远看不到)
public class VisibilityDemo { private static boolean shutdown = false; static class HelloThread extends Thread{ @Override public void run() { while (!shutdown){ //... } System.out.println("exit hello"); } } public static void main(String[] args) throws InterruptedException { new HelloThread().start(); Thread.sleep(1000); shutdown = true; System.out.println("exit main"); } }
预期是两个线程都退出,但实际执行时,HelloThread永远都不会退出,在HelloThread看来,shutdown永远都是false。可以使用volatile关键字、synchronized关键字、显式锁等方法解决。
可以用来修饰实例方法、静态方法、代码块。
实例方法:保护的是同一个对象的调用,确保同时只能有一个线程执行。
静态方法:保护的是类对象。
代码块:同步对象可以是任意对象,任何对象都可以作为锁对象。
synchronized是可重入的,对于同一个线程,它获得锁之后,在调用其它需要同样锁的代码时,可以直接调用。可重入是通过记录锁的持有数量来实现的。
内存可见性:对于本来就是原子的操作方法,可以使用轻量级的volatile关键字保证可见性。
类Collection中有一些方法,可以返回线程安全的同步容器
public static <T> List<T> synchronizedList(List<T> list) public static <T> Collection<T> synchronizedCollection(Collection<T> c) public static <K,V> Map<K,V> synchronizedMap(Map<K,V> m)
他们是给所有容器方法多加上synchronized来实现安全的,比如 SynchronizedCollection中的方法:
public int size() { synchronized (mutex) {return c.size();} } public boolean isEmpty() { synchronized (mutex) {return c.isEmpty();} } public boolean contains(Object o) { synchronized (mutex) {return c.contains(o);} }
同步容器的性能是比较低的,当并发访问量大的时候性能比较差,Java中还有很多专为并发设计的容器类:CopyOnWriteArrayList、ConcurrentHashMap、ConcurrentLinkedQueue、ConcurrentSkipListSet等。
标签:实现 记录 style 情况 arraylist count nts zed 访问
原文地址:https://www.cnblogs.com/wange/p/10990233.html