一.JUC概述
java.util.concurrent------>JUC 增加了在并发编程中很常用的工具类。用于定义类似于线程的自定义系统,包括线程池,异步IO和轻量级任务框架;还提供了了用于多线程上下文中的collection实现。
二.volatile,不稳定的,异变的。
三个特征:1.互斥性(原子性)
2.内存可见性
3.指令重排序
volatile关键字:当多个线程进行操作的时候,可以保证内存中的数据是可见的,对比synchronized是一种较为轻量级的同步策略。
同步锁synchronize和volatile的区别
(1)synchronize 可以实现互斥性和内存可见性,不能禁止指令重排序
(2)volatile可以实现内存可见性,可以禁止指令重排序但是不能保证原子性。
public class Demo3 extends Thread {
public volatile boolean flag=false;
@Override
public void run() {
System.out.println("子线程开始执行.......");
while (true){
if (flag){
break;
}
}
System.out.println("子程序结束了");
}
}
public class Demo4 {
public static void main(String[] args) throws Exception{
Demo3 demo1=new Demo3();
demo1.start();
System.out.println("输入任意字符结束子线程");
System.in.read();
demo1.flag=true;
System.out.println(demo1.flag);
System.out.println("主线程结束了");
}
}
懒汉式单例
public class SingleTon {
private String name;
private int age;
public SingleTon(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
private SingleTon(){}
private volatile static SingleTon instance;
public static SingleTon getInstance(){
if (instance==null){
synchronized (SingleTon.class){
if (instance==null){
instance=new SingleTon("元宝",22);
}
}
}
return instance;
}
}
public class SingleTest {
public static void main(String[] args) {
SingleTon singleTon=SingleTon.getInstance();
SingleTon singleTon2=SingleTon.getInstance();
SingleTon singleTon3=SingleTon.getInstance();
System.out.println(singleTon.getName());
System.out.println(singleTon.hashCode());
System.out.println(singleTon2.hashCode());
System.out.println(singleTon3.hashCode());
}
}
i++的原子性问题
1.i++的操作可以分为三个步骤 : int temp=i; int temp2=temp+1; i=temp2;
2.所谓的原子性就是这三个步骤不可分割。
3.原子变量:原子变量的值,使用volatile修饰,保证了内存可见性;CAS算法保证了数据的原子性。
public class AtomicDemo implements Runnable{
private int num=0;
@Override
public void run() {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(++num);
}
}
public class AtomicTest {
public static void main(String[] args) {
AtomicDemo atomicDemo=new AtomicDemo();
for (int i = 0; i < 10; i++) {
new Thread(atomicDemo).start();
}
}
}
使用原子变量使用i++ 主函数跟上面这个一样
public class Demo5 implements Runnable{
//private int num=0;
private AtomicInteger atomicinteger=new AtomicInteger(0);
@Override
public void run() {
System.out.println(atomicinteger.getAndIncrement()); //i++
}
}
CAS算法:硬件对于并发的支持,针对多处理器操作而设计的处理器中的一种特殊指令,用于管理对共享数据的并发访问。
无锁的非阻塞算法
三个操作数: 进行比较的旧预估值A
需要读写的内存值 V
将写入的更新值 B
当且仅当A==V时,B的值才会赋值给V 否则的话不作任何操作,这个比较跟交换的过程属于原子操作。
四.Lock接口
synchronize的缺陷:a.获取锁的线程如果因为某些原因不能释放锁,其他线程就只能等待
b.使用同一把锁会进入同一个等待队列,所以需要唤醒所有线程
c.无法实现读写锁操作
案列 实现三个线程交替输出20遍ABC
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Alternative {
private Lock lock=new ReentrantLock();
private int num=1;
private int count=1;
Condition conditionA=lock.newCondition();
Condition conditionB=lock.newCondition();
Condition conditionC=lock.newCondition();
public void printA(){
lock.lock();
try {
if (num!=1){
try {
conditionA.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.print("A");
num=2;
conditionB.signal();
} finally {
lock.unlock();
}
}
public void printB(){
lock.lock();
try {
if (num!=2){
try {
conditionB.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.print("B");
num=3;
conditionC.signal();
} finally {
lock.unlock();
}
}
public void printC(){
lock.lock();
try {
if (num!=3){
try {
conditionC.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.print("C");
System.out.println("-----------"+count+"----------");
num=1;
count++;
conditionA.signal();
} finally {
lock.unlock();
}
}
}
public class AlternativeTest {
public static void main(String[] args) {
Alternative alternative=new Alternative();
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 20; i++) {
alternative.printA();
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 20; i++) {
alternative.printB();
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 20; i++) {
alternative.printC();
}
}
}).start();
}
}
五.并发集合
List和Set集合
CopyOnWriteArrayList 相当于线程安全的ArrayList 实现List接口 支持高并发的; CopyOnWriteArraySet相当于线程安全的HashSet,它继承了AbstractSet类。
CopyOnWriteArraySet内部包含一个CopyOnWriteArrayList对象。它是通过CopyOnWriteArrayList实现的。
map集合:
ConcurrentHashMap是线程安全的哈希表(相当于线程安全的HashMap);
ConcurrentSkipListMap是线程安全的有序的ConcurrentSkipListMap是线程安全的有序的哈希表(相当于线程安全的TreeMap);
它继承于AbstactMap类,并且实现ConcurrentNavigableMap接口。
ConcurrentSkipListMap是通过“跳表”来实现的,它支持并发;哈希表(相当于线程安全的TreeMap);
ConcurrentSkipListSet是线程安全的有序的集合(相当于线程安全的TreeSet);它继承于AbstractSet,并实现了NavigableSet接口。
Queue队列:
➣ ArrayBlockingQueue是数组实现的线程安全的有界的阻塞队列;
➣ LinkedBlockingQueue是单向链表实现的(指定大小)阻塞队列,该队列按FIFO(先进先出)排序元素;
➣ LinkedBlockingDeque是双向链表实现的(指定大小)双向并发阻塞队列,
该阻塞队列同时支持FIFO和FILO两种操作方式;
➣ ConcurrentLinkedQueue是单向链表实现的无界队列,该队列按FIFO(先进先出)排序元素。
➣ ConcurrentLinkedDeque是双向链表实现的无界队列,该队列同时支持FIFO和FILO两种操作方式。
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
public class Demo6 {
public static void main(String[] args) {
List<String> all = new CopyOnWriteArrayList<String>();
for (int i = 0; i < 20; i++) {
int temp = i;
new Thread(() -> {
for (int j = 0; j < 30; j++) {
all.add(Thread.currentThread().getName() + "-" + temp + "-" + j);
System.out.println(all);
}
}).start();
}
}
}
ConcurrentHashMap的使用(代码没什么变化)
Map集合的主要特征是做数据的查询处理操作,所以在ConcurrentHashMap设计的时候考虑到了数据更新的安全性与数据查询的并发性。
JDK1.7之前
ConcurrentHashMap采用锁分段机制,默认并发级别为16。
特点是写的时候同步写入,使用独占锁,读的时候为了保证性能使用了共享锁。
JDK1.8以后
ConcurrentHashMap写的时候采用CAS无锁算法进一步提高写入效率。
ArrayBlockingQueue 是数组实现线程安全的有界的阻塞队列,可以作为线程通信同步工具类的使用。
六.同步工具类 CountDownLatch(倒计时) CyclicBarrier(同一个起跑线) Semaphore
1.CountDownLatch类是一个同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待。
闭锁可以延迟线程的进度直到其到达终止状态,闭锁可以用来确保某些活动直到其他活动都完成才能继续执行:
(1)确保某个计算在其需要的所有资源都被初始化后才能继续执行。
(2)确保某个服务在其依赖的所有其他服务都已经启动之后才启动。
(3)等待直到某个操作所有参与者都执行完毕其他线程才能继续执行。
等子线程运行完 主线程再开始这类的问题。
2. CyclicBarrier 等待所有的线程都到位了再一起运行。
CyclicBarrier与CountDownLatch的区别
a.CountDownLatch只能用一次,CyclicBarrier可以reset,可以处理更加复杂的业务
b.CyclicBarrier有getNumberWaiting获取当前阻塞的线程数量,isbroken 判断阻塞的线程是否被中断。
3.Semaphore信号量 控制线程的并发数量
用来控制同时访问特定资源的线程数量,通过协调保证合理的使用公共资源。