1 JUC是什么
juc指 java.util.concurrent包
线程与进程
进程:一个程序的集合,一个进程往往可以包含多个线程,至少包含一个
线程:是执行程序的一次执行过程,它是一个动态的概念。是系统资源分配的单位
Java启动线程的三种方式:
继承Thread类
实现Runnable类
实现callable类
Java是不能开启线程的,源码展示
public synchronized void start() {
/**
* This method is not invoked for the main method thread or "system"
* group threads created/set up by the VM. Any new functionality added
* to this method in the future may have to also be added to the VM.
*
* A zero status value corresponds to state "NEW".
*/
if (threadStatus != 0)
throw new IllegalThreadStateException();
/* Notify the group that this thread is about to be started
* so that it can be added to the group's list of threads
* and the group's unstarted count can be decremented. */
group.add(this);
boolean started = false;
try {
start0();
started = true;
} finally {
try {
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
/* do nothing. If start0 threw a Throwable then
it will be passed up the call stack */
}
}
}
//本地方法调用底层的C++,java无法直接操作硬件
private native void start0();
并发与并行
并发:(多线程操作同一资源)
Cpu单核,模拟出条多线程
并行(多个人一起走)
Cup多核,多个线程可以同时执行
public class TestCPU {
public static void main(String[] args) {
//获取CPU的核数:CPU密集型 IO密集型
System.out.println(Runtime.getRuntime().availableProcessors());
}
}
并发编程的本质:充分利用CPU的资源
线程的状态
public enum State {
/**
* Thread state for a thread which has not yet started. 新生
*/
NEW,
/**
* Thread state for a runnable thread. A thread in the runnable
* state is executing in the Java virtual machine but it may
* be waiting for other resources from the operating system
* such as processor. 运行
*/
RUNNABLE,
/**
* Thread state for a thread blocked waiting for a monitor lock.
* A thread in the blocked state is waiting for a monitor lock
* to enter a synchronized block/method or
* reenter a synchronized block/method after calling
* {@link Object#wait() Object.wait}. 阻塞
*/
BLOCKED,
/**
* Thread state for a waiting thread.
* A thread is in the waiting state due to calling one of the
* following methods:
* <ul>
* <li>{@link Object#wait() Object.wait} with no timeout</li>
* <li>{@link #join() Thread.join} with no timeout</li>
* <li>{@link LockSupport#park() LockSupport.park}</li>
* </ul>
*
* <p>A thread in the waiting state is waiting for another thread to
* perform a particular action.
*
* For example, a thread that has called <tt>Object.wait()</tt>
* on an object is waiting for another thread to call
* <tt>Object.notify()</tt> or <tt>Object.notifyAll()</tt> on
* that object. A thread that has called <tt>Thread.join()</tt>
* is waiting for a specified thread to terminate. 等待,死死的等 with no timeout
*/
WAITING,
/**
* Thread state for a waiting thread with a specified waiting time.
* A thread is in the timed waiting state due to calling one of
* the following methods with a specified positive waiting time:
* <ul>
* <li>{@link #sleep Thread.sleep}</li>
* <li>{@link Object#wait(long) Object.wait} with timeout</li>
* <li>{@link #join(long) Thread.join} with timeout</li>
* <li>{@link LockSupport#parkNanos LockSupport.parkNanos}</li>
* <li>{@link LockSupport#parkUntil LockSupport.parkUntil}</li>
* </ul> waiting time 限时等待
*/
TIMED_WAITING,
/**
* Thread state for a terminated thread.
* The thread has completed execution. 终止
*/
TERMINATED;
}
1.1 Wait/sleep区别
来自不同的类 :wait是Object类的;sleep是Thread类的
锁的释放:Wait会释放锁,sleep不会释放锁(等是醒着的,睡着了就不知道释放了)
使用范围:wait只能在同步代码块中;sleep任何地方都可以(等至少要有个事情,睡任何地方都可以睡)
异常捕获:wait不需要,sleep必须要捕获异常
2 Lock锁
2.1 传统的锁synchronized
代码示例
/**
* 真正的多线程开发:线程就是一个单独的资源类,没有任何附属的操作
*/
public class SaleTicketDemo1 {
public static void main(String[] args) {
Ticket ticket = new Ticket();
//@FunctionalInterface 函数式接口 lambda表达式(参数)->{代码}
new Thread(() ->{
for (int i = 0; i < 40; i++) {
ticket.saleTicket();
}
},"A").start();
new Thread(() ->{
for (int i = 0; i < 40; i++) {
ticket.saleTicket();
}
},"B").start();
new Thread(() ->{
for (int i = 0; i < 40; i++) {
ticket.saleTicket();
}
},"C").start();
}
}
//OOP
class Ticket{
int num = 30;
public synchronized void saleTicket(){
if(num>0){
System.out.println(Thread.currentThread().getName()+"已经卖出"+num--+",剩余"+num);
}
}
}
信息打印
2.2 Interface Lock
公平锁,非公平锁
/**
* Creates an instance of {@code ReentrantLock}.
* This is equivalent to using {@code ReentrantLock(false)}.
*
* new NonfairSync() --》非公平锁
*/
public ReentrantLock() {
sync = new NonfairSync();
}
/**
* Creates an instance of {@code ReentrantLock} with the
* given fairness policy.
*
* @param fair {@code true} if this lock should use a fair ordering policy
*
* new FairSync()--》公平锁
*/
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
公平锁:十分公平;先来后到
非公平锁:不公平;可以插队(默认);(如果来了两个线程一个3h,一个3s那么插队更好)
Lock 使用代码示例
public class SaleTicketDemo2 {
public static void main(String[] args) {
Ticket2 ticket = new Ticket2();
new Thread(() ->{for (int i = 0; i < 40; i++) ticket.saleTicket();},"A").start();
new Thread(() ->{for (int i = 0; i < 40; i++) ticket.saleTicket();},"B").start();
new Thread(() ->{for (int i = 0; i < 40; i++) ticket.saleTicket();},"C").start();
}
}
//三步曲 1创建Lock new ReentrantLock() 2加锁lock.lock() 3 解锁 lock.unlock();
class Ticket2{
int num = 30;
// 1 创建Lock
Lock lock = new ReentrantLock();
public void saleTicket(){
//2 加锁
lock.lock();
try{//业务代码
if(num>0){
System.out.println(Thread.currentThread().getName()+"已经卖出"+num--+",剩余"+num);
}
}catch (Exception e){
e.printStackTrace();
}finally {//解锁
lock.unlock();
}
}
}
2.3 Synchronized与Lock的区别
1 Synchronized 内置的Java关键字,lock是一个类
2 Synchronized无法判断获取锁的状态,lock可以判断是否获取到了锁
3 Synchronized会自动释放锁,lock必须要手动释放锁(如果不释放,死锁)
4 Synchronized线程1(获得锁,阻塞)、线程2(等待,一直等);lock锁就不一定会等待下去
5 Synchronized可重入锁,不可以中断,非公平;Lock可重入锁,可以判断锁,非公平(可以自己设置)
6 Synchronized适合锁少量的代码同步问题;lock适合锁大量的同步代码
2.4 生产消费者问题
2.4.1 synchronized版本
代码示例
/**
* 线程之间的通信问题:生产和消费者问题 等待唤醒 通知唤醒
* 线程交替执行AB 操作同一个变量 num = 0
*/
public class TestPC {
public static void main(String[] args){
Data data = new Data();
new Thread(()->{ for (int i = 0; i < 5; i++)data.increment();},"A").start();
new Thread(()->{ for (int i = 0; i < 5; i++)data.decrement();},"B").start();
new Thread(()->{ for (int i = 0; i < 5; i++)data.increment();},"C").start();
new Thread(()->{ for (int i = 0; i < 5; i++)data.decrement();},"D").start();
}
}
//等待,业务,通知
class Data{
private int num = 0;
//+1
public synchronized void increment(){
if(num != 0){//等待
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
num++;
System.out.println(Thread.currentThread().getName()+"->"+num);
//通知其他线程,我+1完成
this.notifyAll();
}
//-1
public synchronized void decrement(){
if(num == 0){//等待
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
num--;
System.out.println(Thread.currentThread().getName()+"->"+num);
//通知其他线程,我-1完成
this.notifyAll();
}
}
信息打印
当只有AB线程时:
当有ABCD线程时
存在的问题 当AB两个线程正常打印信息,当ABCD四个线程时就有问题了(虚假唤醒)
因为if只判断了一次
解决办法将if改为while
/**
* 线程之间的通信问题:生产和消费者问题 等待唤醒 通知唤醒
*/
public class TestPC {
public static void main(String[] args){
Data data = new Data();
new Thread(()->{ for (int i = 0; i < 5; i++)data.increment();},"A").start();
new Thread(()->{ for (int i = 0; i < 5; i++)data.decrement();},"B").start();
new Thread(()->{ for (int i = 0; i < 5; i++)data.increment();},"C").start();
new Thread(()->{ for (int i = 0; i < 5; i++)data.decrement();},"D").start();
}
}
//等待,业务,通知
class Data{
private int num = 0;
//+1
public synchronized void increment(){
while(num != 0){//等待
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
num++;
System.out.println(Thread.currentThread().getName()+"->"+num);
//通知其他线程,我+1完成
this.notifyAll();
}
//-1
public synchronized void decrement(){
while(num == 0){//等待
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
num--;
System.out.println(Thread.currentThread().getName()+"->"+num);
//通知其他线程,我-1完成
this.notifyAll();
}
}
打印信息
2.4.2JUC版本生产与消费者
代码示例
public class JUCTestPC {
public static void main(String[] args){
Data1 data = new Data1();
new Thread(()->{ for (int i = 0; i < 10; i++)data.increment();},"A").start();
new Thread(()->{ for (int i = 0; i < 10; i++)data.decrement();},"B").start();
new Thread(()->{ for (int i = 0; i < 10; i++)data.increment();},"C").start();
new Thread(()->{ for (int i = 0; i < 10; i++)data.decrement();},"D").start();
}
}
//等待,业务,通知
class Data1{
private int num = 0;
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
//+1
public void increment(){
lock.lock();
try {
while(num != 0){//等待
condition.await();
}
num++;
System.out.println(Thread.currentThread().getName()+"->"+num);
//通知其他线程,我+1完成
condition.signalAll();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
//-1
public void decrement(){
lock.lock();
try {
while(num == 0){//等待
condition.await();
}
num--;
System.out.println(Thread.currentThread().getName()+"->"+num);
//通知其他线程,我-1完成
condition.signalAll();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
信息打印
2.4.3 Condition精确通知
代码示例
public class JUCTestConditionPC {
public static void main(String[] args) {
Data3 data3 = new Data3();
new Thread(()->{ for (int i = 0; i < 5; i++) data3.printA(); },"A").start();
new Thread(()->{ for (int i = 0; i < 5; i++) data3.printB(); },"B").start();
new Thread(()->{ for (int i = 0; i < 5; i++) data3.printC(); },"C").start();
}
}
class Data3{
private int num = 1;
private Lock lock = new ReentrantLock();
Condition condition1 = lock.newCondition();
Condition condition2 = lock.newCondition();
Condition condition3 = lock.newCondition();
public void printA(){
lock.lock();
try {
while(num!=1){
condition1.await();
}
num = 2;
System.out.println(Thread.currentThread().getName()+"->AAA");
condition2.signalAll();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void printB(){
lock.lock();
try {
while(num!=2){
condition2.await();
}
num = 3;
System.out.println(Thread.currentThread().getName()+"->BBB");
condition3.signalAll();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void printC(){
lock.lock();
try {
while(num!=3){
condition3.await();
}
num = 1;
System.out.println(Thread.currentThread().getName()+"->CCC");
condition1.signalAll();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
信息打印
3 八锁现象(深刻理解锁)
/**
* 1.标准情况下,两个线程先打印发短信还打电话
*/
public class Test1_Synchronized {
public static void main(String[] args) {
Phone1 phone = new Phone1();
new Thread(()->{ phone.sendMessage();},"A").start();
new Thread(()->{ phone.call();},"B").start();
}
}
class Phone1 {
//synchronized 锁的对象是方法的调用者
//两个方法用的是同一个锁,谁先拿到谁执行!
public synchronized void sendMessage(){
System.out.println("发短信");
}
public synchronized void call(){
System.out.println("打电话");
}
}
打印信息
总结:
1 synchronized 锁的对象是方法的调用者
2 两个方法用的是同一个锁,谁先拿到谁执行!
/**
* 2.sendMessage延迟4秒,两个线程先打印发短信还打电话
*/
public class Test2_Synchronized {
public static void main(String[] args) {
Phone2 phone = new Phone2();
new Thread(()->{ phone.sendMessage();},"A").start();
new Thread(()->{ phone.call();},"B").start();
}
}
class Phone2 {
//synchronized 锁的对象是方法的调用者
//两个方法用的是同一个锁,谁先拿到谁执行!
public synchronized void sendMessage(){
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("发短信");
}
public synchronized void call(){
System.out.println("打电话");
}
}
打印信息
小结:
1 synchronized 锁的对象是方法的调用者
2 两个方法用的是同一个锁,谁先拿到谁执行! 不管你延时多久,A先拿到方法就先执行
/**
* 3增加一个普通方法后,先执行发短信还是say hello
*/
public class Test3_Synchronized {
public static void main(String[] args) {
Phone3 phone = new Phone3();
new Thread(()->{ phone.sendMessage();},"A").start();
new Thread(()->{ phone.call();},"B").start();
new Thread(()->{ phone.hello();},"C").start();
}
}
class Phone3 {
//synchronized 锁的对象是方法的调用者
public synchronized void sendMessage(){
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("发短信");
}
public synchronized void call(){
System.out.println("打电话");
}
//这里没有锁;不是同步方法,不受锁的影响
public void hello(){
System.out.println("say hello");
}
}
打印信息
小结:
1 synchronized 锁的对象是方法的调用者
2 普调方法没有锁;不是同步方法,不受锁的影响
/**
* 4两个线程先打印发短信还打电话
*/
public class Test4_Synchronized {
public static void main(String[] args) {
//两个对象 两个调用者 两把锁
Phone4 phone1 = new Phone4();
Phone4 phone2 = new Phone4();
new Thread(()->{ phone1.sendMessage();},"A").start();
new Thread(()->{ phone2.call();},"B").start();
}
}
class Phone4 {
//synchronized 锁的对象是方法的调用者
public synchronized void sendMessage(){
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("发短信");
}
public synchronized void call(){
System.out.println("打电话");
}
}
信息打印
小结:
- 两个对象 两个调用者 两把锁 A有延时 所以先执行B线程
/**
* 5两个线程先打印发短信还打电话 只有一个对象的情况
*/
public class Test5_Synchronized {
public static void main(String[] args) {
Phone5 phone1 = new Phone5();
new Thread(()->{ phone1.sendMessage();},"A").start();
new Thread(()->{ phone1.call();},"B").start();
}
}
//Phone4唯一的一个Class模板
class Phone5 {
//synchronized static静态方法 类加载就有了!锁的是Class模板
public static synchronized void sendMessage(){
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("发短信");
}
public static synchronized void call(){
System.out.println("打电话");
}
}
信息打印
小结:
- static静态方法 类加载就有了!锁的是Class模板
- 锁是一把 谁先获得资源谁先执行(如果AB线程交换顺序着执行顺序也不一样)
/**
* 6两个线程先打印发短信还打电话 两个对象情况
*/
public class Test6_Synchronized {
public static void main(String[] args) {
//两个对象的Class类模板只有一个, static锁的是Class
Phone6 phone1 = new Phone6();
Phone6 phone2 = new Phone6();
new Thread(()->{ phone1.sendMessage();},"A").start();
new Thread(()->{ phone2.call();},"B").start();
}
}
class Phone6 {
//synchronized static静态方法 类加载就有了!锁的是Class模板
public static synchronized void sendMessage(){
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("发短信");
}
public static synchronized void call(){
System.out.println("打电话");
}
}
信息打印
小结:
- 两个对象的Class类模板只有一个, static锁的是Class
- 锁是一把 谁先获得资源谁先执行(如果AB线程交换顺序着执行顺序也不一样)
/**
* 7两个线程先打印发短信还打电话 一个对象情况 一个静态方法 一个普通方法
*/
public class Test7_Synchronized {
public static void main(String[] args) {
Phone7 phone1 = new Phone7();
new Thread(()->{ phone1.sendMessage();},"A").start();
new Thread(()->{ phone1.call();},"B").start();
}
}
class Phone7 {
//静态同步方法 锁的是Class模板
public static synchronized void sendMessage(){
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("发短信");
}
//普通的同步方法 锁的调用者
public synchronized void call(){
System.out.println("打电话");
}
}
信息打印
小结:
- 静态同步方法 锁的是Class模板;普通的同步方法 锁的调用者
- 锁是两把 A有时延 B先执行
/**
* 8两个线程先打印发短信还打电话 一个对象情况 一个静态方法 一个普通方法
*/
public class Test8_Synchronized {
public static void main(String[] args) {
//两个对象的Class类模板只有一个, static锁的是Class
Phone8 phone1 = new Phone8();
Phone8 phone2 = new Phone8();
new Thread(()->{ phone1.sendMessage();},"A").start();
new Thread(()->{ phone2.call();},"B").start();
}
}
class Phone8 {
//静态同步方法 锁的是Class模板
public static synchronized void sendMessage(){
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("发短信");
}
//普通的同步方法 锁的调用者
public synchronized void call(){
System.out.println("打电话");
}
}
打印信息
小结:
- 两个调用者的情况下(静态同步方法 锁的是Class模板;普通同步方法 锁的调用者)
- 锁是两把 A有时延 B先执行
总结
New this具体的对象实例,调用者
Static class唯一的一个模板
4 集合类不安全的问题
4.1 List
代码示例
public class TestList {
public static void main(String[] args) {
/**
* 解决办法:
* 1 用Vector,其实就是加了synchronized锁
* 2.Collections.synchronizedList(new ArrayList<>())解决
* 3.new CopyOnWriteArrayList<>()
*/
//List<String> list = new ArrayList<>();
//List<String> list = new Vector<>();
//List<String> list = Collections.synchronizedList(new ArrayList<>());
//CopyOnWrite 写入时复制 cow 计算机程序设计领域 的一种优化策略
//多个线程调用的时候,list,读取的时候,固定写入(覆盖);在写入时覆盖避免造成数据问题
List<String> list = new CopyOnWriteArrayList<>();
for (int i = 0; i < 30; i++) {
new Thread(()->{
list.add(UUID.randomUUID().toString().substring(0,5));
System.out.println(list);
},String.valueOf(i)).start();
}
}
}
打印信息
ArrayList
Vector
Collections.synchronizedList(new ArrayList<>())
CopyOnWriteArrayList
总结
ArrayList在多线程情况下可能会出现异常且不安全,没有全部成功添加,长度不够30
其他情况的list正常
源码解析
Vector 用的同步方法 synchronized
/**
* Appends the specified element to the end of this Vector.
*
* @param e element to be appended to this Vector
* @return {@code true} (as specified by {@link Collection#add})
* @since 1.2
*/
public synchronized boolean add(E e) {
modCount++;
ensureCapacityHelper(elementCount + 1);
elementData[elementCount++] = e;
return true;
}
CopyOnWriteArrayList 使用的 ReentrantLock
/**
* Appends the specified element to the end of this list.
*
* @param e element to be appended to this list
* @return {@code true} (as specified by {@link Collection#add})
*/
public boolean add(E e) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
Object[] elements = getArray();
int len = elements.length;
Object[] newElements = Arrays.copyOf(elements, len + 1);
newElements[len] = e;
setArray(newElements);
return true;
} finally {
lock.unlock();
}
}
4.2 Set
hasSet底层是什么–》(map的key不重复)
/**
* Constructs a new, empty set; the backing <tt>HashMap</tt> instance has
* default initial capacity (16) and load factor (0.75).
*/
public HashSet() {
map = new HashMap<>();
}
/**
* Adds the specified element to this set if it is not already present.
* More formally, adds the specified element <tt>e</tt> to this set if
* this set contains no element <tt>e2</tt> such that
* <tt>(e==null ? e2==null : e.equals(e2))</tt>.
* If this set already contains the element, the call leaves the set
* unchanged and returns <tt>false</tt>.
*
* @param e element to be added to this set
* @return <tt>true</tt> if this set did not already contain the specified
* element
*/
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
代码示例
/**
* 解决线程安全的办法
* 1Collections.synchronizedSet(new HashSet<>());
* 2new CopyOnWriteArraySet<>();
*/
public class TestSet {
public static void main(String[] args) {
//Set<String> set = new HashSet<>();
//Set<String> set = Collections.synchronizedSet(new HashSet<>());
Set<String> set = new CopyOnWriteArraySet<>();
for (int i = 0; i < 30; i++) {
new Thread(()->{
set.add(UUID.randomUUID().toString().substring(0,5));
System.out.println(set);
},String.valueOf(i)).start();
}
}
}
4.3 HashMap
代码示例
/**
* 解决办法:
* 1 Collections.synchronizedMap(new HashMap<>());
* 2 new Hashtable<>() 底层运用synchronized
* 3 new ConcurrentHashMap<>()
*/
public class TestMap {
public static void main(String[] args) {
//new HashMap<>(16,0.75f) 默认容量16,加载因子 0.75f
//Map<String,Object> map = new HashMap<>();
//Map<String,Object> map = new Hashtable<>();
//Map<String,Object> map = Collections.synchronizedMap(new HashMap<>());
Map<String,Object> map = new ConcurrentHashMap<>();
for (int i = 0; i < 30; i++) {
new Thread(()->{
map.put(Thread.currentThread().getName(),UUID.randomUUID().toString().substring(0,5));
System.out.println(map);
},String.valueOf(i)).start();
}
}
}
5 Callable
1.可以有返回值
2.可以抛出异常
3.方法不同,run()/call()
代码示例
public class TestCallable {
public static void main(String[] args) throws ExecutionException, InterruptedException {
//new Thread(new Runnable()).start();
//new Thread(new FutureTask<V>()).start();
//new Thread(new FutureTask<V>(Callable)).start();
MyThread thread = new MyThread();
FutureTask task = new FutureTask(thread);//适配类
new Thread(task,"A").start();
new Thread(task,"B").start();//结果会被缓存,效率高 所以只输出一次call方法
//这个get方法可能会产生阻塞!把他放到最后;或者用异步通信来处理
Integer o =(Integer) task.get();
System.out.println(o);
}
}
class MyThread implements Callable<Integer> {
@Override
public Integer call() throws Exception {
System.out.println("call方法");
return 1024;
}
}
信息打印
6 countDownLatch(减法计数器)
代码示例
public class TestCountDownLatchDemo {
public static void main(String[] args) throws InterruptedException {
//总数是7 ,必须要执行完任务的时候再继续往下走
CountDownLatch countDownLatch = new CountDownLatch(7);
for (int i = 0; i < 7; i++) {
new Thread(()->{
System.out.println(Thread.currentThread().getName()+" Go out!!!");
countDownLatch.countDown();//数量-1
},String.valueOf(i)).start();
}
countDownLatch.await();//等待计数器归0,然后再向下执行
System.out.println("close door");
}
}
信息打印
如注释掉 countDownLatch.await();
注意当new CountDownLatch(7) 当7大于线程数时就会卡在线程处一直不结束
原理
CountdownLatch.countdown()//数量-1
CountdownLatch.await()//等待计数器归零,然后再向下执行
每次有线程调用countdown数量-1,假设计数器变为0,CountdownLatch.await()就会被唤醒,继续执行下面的代码;
7 CyclicBarrier(加法计数器)
代码示例
public class TestCyclicBarrierDemo {
public static void main(String[] args){
CyclicBarrier cyclicBarrier = new CyclicBarrier(7,()->{
System.out.println("集齐成功,召唤神龙");
});
for (int i = 0; i < 7; i++) {
final int temp = i;//定义一个临时变量,lambda里面可以访问
new Thread(()->{
System.out.println(Thread.currentThread().getName()+"收集"+temp+"龙珠");
try {
cyclicBarrier.await();//等待
} catch (Exception e) {
e.printStackTrace();
}
},String.valueOf(i)).start();
}
}
}
8 Semaphone
信号量
代码示例
public class TestSemaphore {
public static void main(String[] args) {
Semaphore semaphore = new Semaphore(2);
for (int i = 0; i < 4; i++) {
new Thread(()->{
try {
semaphore.acquire();//获得
System.out.println(Thread.currentThread().getName()+"抢到了车位");
TimeUnit.SECONDS.sleep(1);
System.out.println(Thread.currentThread().getName()+"离开了车位");
} catch (Exception e) {
e.printStackTrace();
}finally {
semaphore.release();//释放
}
}).start();
}
}
}
打印信息
原理
semaphore.acquire();//获得,假如已经满了,等待,等到释放为止
semaphore.release();//释放,会将当前的信号量释放+1,然后召唤等待的线程
作用:多个共享资源互斥的使用!并发限流,控制最大的线程数
9 ReadWriteLock(读写锁)
代码示例
/**
* 独占锁(写锁) 一次只能被一个线程占有
* 共享锁(读锁) 多个线程可以同时占用
* ReadWriteLock
* 读-读 可以共享
* 读-写 不能共享
* 写-写 不能共享
*/
public class ReadWriteLockDemo {
public static void main(String[] args) {
MyCache myCache = new MyCache();
MyCacheLock myCacheLock = new MyCacheLock();
for (int i = 0; i < 5; i++) {
int temp = i;
new Thread(()->{
myCacheLock.put(temp+"",temp);
},String.valueOf(i)).start();
}
for (int i = 0; i < 5; i++) {
int temp = i;
new Thread(()->{
myCacheLock.get(temp+"");
},String.valueOf(i)).start();
}
}
}
//自定义锁缓存
class MyCacheLock {
private volatile Map<String,Object> map = new HashMap<>();
private ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
//写入时,只希望同时只有一个线程写
public void put(String key,Object value){
readWriteLock.writeLock().lock();
System.out.println(Thread.currentThread().getName()+"写入"+key);
map.put(key,value);
System.out.println(Thread.currentThread().getName()+"写入OK");
readWriteLock.writeLock().unlock();
}
//读取时,所有都线程都可以读
public void get(String key){
System.out.println(Thread.currentThread().getName()+"读取"+key);
Object o = map.get(key);
System.out.println(Thread.currentThread().getName()+"读取OK");
}
}
//普通自定义缓存
class MyCache {
private volatile Map<String,Object> map = new HashMap<>();
public void put(String key,Object value){
System.out.println(Thread.currentThread().getName()+"写入"+key);
map.put(key,value);
System.out.println(Thread.currentThread().getName()+"写入OK");
}
public void get(String key){
System.out.println(Thread.currentThread().getName()+"读取"+key);
Object o = map.get(key);
System.out.println(Thread.currentThread().getName()+"读取OK");
}
}
MyCache 信息打印
该方式存在插入并发问题
MyCacheLock 信息打印
10 阻塞队列
写入:如果队列满了,就必须阻塞等待
取:如果是队列是空的,必须阻塞等待生产
BlockingQueue
使用队列
添加 移除
四组API
方式 | 抛出异常 | 有返回值(不抛出异常) | 阻塞等待 | 超时等待 |
---|---|---|---|---|
添加 | add | offer(失败返回false) | put | offter(…) |
移除 | remove | poll(没有返回null) | take | poll(…) |
检查队首元素 | element | peek | - | - |
代码示例
public class TestBlockingQueue {
public static void main(String[] args) throws InterruptedException {
test1();
}
//有返回值 抛出异常
public static void test1(){
BlockingQueue blockingQueue = new ArrayBlockingQueue(3);
System.out.println(blockingQueue.add("a"));//true
System.out.println(blockingQueue.add("b"));//true
System.out.println(blockingQueue.add("c"));//true
//System.out.println(blockingQueue.add("d")); //java.lang.IllegalStateException: Queue full 超长跑异常
System.out.println("--------------------------");
System.out.println(blockingQueue.remove());
System.out.println(blockingQueue.remove());
System.out.println(blockingQueue.remove());
System.out.println(blockingQueue.remove()); //java.util.NoSuchElementException
}
}
test1信息打印
代码示例
public class TestBlockingQueue {
public static void main(String[] args) throws InterruptedException {
test2();
}
//有返回值 不抛异常
public static void test2(){
BlockingQueue blockingQueue = new ArrayBlockingQueue(3);
System.out.println(blockingQueue.offer("a"));//true
System.out.println(blockingQueue.offer("b"));//true
System.out.println(blockingQueue.offer("c"));//true
System.out.println(blockingQueue.offer("d"));//false
System.out.println("-----------------------");
System.out.println(blockingQueue.poll());//a
System.out.println(blockingQueue.poll());//b
System.out.println(blockingQueue.poll());//c
System.out.println(blockingQueue.poll());//null
}
}
test2信息打印
代码示例
public class TestBlockingQueue {
public static void main(String[] args) throws InterruptedException {
test3();
}
//阻塞等待
public static void test3() throws InterruptedException {
BlockingQueue blockingQueue = new ArrayBlockingQueue(3);
blockingQueue.put("a");
blockingQueue.put("b");
blockingQueue.put("c");
//blockingQueue.put("d");//超过长度一致等待入队 程序不结束
System.out.println("---------------------");
System.out.println(blockingQueue.take());
System.out.println(blockingQueue.take());
System.out.println(blockingQueue.take());
System.out.println(blockingQueue.take());//超过长度一致等待取值 程序不结束
}
}
test3信息打印
代码示例
public class TestBlockingQueue {
public static void main(String[] args) throws InterruptedException {
test4();
}
//超时等待
public static void test4() throws InterruptedException {
BlockingQueue blockingQueue = new ArrayBlockingQueue(3);
System.out.println(blockingQueue.offer("a", 3, TimeUnit.SECONDS));
System.out.println(blockingQueue.offer("b", 3, TimeUnit.SECONDS));
System.out.println(blockingQueue.offer("c", 3, TimeUnit.SECONDS));
//System.out.println(blockingQueue.offer("d", 3, TimeUnit.SECONDS));//超过长度设置等待入队时间 程序过时自动结束
System.out.println("---------------------");
System.out.println(blockingQueue.poll());
System.out.println(blockingQueue.poll());
System.out.println(blockingQueue.poll());
System.out.println(blockingQueue.poll(3,TimeUnit.SECONDS));//超过长度设置等待取值时间 程序过时自动结束
}
}
test4信息打印
SynchronousQueue同步队列
没有容量
进去一个元素,必须等取出来之后,才能继续存放
代码示例
public class TestSynchronousQueue {
public static void main(String[] args) {
BlockingQueue<String> synchronousQueue = new SynchronousQueue<String>();
new Thread(()->{
try {
System.out.println(Thread.currentThread().getName()+" put 1");
synchronousQueue.put("1");
System.out.println(Thread.currentThread().getName()+" put 2");
synchronousQueue.put("2");
System.out.println(Thread.currentThread().getName()+" put 3");
synchronousQueue.put("3");
} catch (InterruptedException e) {
e.printStackTrace();
}
},"A").start();
new Thread(()->{
try {
TimeUnit.SECONDS.sleep(2);
System.out.println(Thread.currentThread().getName()+"-->"+synchronousQueue.take());
TimeUnit.SECONDS.sleep(3);
System.out.println(Thread.currentThread().getName()+"-->"+synchronousQueue.take());
TimeUnit.SECONDS.sleep(4);
System.out.println(Thread.currentThread().getName()+"-->"+synchronousQueue.take());
} catch (Exception e) {
e.printStackTrace();
}
},"B").start();
}
}
信息打印
11 线程池
线程池:三大方法、7大参数、4种拒绝策略
池化技术
程序的运行,本质:占用系统的资源!优化资源的使用
线程池、连接池,内存池、对象池…
池化技术:事先准备好一些资源,有人要用,就来池里取,用完之后还给池里
线程池的好处
降低资源的消耗
提高响应的速度
方便管理
线程服用、可以控制最大并发数,管理线程
11.1 三大方法
代码示例
public class ThreadStartWay {
public static void main(String[] args) {
ExecutorService service1 = Executors.newSingleThreadExecutor();//单个线程
ExecutorService service2 = Executors.newFixedThreadPool(5);//创建一个固定的线程池的大小
ExecutorService service3 = Executors.newCachedThreadPool();//可伸缩的,遇强则强
try {
for (int i = 0; i < 30; i++) {
service3.execute(()->{
System.out.println(Thread.currentThread().getName()+" hello");
});
}
} catch (Exception e) {
e.printStackTrace();
} finally {
service3.shutdown();
}
}
}
单个线程底层源码
/**
* Creates an Executor that uses a single worker thread operating
* off an unbounded queue. (Note however that if this single
* thread terminates due to a failure during execution prior to
* shutdown, a new one will take its place if needed to execute
* subsequent tasks.) Tasks are guaranteed to execute
* sequentially, and no more than one task will be active at any
* given time. Unlike the otherwise equivalent
* {@code newFixedThreadPool(1)} the returned executor is
* guaranteed not to be reconfigurable to use additional threads.
*
* @return the newly created single-threaded Executor
*/
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
固定线程池
/**
* Creates a thread pool that reuses a fixed number of threads
* operating off a shared unbounded queue. At any point, at most
* {@code nThreads} threads will be active processing tasks.
* If additional tasks are submitted when all threads are active,
* they will wait in the queue until a thread is available.
* If any thread terminates due to a failure during execution
* prior to shutdown, a new one will take its place if needed to
* execute subsequent tasks. The threads in the pool will exist
* until it is explicitly {@link ExecutorService#shutdown shutdown}.
*
* @param nThreads the number of threads in the pool
* @return the newly created thread pool
* @throws IllegalArgumentException if {@code nThreads <= 0}
*/
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
缓存线程池
/**
* Creates a thread pool that creates new threads as needed, but
* will reuse previously constructed threads when they are
* available. These pools will typically improve the performance
* of programs that execute many short-lived asynchronous tasks.
* Calls to {@code execute} will reuse previously constructed
* threads if available. If no existing thread is available, a new
* thread will be created and added to the pool. Threads that have
* not been used for sixty seconds are terminated and removed from
* the cache. Thus, a pool that remains idle for long enough will
* not consume any resources. Note that pools with similar
* properties but different details (for example, timeout parameters)
* may be created using {@link ThreadPoolExecutor} constructors.
*
* @return the newly created thread pool
*/
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
总结:本质都是通过ThreadPoolExecutor()
11.2 源码分析~七大参数
源码
/**
* Creates a new {@code ThreadPoolExecutor} with the given initial
* parameters and default thread factory and rejected execution handler.
* It may be more convenient to use one of the {@link Executors} factory
* methods instead of this general purpose constructor.
*
* @param corePoolSize the number of threads to keep in the pool, even
* if they are idle, unless {@code allowCoreThreadTimeOut} is set
* @param maximumPoolSize the maximum number of threads to allow in the
* pool
* @param keepAliveTime when the number of threads is greater than
* the core, this is the maximum time that excess idle threads
* will wait for new tasks before terminating.
* @param unit the time unit for the {@code keepAliveTime} argument
* @param workQueue the queue to use for holding tasks before they are
* executed. This queue will hold only the {@code Runnable}
* tasks submitted by the {@code execute} method.
* @throws IllegalArgumentException if one of the following holds:<br>
* {@code corePoolSize < 0}<br>
* {@code keepAliveTime < 0}<br>
* {@code maximumPoolSize <= 0}<br>
* {@code maximumPoolSize < corePoolSize}
* @throws NullPointerException if {@code workQueue} is null
*/
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
/**
* Creates a new {@code ThreadPoolExecutor} with the given initial
* parameters.
*
* @param corePoolSize the number of threads to keep in the pool, even
* if they are idle, unless {@code allowCoreThreadTimeOut} is set
* @param maximumPoolSize the maximum number of threads to allow in the
* pool
* @param keepAliveTime when the number of threads is greater than
* the core, this is the maximum time that excess idle threads
* will wait for new tasks before terminating.
* @param unit the time unit for the {@code keepAliveTime} argument
* @param workQueue the queue to use for holding tasks before they are
* executed. This queue will hold only the {@code Runnable}
* tasks submitted by the {@code execute} method.
* @param threadFactory the factory to use when the executor
* creates a new thread
* @param handler the handler to use when execution is blocked
* because the thread bounds and queue capacities are reached
* @throws IllegalArgumentException if one of the following holds:<br>
* {@code corePoolSize < 0}<br>
* {@code keepAliveTime < 0}<br>
* {@code maximumPoolSize <= 0}<br>
* {@code maximumPoolSize < corePoolSize}
* @throws NullPointerException if {@code workQueue}
* or {@code threadFactory} or {@code handler} is null
*/
public ThreadPoolExecutor(int corePoolSize,//核心线程池大小
int maximumPoolSize,//最大核心线程池大小
long keepAliveTime,//超时了没有人调用就会释放
TimeUnit unit,//超时单位
BlockingQueue<Runnable> workQueue,//阻塞队列
ThreadFactory threadFactory,//线程工厂,创建线程的,一般不用动
RejectedExecutionHandler handler) {//拒绝策略
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
11.3 四大策略
new ThreadPoolExecutor.AbortPolicy();//队列人满了,还来人,不处理了,抛出异常
new ThreadPoolExecutor.CallerRunsPolicy();//哪儿来去哪儿
new ThreadPoolExecutor.DiscardOldestPolicy();//队列满了,丢掉任务,不抛异常
new ThreadPoolExecutor.DiscardPolicy();//队列满了,尝试和去最早的竞争,不会抛异常
手动创建一个线程池
代码示例
public class ThreadPoolDemo {
public static void main(String[] args) {
int core = Runtime.getRuntime().availableProcessors();
ExecutorService threadPool = new ThreadPoolExecutor(
core,core*4,3,
TimeUnit.SECONDS,
new LinkedBlockingDeque<>(core),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.DiscardOldestPolicy()
);
try {
for (int i = 0; i < 20; i++) {
threadPool.execute(()->{
System.out.println(Thread.currentThread().getName()+"!");
});
}
} catch (Exception e) {
e.printStackTrace();
} finally {
threadPool.shutdown();
}
}
}
CPU密集型 与 IO密集型
1cpu密集型,几核就是几,可以保持cpu的效率最高!
获取cup的多少核:Runtime.getRuntime().availableProcessors();设置最大核心线程池大小
2 io 密集型,判断你的程序中十分耗IO的线程
12 四大基本函数型接口
12.1 函数式接口
@FunctionalInterface
public interface Function<T, R> {
/**
* Applies this function to the given argument.
*
* @param t the function argument
* @return the function result
*/
R apply(T t);
代码示例
/**
* 函数式接口 输入一个参数,返回一个参数
*/
public class FunctionDemo01 {
public static void main(String[] args) {
Function<String,String> function1 = new Function<String,String>() {
@Override
public String apply(String str) {
return str;
}
};
Function<String,String> function2 =(str)->{return str;};
Function<String,String> function3 = str->{return str;};
System.out.println(function1.apply("hello world 1 !"));
System.out.println(function2.apply("hello world 2 !"));
System.out.println(function3.apply("hello world 3 !"));
}
}
信息打印
12.2 断定型
@FunctionalInterface
public interface Predicate<T> {
/**
* Evaluates this predicate on the given argument.
*
* @param t the input argument
* @return {@code true} if the input argument matches the predicate,
* otherwise {@code false}
*/
boolean test(T t);
代码示例
/**
* 断定型接口 输入一个参数,返回boolean
*/
public class PredicateDemo02 {
public static void main(String[] args) {
Predicate<String> predicate1 = new Predicate<String>() {
@Override
public boolean test(String str) {
return str.isEmpty();
}
};
Predicate<String> predicate2 = (str)->{return str.isEmpty();};
Predicate<String> predicate3 = str->{return str.isEmpty();};
System.out.println(predicate1.test(""));//true
System.out.println(predicate2.test("123"));//false
System.out.println(predicate3.test(""));//true
}
}
信息打印
12.3 消费型
@FunctionalInterface
public interface Consumer<T> {
/**
* Performs this operation on the given argument.
*
* @param t the input argument
*/
void accept(T t);
代码示例
/**
* 消费者 只有输入 没有返回值
*/
public class ConsumerDemo03 {
public static void main(String[] args) {
Consumer<String> consumer1 = new Consumer<String>() {
@Override
public void accept(String str) {
System.out.println(str);
}
};
Consumer<String> consumer2 = (str)->{System.out.println(str);};
Consumer<String> consumer3 = str->{System.out.println(str);};
consumer1.accept("1123");
consumer2.accept("avs");
consumer3.accept("ava212");
}
}
打印信息
12.4 供给型
@FunctionalInterface
public interface Supplier<T> {
/**
* Gets a result.
*
* @return a result
*/
T get();
}
代码示例
/**
* 供给型 没有传入参数,只有返回参数
*/
public class SupplierDemo04 {
public static void main(String[] args) {
Supplier<String> supplier1 = new Supplier<String>() {
@Override
public String get() {
System.out.println("o(* ̄︶ ̄*)o");
return "wo cao";
}
};
Supplier<String> supplier2 =()->{return "wo cao";};
Supplier<String> supplier3 =()->{return "wo cao";};
System.out.println(supplier1.get());
System.out.println(supplier2.get());
System.out.println(supplier3.get());
}
}
信息打印
12.5 Stream流试计算
代码示例
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
private int id;
private String name;
private int age;
}
/**
* 一行代码实现
* 1id为偶数
* 2年龄大于23
* 3用户名转为大写
* 4用户名字母倒序
* 5只输出一个用户
*/
public class StreamDemo1 {
public static void main(String[] args) {
User u1 = new User(1,"a",21);
User u2 = new User(2,"b",22);
User u3 = new User(3,"c",23);
User u4 = new User(4,"d",24);
User u5 = new User(5,"e",25);
User u6 = new User(6,"f",26);
List<User> usersList = Arrays.asList(u1, u2, u3, u4, u5, u6);
usersList.stream()
.filter(u->{return u.getId()%2==0;})
.filter(u->{return u.getAge()>23;})
.map(u->{return u.getName().toUpperCase();})
.sorted((uu1,uu2)->{return uu2.compareTo(uu1);})
.limit(1)
.forEach(System.out::println);
}
}
信息打印
ForkJoin (拆分任务)
Forkjoin特点:工作窃取
双端队列
13 异步回调
Future:对将来的某个事件结果进行建模
代码示例
public class FutureTest01 {
public static void main(String[] args) {
//没有返回值 runAsync 异步回调
CompletableFuture<Void> voidCompletableFuture = CompletableFuture.runAsync(()->{
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"=>runAsync");
});
System.out.println("1233");
try {
voidCompletableFuture.get();//获取阻塞执行结果
} catch (Exception e) {
e.printStackTrace();
}
}
}
信息打印
有返回值的情况
public class FutureTest02 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
CompletableFuture<Integer> completableFuture = CompletableFuture.supplyAsync(() -> {
System.out.println(Thread.currentThread().getName()+"=>supplyAsync");
//int i = 10/0;
return 200;
});
System.out.println(completableFuture.whenComplete((t, u) -> {
System.out.println("t=>" + t);// 200 成功返回值
System.out.println("u=>" + u);// 错误信息 java.util.concurrent.CompletionException: java.lang.ArithmeticException: / by zero
}).exceptionally((e) -> {
System.out.println("message=>" + e.getMessage());
return 500;
}).get());
}
}
无异常打印信息
放开注释,有异常打印信息
14 JMM
volatile是Java虚拟机提供轻量级的同步机制
1保证可见性
2不保证原子性
3禁止指令重排
什么是JMM
JMM:java内存模型
关于jmm的一些同步的约定:
1 线程解锁前,必须把共享变量立刻刷回主存
2 线程加锁前,必须读取主存中的最新值到工作内存中!
3 加锁解锁是同一把锁
线程 工作内存 主内存 8种操作:
- lock(锁定):作用于主内存的变量,把一个变量标识为一条线程独占状态。
- unlock(解锁):作用于主内存变量,把一个处于锁定状态的变量释放出来,释放后的变量才可以被其他线程锁定。
- read(读取):作用于主内存变量,把一个变量值从主内存传输到线程的工作内存中,以便随后的load动作使用
- load(载入):作用于工作内存的变量,它把read操作从主内存中得到的变量值放入工作内存的变量副本中。
- use(使用):作用于工作内存的变量,把工作内存中的一个变量值传递给执行引擎,每当虚拟机遇到一个需要使用变量的值的字节码指令时将会执行这个操作。
- assign(赋值):作用于工作内存的变量,它把一个从执行引擎接收到的值赋值给工作内存的变量,每当虚拟机遇到一个给变量赋值的字节码指令时执行这个操作。
- store(存储):作用于工作内存的变量,把工作内存中的一个变量的值传送到主内存中,以便随后的write的操作。
- write(写入):作用于主内存的变量,它把store操作从工作内存中一个变量的值传送到主内存的变量中。
14.1 保证可见性
/**
* volatile可以保证可见性
*/
public class JMMDemo01 {
//不加volatile程序会进入死循环,加上volatile可以保证可见性
private volatile static int num = 0;
public static void main(String[] args) {
new Thread(()->{//线程对主内存中num的值的变化不知道的,所以线程一直运行
while(num == 0){
}
}).start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
num = 1;
System.out.println(num);
}
}
但是在while中打印信息程序又会停止,为什么?
14.2 不保证原子性
原子性 不可分割
线程A在执行任务的时候,不能被打扰,也不能被分割;要么同时成功,要么同时失败。
/**
* volatile不保证原子性
*/
public class JMMDemo02 {
//volatile保证可见性,不保证原子性
private volatile static int num = 0;
private static void add(){
num++;//不是原子性操作
}
public static void main(String[] args) {
//理论上值为20000
for (int i = 0; i < 20; i++) {
new Thread(()->{
for (int j = 0; j <1000 ; j++) {
add();
}
}).start();
}
while (Thread.activeCount()>2){//main gc 在运行
Thread.yield();
}
System.out.println(Thread.currentThread().getName()+ " "+ num);
}
}
信息打印
使用原子类解决问题
/**
* AtomicInteger原子类解决问题
*/
public class JMMDemo03 {
//volatile保证可见性,不保证原子性
//private volatile static int num = 0;
//用原子类解决问题
private static AtomicInteger num = new AtomicInteger();
private static void add(){
//num++;
num.getAndIncrement();// AtomicInteger+1 方法 CAS
}
public static void main(String[] args) {
//理论上值为20000
for (int i = 0; i < 20; i++) {
new Thread(()->{
for (int j = 0; j <1000 ; j++) {
add();
}
}).start();
}
while (Thread.activeCount()>2){//main gc 在运行
Thread.yield();
}
System.out.println(Thread.currentThread().getName()+ " "+ num);
}
}
信息打印
这些类的底层都直接和操作系统挂钩,在内存中修改值!Unsafe是一个特殊的类
14.3 指令重拍
什么是指令重拍:你写的程序,计算机并不是按照你写的那样去执行的
源代码->编译器优化的重排->指令并行也可能会重排->内存系统也会重排->执行
处理器在进行指令重排的时候;会优先考虑数据之间的依赖性!
int x = 1;//1
int y = 2;//2
x = x + 5;//3
y = x * y;//4
//我们期望顺序是1234 但是可能执行顺序是 2134 1324
可能造成影响的结果:a b x y 四个值都为0;
线程A | 线程B |
---|---|
x=a | y=b |
b=1 | a=2 |
正常结果:x = 0 y = 0; 但是指令重排
线程A | 线程B |
---|---|
b=1 | a=2 |
x=a | y=b |
指令重排导致结果: x = 2 y = 1;
Volatile 可以避免指令重排
1 保证特定的操作的执行顺序!
2 可以保证某些变量的内存可见性(利用这些特性volatile实现了可见性)
Volatile是可以保持可见性,不能保证原子性,由于内存屏障,可以保证避免指令重排的现象产生
15 单列模式
15.2 饿汉模式
//饿汉式单列模式
public class HungrySingle {
private HungrySingle(){
System.out.println(Thread.currentThread().getName()+"->创建");
}
private final static HungrySingle HUNGRY_SINGLE = new HungrySingle();
public static HungrySingle getHungrySingle(){
return HUNGRY_SINGLE;
}
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
new Thread(()->{
HungrySingle instance = HungrySingle.getHungrySingle();
}).start();
}
}
}
信息打印
15.2 懒汉模式V1
//懒汉模式~不双重校验不安全
public class LazyMan01 {
private LazyMan01(){
System.out.println(Thread.currentThread().getName() + "->创建");
}
private static LazyMan01 lazyMan;
//不加双重校验在多线程下不安全
public static LazyMan01 getInstance(){
if(lazyMan==null){
lazyMan = new LazyMan01();
}
return lazyMan;
}
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
new Thread(()->{
LazyMan01 instance = LazyMan01.getInstance();
}).start();
}
}
}
信息打印
15.3 双重检测锁 模式懒汉单例
//双重检测锁 模式懒汉单例~线程安全 DCL 避免指令重排
public class LazyMan02 {
private LazyMan02(){
System.out.println(Thread.currentThread().getName() + "->创建");
}
//所以要避免指令重拍 添加volatile
private volatile static LazyMan02 lazyMan;
//不加双重校验在多线程下不安全
public static LazyMan02 getInstance(){
if(lazyMan==null){
synchronized (LazyMan02.class){
if(lazyMan==null){
lazyMan = new LazyMan02();//这行代码不是一个原子性操作
/**
* 三步:1分配内存空间;2执行构造方法,初始化对象;3把初始化对象指向分配的空间
* 期望指令为123
* 但是可能为132 线程A执行132
* 线程B //此时线程B来进行校验发现 lazyMan!=null 就返回一个空对象
*
* 所以要避免指令重拍 添加volatile
*/
}
}
}
return lazyMan;
}
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
new Thread(()->{
LazyMan02 instance = LazyMan02.getInstance();
}).start();
}
}
}
信息打印
15.4 反射破坏单列模式
//反射破坏单列模式
public class LazyMan03 {
private LazyMan03(){
}
private volatile static LazyMan03 lazyMan;
public static LazyMan03 getInstance(){
if(lazyMan==null){
synchronized (LazyMan03.class){
if(lazyMan==null){
lazyMan = new LazyMan03();
}
}
}
return lazyMan;
}
public static void main(String[] args) throws Exception {
LazyMan03 instance1 = LazyMan03.getInstance();//该方法调用一次私有构造方法 lazyMan会创建一个对象
Constructor<LazyMan03> declaredConstructor = LazyMan03.class.getDeclaredConstructor(null);
declaredConstructor.setAccessible(true);
LazyMan03 instance2 = declaredConstructor.newInstance();
System.out.println(instance1.hashCode());
System.out.println(instance2.hashCode());
}
}
信息打印
v2
//反射破坏单列模式
public class LazyMan04 {
private boolean boolVal = true;
private LazyMan04(){
synchronized (LazyMan04.class){
if(boolVal){
boolVal= false;
}else{
throw new RuntimeException("请不要试图用反射破坏单列异常");
}
}
}
private volatile static LazyMan04 lazyMan;
public static LazyMan04 getInstance(){
if(lazyMan==null){
synchronized (LazyMan04.class){
if(lazyMan==null){
lazyMan = new LazyMan04();
}
}
}
return lazyMan;
}
public static void main(String[] args) throws Exception {
Constructor<LazyMan03> declaredConstructor = LazyMan03.class.getDeclaredConstructor(null);
declaredConstructor.setAccessible(true);
//根本没有使用私有构造方法创建 lazyMan会为null 所以不会抛异常
LazyMan03 instance1 = declaredConstructor.newInstance();
LazyMan03 instance2 = declaredConstructor.newInstance();
System.out.println(instance1.hashCode());
System.out.println(instance2.hashCode());
}
}
信息打印
15.5 静态内部类单列模式
public class Singleton {
private static class SingletonHolder {
private static Singleton instance = new Singleton();
}
private Singleton() {
}
public static Singleton getInstance() {
return SingletonHolder.instance;
}
}
15.6 枚举单列模式
//enum 枚举也是一个class类
public enum EnumSingle {
INSTANCE;
public EnumSingle getInstance(){
return INSTANCE;
}
}
class Test{
public static void main(String[] args) throws Exception {
EnumSingle instance1 = EnumSingle.INSTANCE;
/**
* 运行抛异常 java.lang.NoSuchMethodException: com.companyname.single.EnumSingle.<init>()
* 说明 没有无参构造方法
*/
Constructor<EnumSingle> declaredConstructor = EnumSingle.class.getDeclaredConstructor(String.class,int.class);
declaredConstructor.setAccessible(true);
EnumSingle instance2 = declaredConstructor.newInstance();
System.out.println(instance1.hashCode());
System.out.println(instance2.hashCode());
//结果与预期相同 java.lang.IllegalArgumentException: Cannot reflectively create enum objects
}
}
打印信息
总结
1 饿汉式
- 优点 就是实现简单 安全可靠
- 缺点 没起到懒加载的效果,还没启动就创建出来了
2 懒汉式
- 优点
1 v1 懒加载 先判断是否为空再去创建实例 但是存在线程安全;
2 v2 添加双重校验 通过枷锁 可以保证同时只有一个线程走到第二个判断 空代码中去,保证了只创建一个实例
用volatile关键字来修饰singleton,其作用是防止指令重排 - 缺点 但是可能存在反射攻击或者反序列化攻击
3 静态内部类
- 优点 线程安全 不会在类加载是就加载,达到懒加载的效果
- 缺点 但是可能存在反射攻击或者反序列化攻击
4 枚举模式
- 优点
1最佳的单例实现模式就是枚举模式
2利用枚举的特性,让JVM来帮我们保证线程安全和单一实例的问题
3写法还特别简单
16 理解CAS
16.1 什么是CAS
比较当前工作内存种的值和主内存中的值,如果这个值是期望的,那么执行操作!如果不是就一直循环
缺点
1循环会耗时
2一次性只能保证一个共享变量的原子性
3ABA问题
public class CASDemo {
public static void main(String[] args) {
//CAS compareAndSet:比较并交换
AtomicInteger atomicInteger = new AtomicInteger(2020);
//期望更新~如果我们期望的值达到了,那么就更新,否则就不更新CAS是CPU的并发原语;
System.out.println(atomicInteger.compareAndSet(2020, 2021));
System.out.println(atomicInteger.get());
System.out.println(atomicInteger.compareAndSet(2020, 2021));
System.out.println(atomicInteger.get());
}
}
信息打印
源码
/**
* Atomically increments by one the current value.
*
* @return the previous value
*/
public final int getAndIncrement() {
return unsafe.getAndAddInt(this, valueOffset, 1);
}
...
public final int getAndAddInt(Object var1, long var2, int var4) {
int var5;
do {
var5 = this.getIntVolatile(var1, var2);//获取内存中的值
} while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4)); //内存操作,效率很高
return var5;
}
Unsafe java无法操作内存,Java可以调用c++ native c++可以操作内存,unsafe相当于Java的后门,可以通过这个类操作内存
16.2 CAS:ABA问题(狸猫换太子)
public class CASDemo {
public static void main(String[] args) {
AtomicInteger atomicInteger = new AtomicInteger(2020);
//捣乱线程
System.out.println(atomicInteger.compareAndSet(2020, 2021));
System.out.println(atomicInteger.get());
System.out.println(atomicInteger.compareAndSet(2021, 2020));
System.out.println(atomicInteger.get());
//期望线程
System.out.println(atomicInteger.compareAndSet(2020, 666));
System.out.println(atomicInteger.get());
}
}
信息打印
插曲
Integer使用了对象缓存机制,默认范围-128~127,推荐使用静态工厂方法valueOf获取对象实例,而不是new,因为valueOf使用缓存,而new一定会创建新的对象分配新的内存空间
/**
* 1==是判断两个变量或实例是不是指向同一个内存空间,
* equals是判断两个变量或实例所指向的内存空间的值是不是相同
* 2==是指对内存地址进行比较 , equals()是对字符串的内容进行比较
* 3==指引用是否相同, equals()指的是值是否相同
*/
public class IntegerTest {
public static void main(String[] args) {
Integer i1 = 1024;
Integer i2 = 1024;
Integer i3 = 24;
Integer i4 = 24;
System.out.println("i1==i2:"+(i1==i2)); //false
System.out.println("i1.equals(i2):"+i1.equals(i2));//true
System.out.println("--- -128~127之间的数 ----");
System.out.println("i3==i4:"+(i3==i4));//true
System.out.println("i3.equals(i4):"+i3.equals(i4));//true
}
}
信息打印
原子引用 解决ABA问题,引入原子引用!对应的思想:乐观锁!
public class CASDemo2 {
public static void main(String[] args) {
AtomicStampedReference<Integer> stampedReference = new AtomicStampedReference<Integer>(27,1);
new Thread(()->{
System.out.println("a1=>"+stampedReference.getStamp());
//休眠一秒保证b线程获取到stampedReference.getStamp() 和 最开始是一直的都为1
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("a线程cas1:"+stampedReference.compareAndSet(27, 33, stampedReference.getStamp(), stampedReference.getStamp() + 1));
System.out.println("a2=>"+stampedReference.getStamp());
System.out.println("a线程cas2:"+stampedReference.compareAndSet(33, 27, stampedReference.getStamp(), stampedReference.getStamp() + 1));
System.out.println("a3=>"+stampedReference.getStamp());
},"a").start();
new Thread(()->{
int stamp = stampedReference.getStamp();
System.out.println("b1=>"+stamp);
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("b线程cas1:"+stampedReference.compareAndSet(27, 66, stamp, stampedReference.getStamp() + 1));
System.out.println("b2=>"+stampedReference.getStamp());
},"b").start();
}
}
信息打印
17 各种锁
1公平锁、非公平锁
公平锁:非常公平,不能够插队,必须先来后到
非公平锁:非常不公平,可以插队(默认都是非公平)
/**
* Creates an instance of {@code ReentrantLock}.
* This is equivalent to using {@code ReentrantLock(false)}.
*/
public ReentrantLock() {
sync = new NonfairSync();
}
/**
* Creates an instance of {@code ReentrantLock} with the
* given fairness policy.
*
* @param fair {@code true} if this lock should use a fair ordering policy
*/
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
2可重入锁
可从入锁(递归锁):
synchronized
public class LockDemo1 {
public static void main(String[] args) {
iPhone phone = new iPhone();
new Thread(()->{
phone.sms();
},"A").start();
new Thread(()->{
phone.sms();
},"B").start();
}
}
class iPhone{
public synchronized void sms(){
System.out.println(Thread.currentThread().getName()+"=> sms");
call();
}
private synchronized void call() {
System.out.println(Thread.currentThread().getName()+"=> call");
}
}
信息打印
lock
public class LockDemo2 {
public static void main(String[] args) {
iPhone2 phone = new iPhone2();
new Thread(()->{
phone.sms();
},"A").start();
new Thread(()->{
phone.sms();
},"B").start();
}
}
class iPhone2{
Lock lock = new ReentrantLock(); //lock锁必须配对,否则会死锁
public void sms(){
lock.lock();//第一把锁
try {
System.out.println(Thread.currentThread().getName()+"=> sms");
call();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
private void call() {
lock.lock();//第一把锁
try {
System.out.println(Thread.currentThread().getName()+"=> call");
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
信息打印
3自旋锁
public final int getAndAddInt(Object var1, long var2, int var4) {
int var5;
do {
var5 = this.getIntVolatile(var1, var2);
} while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));
return var5;
}
public class SpinlockDemo3 {
AtomicReference<Thread> atomicReference = new AtomicReference();
public void mylock(){
Thread thread = Thread.currentThread();
System.out.println(thread.getName()+"===> mylock");
//自旋锁
while (!atomicReference.compareAndSet(null,thread)){
}
}
public void myUnlock(){
Thread thread = Thread.currentThread();
System.out.println(thread.getName()+"===> myUnlock");
atomicReference.compareAndSet(thread,null);
}
}
public class SpinlockDemo3Test {
public static void main(String[] args) throws InterruptedException {
SpinlockDemo3 lock = new SpinlockDemo3();
new Thread(()->{
lock.mylock();
try {
TimeUnit.SECONDS.sleep(5);
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.myUnlock();
}
},"A").start();
TimeUnit.SECONDS.sleep(2);
new Thread(()->{
lock.mylock();
try {
TimeUnit.SECONDS.sleep(1);
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.myUnlock();
}
},"B").start();
}
}
信息打印
4死锁
public class DeadLockDemo {
public static void main(String[] args) {
String lockA = "lockA";
String lockB = "lockB";
new Thread(new MyThread(lockA,lockB),"T1").start();
new Thread(new MyThread(lockB,lockA),"T2").start();
}
}
class MyThread implements Runnable{
private String lockA;
private String lockB;
public MyThread(String lockA,String lockB){
this.lockA = lockA;
this.lockB = lockB;
}
@Override
public void run() {
synchronized (lockA){
System.out.println(Thread.currentThread().getName()+" ->"+lockA+" get "+lockB+",lockB is locked");
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (lockB){
System.out.println(Thread.currentThread().getName()+" ->"+lockB+" get "+lockA+",lockA is locked");
}
}
}
}
信息打印
解决问题
1、使用Jps-l 定位进程号
2、使用jstack进程号 找到死锁位置