JUC并发编程

本文深入探讨了Java中的并发编程,涵盖了线程、进程、线程状态、JUC工具包如锁、并发队列、线程池等内容,并详细讲解了Volatile的特性和JMM内存模型。此外,还介绍了函数式接口和Stream流式计算在多线程环境中的应用。最后,讨论了单例模式和死锁问题,以及如何排查和避免死锁。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

什么是JUC

java.util工具包

线程和进程

进程:一个程序,程序的集合

一个进程往往可以包含多个线程,至少包含一个;

java默认有几个进程?2个mian和GC

对于java而言:Thread、Runnable、Callable

Java真的可以开启线程吗?开不了,是java调用本地方法开启线程。

并发、并行

并发编程:并发、并行

并发(多个线程操作同一个资源)

  • CPU一核,模拟出来多条线程,足够快,快速交替

并行(多个人一起行走)

  • CPU多核,多个线程可以同时执行;线程池

并发编程的本质:充分利用CPU资源

所有的公司都很看重

线程状态

创建、运行、阻塞、等待、超时等待、死亡

wait和sleep的区别

1.来自不同的类

wait=>Object

sleep=>Thread

2.关于锁的释放

wait会释放,sleep不会释放锁

3.使用的范围是不同

wait必须在同步代码块,sleep可以在任何地方

4.释放需要捕获异常

sleep需捕获异常

wait不需要捕获异常

Lock锁

传统锁Synchronized

package org.song.Test;

/**
 * Synchronized
 * 
 * @author
 *
 */
public class Test01 {

	public static void main(String[] args) {
		ticket ticket = new ticket();

		new Thread(() -> {
			for (int i = 0; i < 60; i++) {
				ticket.sale();
			}
		}, "A").start();
		new Thread(() -> {
			for (int i = 0; i < 60; i++) {
				ticket.sale();
			}
		}, "B").start();
		new Thread(() -> {
			for (int i = 0; i < 60; i++) {
				ticket.sale();
			}
		}, "C").start();

	}

}

// 改变实现Runnable接口的方法,让这个类成为一个干净的资源类,有助降低耦合
// 在资源类中,应该只有方法和属性,即是oop编程思想
// 改变为使用lambad表达式实现
class ticket {
	private int ticket = 50;

	public synchronized void sale() {
		if (ticket > 0) {
			System.out.println(Thread.currentThread().getName() + "买了票" + ticket-- + "剩余" + ticket);
		}
	}
}

Lock

常用实现类Renntrantlock

公平锁

十分公平,不可以插队

非公平锁

十分不公平,可以插队(默认)

Renntranlock

  • 无参构造器,默认的是非公平锁
  • 有一个传入boolean值的构造器,改变为公平锁
package org.song.Test;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * Synchronized
 * 
 * @author
 *
 */
public class Test01 {

	public static void main(String[] args) {
		ticket ticket = new ticket();

		new Thread(() -> {
			for (int i = 0; i < 60; i++) {
				ticket.sale();
			}
		}, "A").start();
		new Thread(() -> {
			for (int i = 0; i < 60; i++) {
				ticket.sale();
			}
		}, "B").start();
		new Thread(() -> {
			for (int i = 0; i < 60; i++) {
				ticket.sale();
			}
		}, "C").start();

	}

}

/*
 * 1、实现接口类ReentrantLock
 * 2、lock.lock(); 加锁
 * 3、将要写的代码放入try中
 * 4、在finally代码块中解锁: lock.unlock();
 */
class ticket {
	private int ticket = 50;
	Lock lock=new ReentrantLock();
	public void sale() {
		lock.lock();
		try {
			if (ticket > 0) {
				System.out.println(Thread.currentThread().getName() + "买了票" + ticket-- + "剩余" + ticket);
			}
		} finally {
			lock.unlock();
		}

	}
}

Synchronized和Lock的区别

  1. Synchronized 内置java关键字,Lock是一个java类
  2. Synchronized 无法获取锁的状态,Lock可以判断是否获取到了锁
  3. Synchronized 会自动释放锁,lock必须要手动释放锁!如果不释放锁,死锁
  4. Synchronized 线程1(获得锁,阻塞),线程2(等待,一直等);Lock就不一定会等待下去
  5. Synchronized 可重入锁,不可以中断,非公平;Lock,可重入锁,非公平(可以自己设置)
  6. Synchronized 适合锁少量的代码同步,Lock适合锁大量的同步代码

生产者、消费者问题

synchronized版

  1. 使用if做判断的时候,会产生虚假唤醒,做判断应该采用while
package org.song.Test;


/**
 * Synchronized版的生产者消费者问题
 * @author 
 *
 */
public class Test02 {

	public static void main(String[] args) {
		Pc pc=new Pc();
		
		new Thread(()->{
				try {
					for (int i = 0; i < 100; i++) {
						pc.up();
					}
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			},"A").start();
		
		
		new Thread(()->{
			try {
				for (int i = 0; i < 100; i++) {
					pc.down();
				};
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		},"B").start();
		
		new Thread(()->{
			try {
				for (int i = 0; i < 100; i++) {
					pc.down();
				};
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		},"C").start();
		
		new Thread(()->{
			try {
				for (int i = 0; i < 100; i++) {
					pc.up();
				};
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		},"D").start();
		
		


	}

}

class Pc{
	private int num;
	
	//+1
	public synchronized void up() throws InterruptedException{
		if(num!=0){
			//等待
			this.wait();
		}
		num++;
		System.out.println(Thread.currentThread().getName()+"=>"+num);
		this.notifyAll();
	}
	
	//-1
	public synchronized void down() throws InterruptedException{
		if(num==0){
			//等待
			this.wait();
		}
		num--;
		System.out.println(Thread.currentThread().getName()+"=>"+num);
		this.notifyAll();
	}
}

采用while后:

package org.song.Test;


/**
 * Synchronized版的生产者消费者问题
 * @author 
 *
 */
public class Test02 {

	public static void main(String[] args) {
		Pc pc=new Pc();
		
		new Thread(()->{
				try {
					for (int i = 0; i < 100; i++) {
						pc.up();
					}
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			},"A").start();
		
		
		new Thread(()->{
			try {
				for (int i = 0; i < 100; i++) {
					pc.down();
				};
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		},"B").start();
		
		new Thread(()->{
			try {
				for (int i = 0; i < 100; i++) {
					pc.down();
				};
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		},"C").start();
		
		new Thread(()->{
			try {
				for (int i = 0; i < 100; i++) {
					pc.up();
				};
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		},"D").start();
		
		


	}

}

class Pc{
	private int num;
	
	//+1
	public synchronized void up() throws InterruptedException{
		while(num!=0){
			//等待
			this.wait();
		}
		num++;
		System.out.println(Thread.currentThread().getName()+"=>"+num);
		this.notifyAll();
	}
	
	//-1
	public synchronized void down() throws InterruptedException{
		while(num==0){
			//等待
			this.wait();
		}
		num--;
		System.out.println(Thread.currentThread().getName()+"=>"+num);
		this.notifyAll();
	}
}

JUC版

  1. 使用Lock加锁
  2. 使用Condtion监视器的await和single替代wait和notifyAll
  3. 通过创建多个condition监视器来实现精准唤醒,以实现业务流程,例如:购买-》付钱-》发货
  4. 创建condition的方法:Condition condition1=lock.newCondition();
package org.song.Test;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * JUC版的生产者消费者问题
 * 
 * @author
 *
 */
public class Test02 {

	public static void main(String[] args) {
		Pc pc = new Pc();

		new Thread(() -> {
			try {
				for (int i = 0; i < 100; i++) {
					pc.up();
				}
			} catch (InterruptedException e) {

				e.printStackTrace();
			}
		}, "A").start();

		new Thread(() -> {
			try {
				for (int i = 0; i < 100; i++) {
					pc.down();
				}
				;
			} catch (InterruptedException e) {

				e.printStackTrace();
			}
		}, "B").start();

		new Thread(() -> {
			try {
				for (int i = 0; i < 100; i++) {
					pc.down();
				}
				;
			} catch (InterruptedException e) {

				e.printStackTrace();
			}
		}, "C").start();

		new Thread(() -> {
			try {
				for (int i = 0; i < 100; i++) {
					pc.up();
				}
				;
			} catch (InterruptedException e) {

				e.printStackTrace();
			}
		}, "D").start();

	}

}

class Pc {
	private int num;
	//创建Lock
	private Lock lock=new ReentrantLock();
	//创建condition
	private Condition condition1=lock.newCondition();
	// +1
	public void up() throws InterruptedException {
		lock.lock();
		try {
			while(num!=0){
				condition1.await();
			}
			num++;
			System.out.println(Thread.currentThread().getName() + "=>" + num);
			condition1.signalAll();
		} catch (Exception e) {
			// TODO: handle exception
		}finally {
			lock.unlock();
		}
		

	}

	// -1
	public void down() throws InterruptedException {
		lock.lock();
		try {
			while(num==0){
				condition1.await();
			}
			num--;
			System.out.println(Thread.currentThread().getName() + "=>" + num);
			condition1.signalAll();
		} catch (Exception e) {
			// TODO: handle exception
		}finally {
			lock.unlock();
		}

	}
}

与Synchronized的wait和notify不同的是:condition可以通过创建多个监听器来达到精准唤醒

package org.song.Test;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class Test03 {

	public static void main(String[] args) {
		A a = new A();
		new Thread(() -> {
			for (int i = 0; i < 10; i++) {
				a.printA();
			}
		}, "A").start();
		new Thread(() -> {
			for (int i = 0; i < 10; i++) {
				a.printB();
			}
		}, "B").start();
		new Thread(() -> {
			for (int i = 0; i < 10; i++) {
				a.printC();
			}
		}, "C").start();

	}

}

class A {
	// 创建lock锁
	private Lock lock = new ReentrantLock();
	// 创建condition监视器
	private Condition condition1 = lock.newCondition();
	private Condition condition2 = lock.newCondition();
	private Condition condition3 = lock.newCondition();

	// 创建一个标志量,来实现业务的实现顺序
	private int num = 1;

	public void printA() {
		lock.lock();
		try {
			while (num != 1) {
				condition1.await();
			}
			num = 2;
			System.out.println(Thread.currentThread().getName() + "=>A");
			condition2.signal();
		} catch (Exception e) {

		} finally {
			lock.unlock();
		}
	}

	public void printB() {
		lock.lock();
		try {
			while (num != 2) {
				condition2.await();
			}
			num = 3;
			System.out.println(Thread.currentThread().getName() + "=>B");
			condition3.signal();
		} catch (Exception e) {

		} finally {
			lock.unlock();
		}
	}

	public void printC() {
		lock.lock();
		try {
			while (num != 3) {
				condition3.await();
			}
			num = 1;
			System.out.println(Thread.currentThread().getName() + "=>C");
			condition1.signal();
		} catch (Exception e) {

		} finally {
			lock.unlock();
		}
	}
}

8锁现象

什么是锁?

如何判断锁的是谁?

深刻理解锁是什么

8锁就是关于锁的八个问题

实例对象产生的锁有很多个(普通同步方法),Class产生的锁只有一个(static同步方法)

package org.song.Test;

import java.util.concurrent.TimeUnit;

/**
 * 8锁现象 关于锁的8个问题
 * 1、标准情况下,一个对象,是先执行发短信还是打电话?
 *    发短信
 * 2、SendMsg()延迟四秒,一个对象,是先执行发短信还是打电话?
 *    发短信
 * 原因:Synchronized锁的对象是方法的调用者,即在只创建一个对象的时候,就只有一把锁
 * 
 * 3、增加了一个普通方法,它不是同步方法,即没有锁,所以不受锁的影响
 * 4、两个对象,两个同步方法,发短信还是打电话?
 *    打电话
 * 5、一个对象,两个静态同步方法,发短信还是打电话
 * 6、两个对象,两个静态同步方法,发短信还是打电话
 * 原因:static同步方法,在类加载时就已经存在了,所以Synchronized锁的是class对象
 *      class锁
 * 7、一个静态同步方法、一个普通同步方法,一个对象
 * 8、一个静态同步方法、一个普通同步方法,两个对象
 * 
 * 实例对象生成的锁有很多个,Class的锁在类加载的时候就产生了,只有一个
 * @author
 * 
 *
 */
public class Test04 {

	public static void main(String[] args) throws InterruptedException {
		Phone phone = new Phone();
		//问题一、二
//		new Thread(() -> {
//			phone.SendMsg();
//		}, "A").start();
//		
//		TimeUnit.SECONDS.sleep(1);
//		
//		new Thread(() -> {
//			phone.Call();;
//		}, "B").start();
		
		
	}

}

class Phone {

	public synchronized void SendMsg() {
		try {
			TimeUnit.SECONDS.sleep(4);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.out.println("发短信");
	}

	public synchronized void Call() {
		System.out.println("电话");
	}

	public static synchronized void SendMsg2() {
		System.out.println("发短信");
	}

	public static synchronized void Call2() {
		System.out.println("电话");
	}

	public void Hello() {
		System.out.println("hello");
	}

}

集合类不安全

List不安全

  1. 使用Vector
  2. 使用Collections工具类的synchronizedList(new ArrayList())
  3. 使用JUC的CopyOnWriteArrayList(); 写入时复制
  4. Vector多使用Synchronized同步方法,效率比CopyOnWriteArrayList效率低
package org.song.Test;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.UUID;
import java.util.Vector;
import java.util.concurrent.CopyOnWriteArrayList;

/**
 * 集合类不安全
 * 
 * @author
 *
 */
public class Test05 {

	public static void main(String[] args) {
		/**
		 * 并发下ArrayList不安全
		 * 1、List<String> list = new Vector<>();   Vector多使用的Synchronized同步方法,效率低
		 * 2、List<String> list =Collections.synchronizedList(new ArrayList<>());
		 * 3、List<String> list =new CopyOnWriteArrayList<>(); 多使用lock锁,效率高
		 * CopyOnWriterArrayList() 写入时复制,即在写入前复制一份出来,等写完后在插入之前的,保证了操作的原子性
		 * @param args
		 */
		List<String> list =new CopyOnWriteArrayList<>();
		for (int i = 0; i < 10; i++) {
			new Thread(() -> {
				list.add(UUID.randomUUID().toString().substring(0, 5));
				System.out.println(list);
			}, String.valueOf(i)).start();
		}
	}

}

set不安全

package org.song.Test;

import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.CopyOnWriteArraySet;

public class Test06 {

	public static void main(String[] args) {
		/**
		 * Set<String> set = new HashSet<>();
		 * 1、Set<String> set = Collections.synchronizedSet(new HashSet<>());
		 * 2、Set<String> set =new CopyOnWriteArraySet<String>();
		 */

		Set<String> set =new CopyOnWriteArraySet<String>();
		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();
		}

	}

}

set底层原理

    public HashSet() {
        map = new HashMap<>();
    }

Map不安全

package org.song.Test;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;

public class Test06 {

	public static void main(String[] args) {
        //concurrent
		//ConcurrentModificationException 并发修改异常
		/**
		 * Map<String, String> map=new HashMap<>();
		 * 1、Map<String, String> map=Collections.synchronizedMap(new HashMap<>());
		 * 2、Map<String, String> map=new ConcurrentHashMap<>();
		 */
		Map<String, String> 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();
		}
	}

}


走近Callable

  1. 有返回值
  2. 可以抛出异常
  3. 方法不同、call方法

FutrueTask是Runnable的一个实现类

package org.song.Test;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

public class Test07 {

	public static void main(String[] args) throws InterruptedException, ExecutionException {
		
		MyThread myThread=new MyThread();
		FutureTask<Integer> futureTask=new FutureTask<>(myThread);
		new Thread(futureTask,"A").start();
		Integer a=futureTask.get();
		System.out.println(a);

	}

}
class MyThread implements Callable<Integer>{

	@Override
	public Integer call() throws Exception {
		System.out.println("call()");
		return 1024;
	}
	
}


常用的辅助类

countDownLatch

  1. CountDownLatch countDownLatch = new CountDownLatch(6); 传递一个参数
  2. countDownLatch.countDown(); 参数-1
  3. countDownLatch.await(); 等待参数归零后,再执行其后的代码
  4. 可以认为是一个减法计数器
package org.song.Test;

import java.util.concurrent.CountDownLatch;

public class Test08 {

	public static void main(String[] args) throws InterruptedException {
		CountDownLatch countDownLatch = new CountDownLatch(6);
		for (int i = 0; i < 6; i++) {
			new Thread(() -> {
				countDownLatch.countDown();
				System.out.println(Thread.currentThread().getName());
			}).start();
		}
		
		countDownLatch.await();
		
		System.out.println("close door");

	}
}

CyclicBarrier

可以认为是一个加法计数器

package org.song.Test;

import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;

public class Test09 {

	public static void main(String[] args) {
		CyclicBarrier cyclicBarrier = new CyclicBarrier(7, () -> {
			System.out.println("召唤神龙");
		});
		for (int i = 1; i <= 7; i++) {
			final int temp=i;
			new Thread(() -> {
				System.out.println(Thread.currentThread().getName()+"收集了"+temp+"龙珠");
				try {
					cyclicBarrier.await();
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				} catch (BrokenBarrierException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			},String.valueOf(i)).start();
		}
	}

}


Semaphore

参照操作系统的信号量

package org.song.Test;

import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;

public class Test10 {

	public static void main(String[] args) {
		Semaphore semaphore = new Semaphore(3);
		for (int i = 1; i <= 6; i++) {
			new Thread(() -> {
				try {
					semaphore.acquire();
					System.out.println(Thread.currentThread().getName()+"获得车位");
					TimeUnit.SECONDS.sleep(2);
					System.out.println(Thread.currentThread().getName()+"离开车位");
				} catch (InterruptedException e) {
					e.printStackTrace();
				}finally {
					semaphore.release();
				}
			},String.valueOf(i)).start();
		}
	}

}


读写锁ReadWriteLock

读可以被多线程同时读,写只能一个线程来写:提高效率

package org.song.Test;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

public class Test11 {

	public static void main(String[] args) {
		MyMap myMap = new MyMap();
		for (int i = 0; i < 5; i++) {
			final int temp=i;
			new Thread(() -> {
				myMap.put(String.valueOf(temp), String.valueOf(temp));
				myMap.get(String.valueOf(temp));
			},String.valueOf(i)).start();
		}
		
		for (int i = 0; i < 5; i++) {
			final int temp=i;
			new Thread(() -> {
				myMap.get(String.valueOf(temp));
			},String.valueOf(i)).start();
		}
	}

}

class MyMap {
	private volatile Map<String, Object> map = new HashMap<String, Object>();
	private ReadWriteLock readWriteLock = new ReentrantReadWriteLock();

	public void put(String key, Object value) {
		readWriteLock.writeLock().lock();
		try {
			System.out.println(Thread.currentThread().getName() + "写入");
			map.put(key, value);
			System.out.println(Thread.currentThread().getName() + "写入完成");
		} catch (Exception e) {
			// TODO: handle exception
		} finally {
			readWriteLock.writeLock().unlock();
		}

	}

	public void get(String key) {
		readWriteLock.readLock().lock();
		try {
			System.out.println(Thread.currentThread().getName() + "读");
			Object o = map.get(key);
			System.out.println(Thread.currentThread().getName() + "读完成");
		} catch (Exception e) {

		} finally {
			readWriteLock.readLock().unlock();
		}

	}
}


阻塞队列

BolckingQueue:阻塞队列:一端取数据,当队列满的时候,会发生写入的阻塞,当队列为空的时候,会发生取的阻塞

BolckingDeque:阻塞双端队列:可以从两端取数据

AbstractQueue:非阻塞队列

四组API

抛出异常不抛出异常,有返回值(true&false)阻塞不阻塞
添加add()offer()put()put(…)
移除remove()poll()take()take(.)
检测队首元素element()peek()--
package org.song.Test;

import java.util.concurrent.ArrayBlockingQueue;

public class Test12 {

	public static void main(String[] args) {
		BlockingQueue1();
	}
	
	public static void BlockingQueue1(){
		ArrayBlockingQueue<String> arrayBlockingQueue = new ArrayBlockingQueue<>(3);
		//ArrayBlockingQueue的参数为队列的大小
		//抛出异常
		arrayBlockingQueue.add("a");
		arrayBlockingQueue.add("b");
		arrayBlockingQueue.add("c");
        
        System.out.println(arrayBlockingQueue.element());
        
		//超过队列容量,抛出异常
		arrayBlockingQueue.add("d");
		
		System.out.println(arrayBlockingQueue.remove());
		System.out.println(arrayBlockingQueue.remove());
		System.out.println(arrayBlockingQueue.remove());
		//队列已为空,抛出异常
		System.out.println(arrayBlockingQueue.remove());
	}

}


package org.song.Test;

import java.util.concurrent.ArrayBlockingQueue;

public class Test12 {

	public static void main(String[] args) {
		BlockingQueue1();
	}
	
	public static void BlockingQueue1(){
		ArrayBlockingQueue<String> arrayBlockingQueue = new ArrayBlockingQueue<>(3);
		//ArrayBlockingQueue的参数为队列的大小
		//不抛出异常,返回true或false
		arrayBlockingQueue.offer("a");
		arrayBlockingQueue.offer("b");
		arrayBlockingQueue.offer("c");
		System.out.println(arrayBlockingQueue.peek());
		//超过队列容量,返回false
		System.out.println(arrayBlockingQueue.offer("d"));

		
		System.out.println(arrayBlockingQueue.poll());
		System.out.println(arrayBlockingQueue.poll());
		System.out.println(arrayBlockingQueue.poll());
		//队列已为空,放回null
		System.out.println(arrayBlockingQueue.poll());
	}

}


package org.song.Test;

import java.util.concurrent.ArrayBlockingQueue;

public class Test12 {

	public static void main(String[] args) throws InterruptedException {
		BlockingQueue1();
	}
	
	public static void BlockingQueue1() throws InterruptedException{
		ArrayBlockingQueue<String> arrayBlockingQueue = new ArrayBlockingQueue<>(3);
		//ArrayBlockingQueue的参数为队列的大小
		//阻塞时,一直阻塞
		arrayBlockingQueue.put("a");
		arrayBlockingQueue.put("b");
		arrayBlockingQueue.put("c");
		//System.out.println(arrayBlockingQueue.peek());
		//超过队列容量,一直等待队列有空间,一直阻塞
		arrayBlockingQueue.put("d");

		
		System.out.println(arrayBlockingQueue.take());
		System.out.println(arrayBlockingQueue.take());
		System.out.println(arrayBlockingQueue.take());
		//队列已为空,队列为空,一直等待队列有元素
		System.out.println(arrayBlockingQueue.take());
	}

}


package org.song.Test;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.TimeUnit;

public class Test12 {

	public static void main(String[] args) throws InterruptedException {
		BlockingQueue1();
	}
	
	public static void BlockingQueue1() throws InterruptedException{
		ArrayBlockingQueue<String> arrayBlockingQueue = new ArrayBlockingQueue<>(3);
		//ArrayBlockingQueue的参数为队列的大小
		//阻塞时,一直阻塞
		arrayBlockingQueue.offer("a");
		arrayBlockingQueue.offer("b");
		arrayBlockingQueue.offer("c");
		//System.out.println(arrayBlockingQueue.peek());
		//超过队列容量,设置时间后,直接结束程序
		arrayBlockingQueue.offer("d",2,TimeUnit.SECONDS);

		
		System.out.println(arrayBlockingQueue.poll());
		System.out.println(arrayBlockingQueue.poll());
		System.out.println(arrayBlockingQueue.poll());
		//队列已为空,设置时间后,直接结束程序
		System.out.println(arrayBlockingQueue.poll(2,TimeUnit.SECONDS));
	}

}


SynchronousQueue

同步队列

没有容量

即最多只能存一个,存进一个 后,等待它取出来,才能存

package org.song.Test;

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.SynchronousQueue;

public class Test13 {

	public static void main(String[] args) {
		BlockingQueue<String> synchronousQueue = new SynchronousQueue<>();

		new Thread(() -> {
			try {
				System.out.println(Thread.currentThread().getName()+"put1");
				synchronousQueue.put("a");
				System.out.println(Thread.currentThread().getName()+"put1");
				synchronousQueue.put("b");
				System.out.println(Thread.currentThread().getName()+"put1");
				synchronousQueue.put("c");
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		},"A").start();
		
		new Thread(() -> {
			try {
				System.out.println(Thread.currentThread().getName()+"take1");
				synchronousQueue.take();
				System.out.println(Thread.currentThread().getName()+"put1");
				synchronousQueue.take();
				System.out.println(Thread.currentThread().getName()+"put1");
				synchronousQueue.take();
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		},"B").start();


	}

}


线程池(重点)

池化技术

程序的运行、本质:占用系统资源!优化资源的使用-》池化技术

线程池、连接池、内存池、对象池

池化技术:事先准备好一些资源,有人要用,就来我这里拿,用完后还给我

线程池的好处:

  1. 降低资源的消耗
  2. 提高的响应的速度
  3. 方便管理

线程复用、可以控制最大并发数、管理线程

线程池:三大方法

  1. ExecutorService threadPool = Executors.newSingleThreadExecutor(); 单线程,固定只有一个线程
  2. ExecutorService threadPool = Executors.newFixedThreadPool(5); 通过传递的参数,生成一个固定大小的线程池
  3. ExecutorService threadPool = Executors.newCachedThreadPool(); 遇强则强
package org.song.Test;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Test14 {

	public static void main(String[] args) {
		// 线程池的三大方法
//		ExecutorService threadPool = Executors.newSingleThreadExecutor();
//		ExecutorService threadPool = Executors.newFixedThreadPool(5);
		ExecutorService threadPool = Executors.newCachedThreadPool();
		for (int i = 0; i < 100; i++) {
			threadPool.execute(() -> {
				System.out.println(Thread.currentThread().getName()+" ok");
			});
		}

	}

}


线程池:七大参数

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iqjPnChC-1630498026446)(C:\Users\宋杰的电脑\Downloads\未命名文件.jpg)]

//底层代码
    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;
    }

ExecutorService threadPool=new ThreadPoolExecutor(

corePoolSize, //核心线程

maximumPoolSize, //最大核心线程

keepAliveTime, //超时释放时间

unit, //TimeUnit 的单位

workQueue, //阻塞队列,相当于银行的候客区

threadFactory, //创建线程的工厂类,一般不改动

handler)//当线程数量超过最大核心线程+阻塞队列数,则会拒绝线程,有四种拒绝策略

  1. AbortPolicy() 抛出异常
  2. CallerRunPolicy() 哪来回哪里去
  3. DiscardPolicy() 丢弃任务,不抛出异常
  4. DiscardOldestPolicy() 与最早的沟通,成功则替换,反之丢弃
package org.song.Test;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class Test15 {

	public static void main(String[] args) {
		ExecutorService threadPool = new ThreadPoolExecutor(
				2, 
				5, 
				3, 
				TimeUnit.SECONDS, 
				new LinkedBlockingQueue<>(3),
				Executors.defaultThreadFactory(), 
				new ThreadPoolExecutor.DiscardOldestPolicy());
		
		/*
		 * new ThreadPoolExecutor.AbortPolicy() 队列满了,抛出异常
		 * new ThreadPoolExecutor.CallerRunsPolicy() 队列满了,哪里来回哪里去,例如main
		 * new ThreadPoolExecutor.DiscardPolicy() 队列满了,不抛出异常
		 * new ThreadPoolExecuto.DiscardOldestPolicy() 队列满了,尝试与最早的线程沟通,其执行结束则替换,反之抛弃任务
		 */
		
		for (int i = 0; i < 9; i++) {
			threadPool.execute(() -> {
				System.out.println(Thread.currentThread().getName()+" ok");
			});
		}

	}

}


线程池执行定时任务

  • https://segmentfault.com/a/1190000008038848
public class ScheduledExecutorServiceTest {

    public static void main(String[] args) throws Exception {
        ScheduledExecutorService timer = Executors.newSingleThreadScheduledExecutor();
        
        TimerTask timerTask = new TimerTask(2000); // 任务需要 2000 ms 才能执行完毕

        System.out.printf("起始时间:%s\n\n", new SimpleDateFormat("HH:mm:ss").format(new Date()));

        // 延时 1 秒后,按 3 秒的周期执行任务
        timer.scheduleAtFixedRate(timerTask, 1000, 3000, TimeUnit.MILLISECONDS);
    }

    private static class TimerTask implements Runnable {

        private final int sleepTime;
        private final SimpleDateFormat dateFormat;

        public TimerTask(int sleepTime) {
            this.sleepTime = sleepTime;
            dateFormat = new SimpleDateFormat("HH:mm:ss");
        }

        @Override
        public void run() {
            System.out.println("任务开始,当前时间:" + dateFormat.format(new Date()));

            try {
                System.out.println("模拟任务运行...");
                Thread.sleep(sleepTime);
            } catch (InterruptedException ex) {
                ex.printStackTrace(System.err);
            }

            System.out.println("任务结束,当前时间:" + dateFormat.format(new Date()));
            System.out.println();
        }

    }

设置最大线程池(调优)

  • CPU 密集型

    通过查看你的电脑有几个cpu,来设置最大核心线程

    package org.song.Test;
    
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    import java.util.concurrent.LinkedBlockingQueue;
    import java.util.concurrent.ThreadPoolExecutor;
    import java.util.concurrent.TimeUnit;
    
    public class Test15 {
    
    	public static void main(String[] args) {
    		
    		System.out.println(Runtime.getRuntime().availableProcessors());
    		ExecutorService threadPool = new ThreadPoolExecutor(
    				2, 
    				Runtime.getRuntime().availableProcessors(), 
    				3, 
    				TimeUnit.SECONDS, 
    				new LinkedBlockingQueue<>(3),
    				Executors.defaultThreadFactory(), 
    				new ThreadPoolExecutor.DiscardOldestPolicy());
    		
    		
    		for (int i = 0; i < 9; i++) {
    			threadPool.execute(() -> {
    				System.out.println(Thread.currentThread().getName()+" ok");
    			});
    		}
    
    	}
    
    }
    
    
    
  • IO 密集型

    通过设置你的系统最消耗io的任务个数的两倍

四大函数式接口(重要)

新时代的程序员:lambda表达式、链式编程、函数式接口、Stream流计算

函数式接口:只有一个方法的接口;简化编程模型

  • Fuction:有一个输入参数,一个输出,只要是函数式接口,可以lambda表达式

    简化:Fuction<String,String> fuction=(str)->{return str;};

  • Predicate:断定型接口,有一个输入参数,返回值为boolean值

    Predicate predicate2=(sss)->{return sss.isEmpty();};

  • Consumer:消费型接口,只有输入,没有返回值

    Consumer cuConsumer2=(str)->{return;};

  • Supplier:供给型接口,没有参数,只有返回值

    Supplier supplier2=()->{return 1024;};

	public static void main(String[] args) {
		Function<String, String> function = new Function<String, String>() {
			
			@Override
			public String apply(String t) {
				return t;
			}
		};
		//简化
		Function function2 =(str)->{return str;};
		System.out.println(function.apply("hhh"));
	}

	public static void main(String[] args) {
		Predicate predicate=new Predicate<String>() {

			@Override
			public boolean test(String t) {
				
				return t.isEmpty();
			}
		};
		//简化
		Predicate<String> predicate2=(sss)->{return sss.isEmpty();};
		System.out.println(predicate.test("sss"));
		
	}

	public static void main(String[] args) {
		Consumer<String> consumer=new Consumer<String>() {
			
			@Override
			public void accept(String t) {
				System.out.println(t);	
			}
		};
		Consumer<String> cuConsumer2=(str)->{return;};
		consumer.accept("你好");
	}


	public static void main(String[] args) {
		Supplier<Integer> supplier=new Supplier<Integer>() {
			
			@Override
			public Integer get() {
				// TODO Auto-generated method stub
				return 1024;
			}
		};
		Supplier<Integer> supplier2=()->{return 1024;};
		System.out.println(supplier.get());
	}

Stream流式计算

什么是Stream流式计算?

存储+计算

ForkJoin

ForkJoin 在JDK1.7后, 并发执行任务,提高效率,大数据量

分支合并

ForkJoin的特点:工作窃取

在a分了多个任务,b分了多个任务时,当a完成的时候,但b还为完成,此时a会窃取b的任务执行,以提高效率 (双端队列)

package org.song.Test;

import java.util.concurrent.RecursiveTask;

public class Test17 extends RecursiveTask<Long> {
	
	private Long start;
	private Long end;
	
	//使用forkjoin的临界值
	private Long temp=10000L;
	
	public Test17(Long start,Long end) {
		this.start=start;
		this.end=end;
	}
	
	
	@Override
	protected Long compute() {
		
		if((end-start)<temp){
			Long sum=0L;
			for (Long i = start; i < end; i++) {
				sum+=i;
			}
			return sum;
		}else{
			//将一个任务拆分成多个任务
			
			long middle=(end+start)/2;
			Test17 task1=new Test17(start, middle);
			task1.fork();
			Test17 task2=new Test17(middle+1, end);
			task2.fork();
			
			//forkjoinTask.fork()  将拆分后的任务加入队列中
			//forkjoinTask.join()  获取子任务的返回值
			
			return task1.join()+task2.join();
			
		}
	}
	
}


package org.song.Test;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
import java.util.stream.LongStream;

public class Test18 {

	public static void main(String[] args) throws InterruptedException, ExecutionException {
		test1();
	}
	
	//使用传统的求和
	public static void test1(){
		Long sum=0L;
		
		Long start=System.currentTimeMillis();
		for (Long i = 1L; i <= 10_0000_0000; i++) {
			sum+=i;
		}
		Long end=System.currentTimeMillis();
		System.out.println("时间:"+(end-start));
	}
	
	//使用forkjoin
	public static void test2() throws InterruptedException, ExecutionException{
		
		
		Long start=System.currentTimeMillis();
		
		ForkJoinPool forkJoinPool = new ForkJoinPool();
		ForkJoinTask<Long> task=new Test17(0L, 10_0000_0000L);
		//forkjoinpool的两个执行方法 executor(无返回)和submit(返回一个forkjoinTask)
		ForkJoinTask<Long> submit = forkJoinPool.submit(task);
		Long sum=submit.get();
		Long end=System.currentTimeMillis();
		System.out.println("sum="+sum+"时间:"+(end-start));
	}
	
	//使用Stream并发流
	
	public static void test3(){
		
		Long start=System.currentTimeMillis();
		
		Long sum=LongStream.rangeClosed(0L, 10_0000_0000L).parallel().reduce(0, Long::sum);
		
		Long end=System.currentTimeMillis();
		System.out.println("时间:"+(end-start));
	}
	
}


使用步骤:

  1. 让一个计算类继承RecursiveTask ,它是ForkjoinTask的子类,有一个返回结果,重写他的方法compute(),可将多个任务分为几个小任务,fork()和join() fork分 join合
  2. 使用forkjoinPool的executor(无返回)合submit(有返回)
  3. 使用submit.get()获取返回结果

异步回凋

类似于Ajax

CompletableFutrue

  • 无返回runAsysc

  • 有返回supplyAsysc

    获取返回值:whencomplete exceptionally

package org.song.Test;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;

public class Test19 {

	@SuppressWarnings("static-access")
	public static void main(String[] args) throws InterruptedException, ExecutionException {
		/*
		 * Futrue 其实现类为Completablefutrue 1、异步执行 2、成功回调 3、失败回调
		 * 
		 */

		// 无返回的回调 runAsysc
		CompletableFuture<Void> completableFuture = new CompletableFuture<String>().runAsync(() -> {
			try {
				TimeUnit.SECONDS.sleep(2);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			System.out.println(Thread.currentThread().getName() + "runAsysc");
		});

		System.out.println("hello");
		completableFuture.get();

		// 有返回的回调 whenComplete      exceptionally错误信息
		CompletableFuture<Integer> completableFuture2 = new CompletableFuture<Integer>().supplyAsync(() -> {
			int a=10/0;
			return 1024;
		});

		System.out.println(completableFuture2.whenComplete((t, u) -> {
			System.out.println(t); //成功回调返回 return 1024
			System.out.println(u); //失败回调返回错误信息
		}).exceptionally((e) -> {
			System.out.println(e.getMessage());
			return 233;
		}).get());

	}

}


JMM

请你谈谈Volatile的理解

Volatile时java虚拟机提供轻量级的同步机制

  1. 保证可见性
  2. 不保证原子性
  3. 禁止指令重排

什么是JMM

JMM:java内存模型,不存在的东西,概念!

关于JMM的一些同步的约定

  1. 线程解锁前,必须把共享变量立刻刷回主存
  2. 线程加锁前,必须读取主存中的最新值到工厂内存中
  3. 加锁合解锁是同一把锁

线程:工作内存、主内存

可见性:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xDw60g3m-1630498026449)(C:\Users\宋杰的电脑\Downloads\未命名文件 (1)].jpg)

lock(锁定):作用于主内存的变量,把一个变量标识为一条线程独占的状态。

unclock(解锁):作用于主内存的变量,把一个处于锁定的状态释放出来。

read(读取):作用于主内存的变量,把一个变量的值从主内存传输到线程的工作内存中

load(载入):作用于工作内存的变量,把read操作从主内存 得到的变量值放入工作内存的变量副本中。

use(使用):作用于工作内存的变量,把工作内存中一个变量的值传递给执行引擎,每当虚拟机遇到一个需要使用到变量的 值的字节码指令时将会执行这个操作。

assign(赋值):作用于工作内存的变量,把一个从执行引擎接收到的值 赋值给工作内存的变量,每当虚拟机遇到一个给变 量赋值的字节码指令时执行这个操作。

store(存储):作用于工作内存的变量,把工作内存中的一个变量的值传递到主内存,以便write操作使用。

write(写入):作用于主内存的变量,把store操作从工作内存中得到的变量的值放入主内存的变量中。

Volatile

  1. 保证可见性
  2. 不保证原子性
  3. 禁止指令重排
package org.song.Test;

import java.util.concurrent.TimeUnit;

//volatile
//保证可见性
public class Test20 {

	private static int num;
	private  volatile static int num;
	public static void main(String[] args) throws InterruptedException {
		new Thread(() -> {
			while(num==0){
				
			}
		}).start();
		
		TimeUnit.SECONDS.sleep(2);
		
		num=1;
		
		System.out.println("main");
	}

}


package org.song.Test;

/*
 * Volatile
 * 不保证原子性
 */
public class Test21 {
	private volatile static int num;

	public static void add() {
		num++;
	}

	public static void main(String[] args) {
		for (int i = 0; i < 20; i++) {
			new Thread(() -> {
				for (int j = 0; j < 1000; j++) {
					add();
				}
				
			}).start();
		}
		
		System.out.println(num);
        //加了Volatile结果依旧小于20000
	}

}


package org.song.Test;

/*
 * Volatile
 * 不保证原子性
 */
public class Test21 {
	private volatile static int num;

	public static void add() {
		num++;
	}

	public static void main(String[] args) {
		for (int i = 0; i < 20; i++) {
			new Thread(() -> {
				for (int j = 0; j < 1000; j++) {
					add();
				}
				
			}).start();
		}
		
		while(Thread.activeCount()>2){
			Thread.yield();
		}
		
		System.out.println(num);
	}

}


解决方法:

  1. 加锁,使方法成为同步方法
  2. 使用AtomicIntegerg类定义:java.util.concurrent.Atomic
	private static AtomicInteger num=new AtomicInteger();

	public static void add() {
		num.getAndIncrement();
	}

指令重排

什么是指令重排:你写的程序,计算机并不是按照你写的那样去执行

源代码–》编译器优化的重排–》指令并行也可能重排–》内存系统也会重排–》执行

处理器在进行指令重排的时候,考虑:数据之间的依赖性

int x=1;//1
int y=2;//2
x=x+5;//3
y=x*x;//
//你希望的执行顺序是1234,但cpu指令重排不一定是1234,但发生错误的概率比较小

volatile可以避免指令重排:

内存屏障,cpu指令,作用:

  1. 保证特定操作的执行顺序
  2. 可以保证某些变量的内存可见性(实现了可见性)
  3. 在volatile修饰的属性上下均加上了内存屏障,禁止了指令重排

彻底玩转单例模式

饿汉式单例、DCL懒汉式单例

反射中的setAccessible(true)

  1. 通知jdk关闭安全检查
  2. 提高了反射创建对象的速度,但有安全性的问题
  3. 可以破环单例模式

饿汉式单例

package org.song.Man;

public class HungryMan {
	//缺点:浪费内存资源
	
	//构造器私有
	private HungryMan(){
		
	}
	
	//一开始就创建对象
	private final static HungryMan HUNGRY_MAN=new HungryMan();
	
	//使用getInstace返回这个对象
	public static HungryMan getInstance(){
		return HUNGRY_MAN;
	}
}

懒汉式单例

01:普通懒汉式

package org.song.Man;

public class LazyMan01 {
	//单线程ok,多线程不安全
	private LazyMan01(){
		
	}
	
	private static LazyMan01 lazyMan01;
	
	public static LazyMan01 getInstance(){
		if(lazyMan01==null){
			lazyMan01=new LazyMan01();
		}
		
		return lazyMan01;
	}
}

02:双重监测懒汉式-DCL懒汉式

package org.song.Man;

public class LazyMan01 {

	private LazyMan01() {
		System.out.println(Thread.currentThread().getName()+" ok");
	}

	private volatile static LazyMan01 lazyMan01;

	//双重监测
	public static LazyMan01 getInstance() {
		if(lazyMan01==null){
			synchronized(LazyMan01.class){
				if (lazyMan01 == null) {
					lazyMan01 = new LazyMan01();
					/*
					 * 实例化这个对象,它不是一个原子操作
					 * 1、分配内存空间
					 * 2、初始化对象
					 * 3、将对象指向内存空间
					 * 可能在执行顺序是132,这样的话就没有初始化对像,存在问题
					 * 所以需要在上面加上volatile关键字,禁止指令重排
					 */
				}
			}
		}


		return lazyMan01;
	}
	
	//多线程不安全
	public static void main(String[] args) {
		for (int i = 0; i < 10; i++) {
			new Thread(() -> {
				getInstance();
			}).start();
		}
	}
}


03:静态内部类

package org.song.Man;

public class Holder {
	private Holder(){
		
	}
	
	public static Holder getInstance(){
		return MyHolder.HOLDER;
	}
	
	public static class MyHolder{
		private final static Holder HOLDER=new Holder();
	}
}


04:通过反射破环单例模式

package org.song.Man;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

public class LazyMan01 {

	private LazyMan01() {
		System.out.println(Thread.currentThread().getName()+" ok");
	}

	private static LazyMan01 lazyMan01;

	//双重监测
	public static LazyMan01 getInstance() {
		if(lazyMan01==null){
			synchronized(LazyMan01.class){
				if (lazyMan01 == null) {
					lazyMan01 = new LazyMan01();
					/*
					 * DCL懒汉式缺点:实例化这个对象,它不是一个原子操作
					 * 1、分配内存空间
					 * 2、初始化对象
					 * 3、将对象指向内存空间
					 * 可能在执行顺序是132,这样的话就没有初始化对像,存在问题
					 */
				}
			}
		}


		return lazyMan01;
	}
	
	public static void main(String[] args) throws NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
		//通过反射破环单例模式
		Constructor<LazyMan01> declaredConstructor = LazyMan01.class.getDeclaredConstructor(null);
		declaredConstructor.setAccessible(true);
		LazyMan01 newInstance = declaredConstructor.newInstance(null);
		LazyMan01 lazyMan01=LazyMan01.getInstance();
		System.out.println(newInstance);
		System.out.println(lazyMan01);
	}
}


解决方法:它通过反射调取无参构造器,那么在无参构造器中再添加一个synchronized(LazyMan01.class),来锁住

package org.song.Man;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;


public class LazyMan01 {

	private LazyMan01() {
		synchronized(LazyMan01.class){
			throw new RuntimeException("不要试图通过反射破环单例模式");
		}
		//System.out.println(Thread.currentThread().getName()+" ok");
	}

	private static LazyMan01 lazyMan01;

	//双重监测
	public static LazyMan01 getInstance() {
		if(lazyMan01==null){
			synchronized(LazyMan01.class){
				if (lazyMan01 == null) {
					lazyMan01 = new LazyMan01();
					/*
					 * DCL懒汉式缺点:实例化这个对象,它不是一个原子操作
					 * 1、分配内存空间
					 * 2、初始化对象
					 * 3、将对象指向内存空间
					 * 可能在执行顺序是132,这样的话就没有初始化对像,存在问题
					 */
				}
			}
		}


		return lazyMan01;
	}
	
	public static void main(String[] args) throws NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
		//通过反射破环单例模式
		Constructor<LazyMan01> declaredConstructor = LazyMan01.class.getDeclaredConstructor(null);
		declaredConstructor.setAccessible(true);
		LazyMan01 newInstance = declaredConstructor.newInstance(null);
		LazyMan01 lazyMan01=LazyMan01.getInstance();
		System.out.println(newInstance);
		System.out.println(lazyMan01);
	}
}


05:如果是之前没有使用构造器构造类,直接使用两个反射构造?

解决方法:通过一个标志位判断

package org.song.Man;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;


public class LazyMan01 {

	private static boolean flag=false;
	private LazyMan01() {
		if(flag==false){
			flag=true;
		}else{
			synchronized(LazyMan01.class){
				throw new RuntimeException("不要试图通过反射破环单例模式");
			}
		}
		//System.out.println(Thread.currentThread().getName()+" ok");
	}

	private static LazyMan01 lazyMan01;

	//双重监测
	public static LazyMan01 getInstance() {
		if(lazyMan01==null){
			synchronized(LazyMan01.class){
				if (lazyMan01 == null) {
					lazyMan01 = new LazyMan01();
					/*
					 * DCL懒汉式缺点:实例化这个对象,它不是一个原子操作
					 * 1、分配内存空间
					 * 2、初始化对象
					 * 3、将对象指向内存空间
					 * 可能在执行顺序是132,这样的话就没有初始化对像,存在问题
					 */
				}
			}
		}


		return lazyMan01;
	}
	
	public static void main(String[] args) throws NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
		//通过反射破环单例模式
		Constructor<LazyMan01> declaredConstructor = LazyMan01.class.getDeclaredConstructor(null);
		declaredConstructor.setAccessible(true);
		LazyMan01 newInstance = declaredConstructor.newInstance(null);
		//LazyMan01 lazyMan01=LazyMan01.getInstance();
		LazyMan01 newInstance2 = declaredConstructor.newInstance(null);
		System.out.println(newInstance);
		System.out.println(newInstance2);
	}
}


06:通过是反射获取属性,一样可以破环单例模式

07:反射不能破环枚举的单例模式

package org.song.Man;

public enum Emun {
	//反射不能破环枚举的单例模式
	INSTANCE;
	public Emun getInstance(){
		return INSTANCE;
	}
}


枚举的最终反编译构造器有两个参数(构造器)string.class和int.class

深入理解CAS

AtomicInteger:int类型的原子类

CAS:compareAndSet

package org.song.atomic;

import java.util.concurrent.atomic.AtomicInteger;

public class CAS {

	public static void main(String[] args) {
		AtomicInteger num=new AtomicInteger(11);
		
		//期望并更新
		//public final boolean compareAndSet(int expect, int update)
		//unsafe类是java通过c++操作内存的后面
		num.compareAndSet(11, 20);
		System.out.println(num.get());
		//原子底层并发原语,效率很高
		num.compareAndSet(20, 21);
		System.out.println(num.get());

	}

}

比较当前工作内存的值和主内存的值,如果这个值是期望的,那么执行操作,如果不是就一直循环(自旋锁

缺点:

  1. 循环会耗时
  2. 一次只能保证一个共享变量的原子性
  3. ABA问题

CAS:ABA问题(狸猫换太子)

一个线程操作一个主内存的数据后,再将这个数据修改回去,但另一个线程不知道这个数据被操作过:这就是ABA问题;

package org.song.atomic;

import java.util.concurrent.atomic.AtomicInteger;

public class CAS {

	public static void main(String[] args) {
		AtomicInteger num=new AtomicInteger(11);
		
		//捣蛋线程
		num.compareAndSet(11, 20);
		System.out.println(num.get());
		num.compareAndSet(20, 11);
		System.out.println(num.get());
		
		
		//另一个线程
		num.compareAndSet(11, 21);
		System.out.println(num.get());

	}

}

解决方法:原子引用

原子引用

AtomicStampedReference

AtomicStampedReference atomicStampedReference = new AtomicStampedReference<>(10, 1);

atomicStampedReference.getStamp()

  1. 线程等待,是为了让获取stemp一致
  2. Integer的引用的坑,它有一个对象缓存机制,其范围再-128-127,在这个区间外,将会生成新的应用对象,比较时建议使用equals
package org.song.atomic;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicStampedReference;

public class CAS {

	public static void main(String[] args) {
		AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<>(10, 1);
		new Thread(() -> {
			try {
				TimeUnit.SECONDS.sleep(2);
				System.out.println(atomicStampedReference.getStamp());
				atomicStampedReference.compareAndSet(10, 20, atomicStampedReference.getStamp(), atomicStampedReference.getStamp()+1);
				System.out.println(atomicStampedReference.getStamp());
				atomicStampedReference.compareAndSet(20, 10, atomicStampedReference.getStamp(), atomicStampedReference.getStamp()+1);
				System.out.println(atomicStampedReference.getStamp());
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			
		}).start();
		
		new Thread(() -> {
			try {
				TimeUnit.SECONDS.sleep(2);
				int stemp=atomicStampedReference.getStamp();
				System.out.println(stemp);
				System.out.println(atomicStampedReference.compareAndSet(10, 20, stemp, stemp+1));
				System.out.println(atomicStampedReference.getStamp());
				System.out.println(atomicStampedReference.compareAndSet(20, 10, stemp, stemp+1));
				System.out.println(atomicStampedReference.getStamp());
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			
		}).start();
	}

}


可重入锁(递归锁)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xY02ZYP4-1630498026450)(C:\Users\宋杰的电脑\AppData\Roaming\Typora\typora-user-images\1630481757807.png)]

  1. 拿到最外面的锁,自然就拿到里面的锁
  2. 锁必须配对
  3. synchronized和lock都是可重入锁
package org.song.atomic;

public class MyLock01 {

	public static void main(String[] args) {
		Phone phone = new Phone();
		new Thread(()->{
			phone.sms();
		}).start();
		new Thread(()->{
			phone.phone();
		}).start();
	}

}
class Phone{
	
	public synchronized void sms(){
		System.out.println(Thread.currentThread().getName()+"sms");
		phone();
	}
	
	public synchronized void phone(){
		System.out.println(Thread.currentThread().getName()+"phone");
	}
	
}


package org.song.atomic;

import java.util.concurrent.locks.ReentrantLock;

public class MyLock01 {

	public static void main(String[] args) {
		Phone phone = new Phone();
		new Thread(() -> {
			phone.sms();
		}).start();
		new Thread(() -> {
			phone.phone();
		}).start();
	}

}

class Phone {
	ReentrantLock reentrantLock = new ReentrantLock();
	public void sms() {
		try {
			reentrantLock.lock();
			reentrantLock.lock();
			System.out.println(Thread.currentThread().getName() + "sms");
			phone();
		} catch (Exception e) {
			// TODO: handle exception
		} finally {
			reentrantLock.unlock();
			reentrantLock.unlock();
		}

	}

	public void phone() {

		try {
			reentrantLock.lock();
			System.out.println(Thread.currentThread().getName() + "phone");
		} catch (Exception e) {
			// TODO: handle exception
		} finally {
			reentrantLock.unlock();
		}

	}

}


自旋锁

  1. 基于CAS实现的自旋锁
  2. 会一直等待
package org.song.atomic;

import java.util.concurrent.atomic.AtomicReference;

public class SpinLock {
	//原子引用的类型时对象时,默认为null
	AtomicReference<Thread> atomicReference=new AtomicReference<Thread>();
	
	public void MyLock(){	
		System.out.println(Thread.currentThread().getName()+" lock");
		while(!atomicReference.compareAndSet(null, Thread.currentThread())){
			
		}
	}
	
	public void MyUnLock(){	
		System.out.println(Thread.currentThread().getName()+" unlock");
		atomicReference.compareAndSet(Thread.currentThread(),null);	
	}
}


package org.song.atomic;

import java.util.concurrent.TimeUnit;

public class Test01 {

	public static void main(String[] args) {
		SpinLock lock=new SpinLock();
		
		new Thread(()->{
			lock.MyLock();
			try {
				TimeUnit.SECONDS.sleep(5);
			} catch (Exception e) {
				// TODO: handle exception
			}finally {
				lock.MyUnLock();
			}
		},"T1").start();
		
		new Thread(()->{
			lock.MyLock();
			try {
				
			} catch (Exception e) {
				// TODO: handle exception
			}finally {
				lock.MyUnLock();
			}
		},"T2").start();

	}

}


死锁

package org.song.atomic;

import java.util.concurrent.TimeUnit;

public class MyDeadLock {

	public static void main(String[] args) {
		String lockA="xxx";
		String lockB="yyy";
		
		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 "+lockA+" get "+lockB);
			try {
				TimeUnit.SECONDS.sleep(6);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			synchronized (lockB) {
				System.out.println(Thread.currentThread().getName()+"lockB "+lockB+" get "+lockA);
			}
		}
		
	}
}


排查问题

  1. 看日志
  2. 查看堆栈信息
    • jps -l 查看到想看的进程号
    • jstack 进程号
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值