在JDK的并发包java.util.concurrent里提供了几个非常有用的并发容器和并发工具类。供我们在多线程开发中进行使用。这些集合和工具类都可以保证高并发的线程安全问题.
1.并发List集合_CopyOnWriteArrayList(重点)
List集合的特点:
1.是一个有序的集合
2.允许存储重复的元素
3.包含一些带索引的方法
1).java.util.concurrent.CopyOnWriteArrayList(类):它是一个“线程安全”的ArrayList,我们之前学习的java.utils.ArrayList不是线程安全的。
2).如果是多个线程,并发访问同一个ArrayList,我们要使用:CopyOnWriteArrayList
需求:
1.创建一个被多个线程共享使用静态的ArrayList集合对象
2.使用Thread-0线程往集合中添加1000个元素
3.使用main线程往集合中添加1000个元素
4.统计集合的长度
CopyOnWriteArrayList集合底层采用了CAS机制乐观锁,可以保证多线程的安全,效率比Vector高
package com.itheima.demo11List;
import java.util.ArrayList;
import java.util.Vector;
import java.util.concurrent.CopyOnWriteArrayList;
/*
需求:
1.创建一个被多个线程共享使用静态的ArrayList集合对象
2.使用Thread-0线程往集合中添加1000个元素
3.使用main线程往集合中添加1000个元素
4.统计集合的长度
*/
public class MyThread extends Thread {
//1.创建一个被多个线程共享使用静态的ArrayList集合对象
/*
ArrayList集合:是一个多线程不安全的集合
*/
//public static ArrayList<Integer> list = new ArrayList<>();
/*
java.util.Vector<E>:JDK1.0时期的集合,是最早期的单列集合
与新 collection 实现不同,Vector 是同步的。
Vector集合底层采用了synchronized悲观锁,可以保证多线程的安全,但是效率低下
*/
//public static Vector<Integer> list = new Vector<>();
/*
java.util.concurrent.CopyOnWriteArrayList<E>集合 implements List<E>接口
是JDK1.5之后出现的
CopyOnWriteArrayList集合底层采用了CAS机制乐观锁,可以保证多线程的安全,效率比Vector高
*/
public static CopyOnWriteArrayList<Integer> list = new CopyOnWriteArrayList<>();
@Override
public void run() {
//2.使用Thread-0线程往集合中添加1000个元素
System.out.println("Thread-0线程,执行线程任务了,开始往集合中添加1000个元素!");
for (int i = 0; i < 1000; i++) {
list.add(i);//[0,1,2,3...999]
//增加睡眠1毫秒的目的,提高线程安全问题出现的概率
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("Thread-0线程,添加1000个元素完毕,执行线程任务结束了!");
}
}
package com.itheima.demo11List;
public class Demo01List {
public static void main(String[] args) throws InterruptedException {
//创建MyThread对象,调用start方法,开启一个新的线程执行run方法
MyThread mt = new MyThread();
mt.start();
//主线程在开启新的线程之后,会继续执行main方法中的代码
//3.使用main线程往集合中添加1000个元素
System.out.println("主线程开始往List集合中添加1000个元素!");
for (int i = 0; i < 1000; i++) {
MyThread.list.add(i);//[0,1,2,3...999]
//增加睡眠1毫秒的目的,提高线程安全问题出现的概率
Thread.sleep(1);
}
System.out.println("主线程添加1000个元素完毕,睡眠3秒钟,等待Thread-0线程执行完毕!");
Thread.sleep(3000);
System.out.println("两个线程都执行完毕,打印集合的长度:"+MyThread.list.size());
}
}
ArrayList集合并发的问题:
1.存储的元素个数不对
2.会引发索引越界异常
主线程开始往List集合中添加1000个元素!
Thread-0线程,执行线程任务了,开始往集合中添加1000个元素!
Thread-0线程,添加1000个元素完毕,执行线程任务结束了!
主线程添加1000个元素完毕,睡眠3秒钟,等待Thread-0线程执行完毕!
两个线程都执行完毕,打印集合的长度:1991
主线程开始往List集合中添加1000个元素!
Thread-0线程,执行线程任务了,开始往集合中添加1000个元素!
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 22
at java.util.ArrayList.add(ArrayList.java:463)
at com.itheima.demo11List.Demo01List.main(Demo01List.java:13)
Thread-0线程,添加1000个元素完毕,执行线程任务结束了!
2.并发Set集合_CopyOnWriteArraySet(重点)
Set集合的特点:
1.不允许存储重复元素
2.不包含索引的方法
需求:
1.创建一个被多个线程共享使用静态的HashSet集合对象
2.使用Thread-0线程往集合中添加1000个元素
3.使用main线程往集合中添加1000个元素
4.统计集合的长度
package com.itheima.demo12Set;
import java.util.HashSet;
import java.util.concurrent.CopyOnWriteArraySet;
/*
需求:
1.创建一个被多个线程共享使用静态的HashSet集合对象
2.使用Thread-0线程往集合中添加1000个元素
3.使用main线程往集合中添加1000个元素
4.统计集合的长度
*/
public class MyThread extends Thread {
//1.创建一个被多个线程共享使用静态的HashSet集合对象
/*
HashSet集合:是一个多线程不安全的集合
*/
//public static HashSet<Integer> set = new HashSet<>();
/*
java.util.concurrent.CopyOnWriteArraySet<E>
是JDK1.5之后出现的
CopyOnWriteArraySet集合底层采用了CAS机制乐观锁,可以保证多线程的安全
*/
public static CopyOnWriteArraySet<Integer> set = new CopyOnWriteArraySet<>();
@Override
public void run() {
//2.使用Thread-0线程往集合中添加1000个元素
System.out.println("Thread-0线程,执行线程任务了,开始往集合中添加1000个元素!");
for (int i = 0; i < 1000; i++) {
set.add(i);//[0,1,2,3...999]
//增加睡眠1毫秒的目的,提高线程安全问题出现的概率
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("Thread-0线程,添加1000个元素完毕,执行线程任务结束了!");
}
}
package com.itheima.demo12Set;
public class Demo01Set {
public static void main(String[] args) throws InterruptedException {
//创建MyThread对象,调用start方法,开启一个新的线程执行run方法
MyThread mt = new MyThread();
mt.start();
//主线程在开启新的线程之后,会继续执行main方法中的代码
//3.使用main线程往集合中添加1000个元素
System.out.println("主线程开始往List集合中添加1000个元素!");
for (int i = 1000; i < 2000; i++) {
MyThread.set.add(i);//[1000,1001,1002,1003...1999]
//增加睡眠1毫秒的目的,提高线程安全问题出现的概率
Thread.sleep(1);
}
System.out.println("主线程添加1000个元素完毕,睡眠3秒钟,等待Thread-0线程执行完毕!");
Thread.sleep(3000);
System.out.println("两个线程都执行完毕,打印集合的长度:"+ MyThread.set.size());
}
}
HashSet集合存在并发问题
主线程开始往List集合中添加1000个元素!
Thread-0线程,执行线程任务了,开始往集合中添加1000个元素!
Thread-0线程,添加1000个元素完毕,执行线程任务结束了!
主线程添加1000个元素完毕,睡眠3秒钟,等待Thread-0线程执行完毕!
两个线程都执行完毕,打印集合的长度:1988
3.并发Map集合_ConcurrentHashMap(重点)
需求:
1.创建一个被多个线程共享使用静态的HashMap集合对象
2.使用Thread-0线程往集合中添加1000个元素
3.使用main线程往集合中添加1000个元素
4.统计集合的长度
package com.itheima.demo13Map;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.concurrent.ConcurrentHashMap;
/*
需求:
1.创建一个被多个线程共享使用静态的HashMap集合对象
2.使用Thread-0线程往集合中添加1000个元素
3.使用main线程往集合中添加1000个元素
4.统计集合的长度
*/
public class MyThread extends Thread {
//1.创建一个被多个线程共享使用静态的HashMap集合对象
/*
HashMap集合:是一个多线程不安全的集合
*/
//public static HashMap<Integer,Integer> map = new HashMap<>();
/*
java.util.Hashtable<K,V>:JDK1.0时期的集合,是最早期的双列集合
不像新的 collection 实现,Hashtable 是同步的
Hashtable集合底层采用了synchronized悲观锁,可以保证多线程的安全,但是效率低下
*/
//public static Hashtable<Integer,Integer> map = new Hashtable<>();
/*
java.util.concurrent.ConcurrentHashMap<K,V><E>集合
是JDK1.5之后出现的
ConcurrentHashMap集合底层采用了CAS机制乐观锁,可以保证多线程的安全,效率比Hashtable高
*/
public static ConcurrentHashMap<Integer,Integer> map = new ConcurrentHashMap<>();
@Override
public void run() {
//2.使用Thread-0线程往集合中添加1000个元素
System.out.println("Thread-0线程,执行线程任务了,开始往集合中添加1000个元素!");
for (int i = 0; i < 1000; i++) {
map.put(i,i);//[0-0,1-1,2-2,3-3...999-999]
//增加睡眠1毫秒的目的,提高线程安全问题出现的概率
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("Thread-0线程,添加1000个元素完毕,执行线程任务结束了!");
}
}
package com.itheima.demo13Map;
public class Demo01Map {
public static void main(String[] args) throws InterruptedException {
//创建MyThread对象,调用start方法,开启一个新的线程执行run方法
MyThread mt = new MyThread();
mt.start();
//主线程在开启新的线程之后,会继续执行main方法中的代码
//3.使用main线程往集合中添加1000个元素
System.out.println("主线程开始往集合中添加1000个元素!");
for (int i = 1000; i < 2000; i++) {
MyThread.map.put(i,i);//[1000-1000,1001-1001...1999-1999]
//增加睡眠1毫秒的目的,提高线程安全问题出现的概率
Thread.sleep(1);
}
System.out.println("主线程添加1000个元素完毕,睡眠3秒钟,等待Thread-0线程执行完毕!");
Thread.sleep(3000);
System.out.println("两个线程都执行完毕,打印集合的长度:"+ MyThread.map.size());
System.out.println(MyThread.map);
}
}
HashMap集合存在并发问题:
主线程开始往集合中添加1000个元素!
Thread-0线程,执行线程任务了,开始往集合中添加1000个元素!
Thread-0线程,添加1000个元素完毕,执行线程任务结束了!
主线程添加1000个元素完毕,睡眠3秒钟,等待Thread-0线程执行完毕!
两个线程都执行完毕,打印集合的长度:1997
4.比较ConcurrentHashMap和Hashtable的效率
Java类库中,从1.0版本也提供一个线程安全的Map:Hashtable
Hashtable和ConcurrentHashMap有什么区别:
Hashtable采用的synchronized——悲观锁,效率更低。
ConcurrentHashMap:采用的CAS 机制——乐观锁,效率更高。
需求:
1.创建一个被多个线程共享使用静态的Hashtable集合(ConcurrentHashMap集合)对象
2.开启1000个线程,每个线程往集合中存储100000个数据
package com.itheima.demo14Map;
import java.util.Hashtable;
import java.util.concurrent.ConcurrentHashMap;
/*
比较ConcurrentHashMap和Hashtable的效率
Hashtable和ConcurrentHashMap有什么区别:
Hashtable采用的synchronized——悲观锁,效率更低。
ConcurrentHashMap:采用的CAS 机制——乐观锁,效率更高。
*/
public class MyThread extends Thread{
//创建一个被多个线程共享使用静态的Hashtable集合(ConcurrentHashMap集合)对象
//public static Hashtable<Integer,Integer> map = new Hashtable<>();
public static ConcurrentHashMap<Integer,Integer> map = new ConcurrentHashMap<>();
@Override
public void run() {
//每个线程往集合中存储100000个数据
long s = System.currentTimeMillis();
for (int i = 0; i < 100000; i++) {
map.put(i,i);
}
long e = System.currentTimeMillis();
System.out.println(Thread.currentThread().getName()+"线程往集合中添加10万个数据耗时:"+(e-s)+"毫秒!");
}
}
package com.itheima.demo14Map;
public class Demo01 {
public static void main(String[] args) {
//开启1000个线程,每个线程往集合中存储100000个数据
for (int i = 0; i < 1000; i++) {
new MyThread().start();
}
}
}
Hashtable效率低下原因:
public synchronized V put(K key, V value)
public synchronized V get(Object key)
Hashtable容器使用synchronized来保证线程安全,但在线程竞争激烈的情况下Hashtable的效率非常低下。因为当一个线程访问Hashtable的同步方法,其他线程也访问Hashtable的同步方法时,会进入阻塞状态。如线程1使用put进行元素添加,线程2不但不能使用put方法添加元素,也不能使用get方法来获取元素,所以竞争越激烈效率越低。