源码+官方文档(面试高频)
普通的线程代码:继承Thread类
Runnable接口:没有返回值,效率相比于Callable接口较低
关于线程和进程,如果不能用一句话说出来,说明你掌握的还不够扎实!
进程:
Java真的能开启线程吗?
并发:(多线程操作同一资源)
并行:(多个人同时走路)
并发编程的本质:充分利用CPU的资源
public enum State {
NEW,//新生
RUNNABLE,//运行
BLOCKED,//阻塞
WAITING,//等待,一直等待
TIMED_WAITING,//超时等待,过期不候
TERMINATED;//终止
}
来自不同的类:
关于锁的释放
使用的范围是不同的
是否需要捕获异常
参考链接
参考链接
作用:
保证线程安全,例如当多个线程访问统一资源时,会发生数据紊乱问题;
用法:
用来修饰代码块和方法
怎么理解:
队列+锁
实例:
//不安全的线程
import sun.security.krb5.internal.Ticket;
/*
可以买到负数的票的原因:
当只剩下一张票时,三个线程都认为是有票;
因此它们都把“1”拿到自己的内存;
第一次拿后,对象内存剩余0;
第二次拿后,对象内存剩余-1;
*/
public class UnsafeTicket {
public static void main(String[] args) {
BuyTicket ticket = new BuyTicket();
new Thread(ticket,"倒霉的我").start();
new Thread(ticket,"幸运的你们").start();
new Thread(ticket,"可恶的黄牛党").start();
}
}
class BuyTicket implements Runnable{
private int tickets = 10;
private boolean flag = true;
@Override
public void run() {
while (flag){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
buy();
}
}
//在方法名前加上"synchronized",那么该方法就变成了同步方法了
//!!!锁的是"this"!!!
//'synchronized'默认锁的是this
private synchronized void buy(){
if (tickets<=0){
flag=false;
//这个return放在这里,如果符合if条件,那么返回出去,不执行下面的sout语句
return;
}
System.out.println(Thread.currentThread().getName()+"买到了第"+tickets--+"张票");
}
}
公平锁:十分公平,先来后到(排队)3h 3s
非公平锁:不公平,可以插队(为了“公平”,引入非公平锁)–默认
Lock锁“三部曲”:
1、创建锁对象
2、加锁
3、解锁
package com.my.demo;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
//以买票为例
/*
* 真正的多线程开发(公司中的)--降低耦合性(考虑怎么解耦?)
* 把线程当作一个资源类,没有任何的附属操作
* 1、属性。方法
*
* */
public class SaleTicket01 {
public static void main(String[] args) {
//并发:多个线程共享同一资源,把资源类丢入线程
//重点理解怎么解耦?
Ticket01 ticket = new Ticket01();
new Thread(()->{
for (int i = 0; i < 30; i++) {
ticket.sale();
}
},"A").start();
new Thread(()->{
for (int i = 0; i < 30; i++) {
ticket.sale();
}
},"B").start();
new Thread(()->{
for (int i = 0; i < 30; i++) {
ticket.sale();
}
},"C").start();
}
}
//OOP编程
class Ticket01{
private int number=20;
Lock lock = new ReentrantLock();
/*
* Lock三部曲:
* 1、new ReentrantLock();相当于创建锁
* 2、lock.lock();//加锁
* 3、lock.unlock();//解锁
* */
public void sale(){
lock.lock();//加锁
try {
//业务代码
if (number>0){
System.out.println(Thread.currentThread().getName()+"卖出了第:"+(number--)+"还剩下:"+number+"张票");
}
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();//解锁
}
}
}
锁是什么?如何判断锁的是谁?
面试:单例模式、排序算法、生产者和消费者问题、死锁
package com.my.pc;
/*
* 线程之间的通信问题:生产者和消费者问题(等待唤醒,通知唤醒)
*
* 线程交替执行:
* 生产者和消费者操作同一个变量
* 生产者:+1
* 消费者:-1
* */
public class Test {
public static void main(String[] args) {
Data data = new Data();
new Thread(()->{
try {
for (int i = 0; i < 10; i++) {
data.increment();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
},"生产者").start();
new Thread(()->{
try {
for (int i = 0; i < 10; i++) {
data.decrement();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
},"消费者").start();
}
}
//资源类
/*
* 编写资源类的思路:
* 判断等待、业务、通知
* */
class Data{
private int number = 0;
public synchronized void increment() throws InterruptedException {
if (number!=0){
//生产者等待
this.wait();
}
number++;
System.out.println(Thread.currentThread().getName()+"=>"+number);
//唤醒消费者进程进行“消费”
this.notifyAll();
}
public synchronized void decrement() throws InterruptedException {
if (number==0){
//消费者等待
this.wait();
}
number--;
System.out.println(Thread.currentThread().getName()+"=>"+number);
//唤醒生产者进程进行“生产”
this.notifyAll();
}
}
这样会出现问题!!–虚假唤醒
场景:当出现多个生产者和消费者时,会出现数据紊乱?
原因:就是这个if语句只判断一次:当消费者线程阻塞,来唤醒生产者线程时,多个生产者线程都会被唤醒,但是只有第二个生产者线程执行了if判断,然后第二个生产者线程阻塞(基于if语句的机理),因为第一个生产者线程能够正常执行,而剩余的其他生产者线程,则会执行if语句后面的方法(“意思就是后面的生产者线程在if判断前面生产者线程时,利用了if语句的bug捡了漏”)
(1)if判断流水线状态为空时,线程被阻塞,这时if判断就完成了,线程被唤醒后直接执行线程剩余操作
(2)while判断流水线状态为空时,线程被阻塞,这时的while循环没有完成,线程被唤醒后会先进行while判断
解决方法:
把if语句改为while语句
package com.my.pc;
/*
* 线程之间的通信问题:生产者和消费者问题(等待唤醒,通知唤醒)
*
* 线程交替执行:
* 生产者和消费者操作同一个变量
* 生产者:+1
* 消费者:-1
* */
public class Test {
public static void main(String[] args) {
Data data = new Data();
new Thread(()->{
try {
for (int i = 0; i < 10; i++) {
data.increment();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
},"生产者").start();
new Thread(()->{
try {
for (int i = 0; i < 10; i++) {
data.decrement();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
},"消费者").start();
new Thread(()->{
try {
for (int i = 0; i < 10; i++) {
data.decrement();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
},"C").start();
new Thread(()->{
try {
for (int i = 0; i < 10; i++) {
data.decrement();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
},"D").start();
}
}
//资源类
/*
* 编写资源类的思路:
* 判断等待、业务、通知
* */
class Data{
private int number = 0;
public synchronized void increment() throws InterruptedException {
/* if (number!=0){
//生产者等待
this.wait();
}*/
while(number!=0){
//生产者等待
this.wait();
}
number++;
System.out.println(Thread.currentThread().getName()+"=>"+number);
//唤醒消费者进程进行“消费”
this.notifyAll();
}
public synchronized void decrement() throws InterruptedException {
/*if (number==0){
//消费者等待
this.wait();
}*/
while (number==0){
//消费者等待
this.wait();
}
number--;
System.out.println(Thread.currentThread().getName()+"=>"+number);
//唤醒生产者进程进行“生产”
this.notifyAll();
}
}
package com.my.pc;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/*
* 线程之间的通信问题:生产者和消费者问题(等待唤醒,通知唤醒)
*
* 线程交替执行:
* 生产者和消费者操作同一个变量
* 生产者:+1
* 消费者:-1
* */
public class Test01 {
public static void main(String[] args) {
Data01 data = new Data01();
new Thread(()->{
try {
for (int i = 0; i < 10; i++) {
data.increment();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
},"生产者").start();
new Thread(()->{
try {
for (int i = 0; i < 10; i++) {
data.decrement();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
},"消费者").start();
new Thread(()->{
try {
for (int i = 0; i < 10; i++) {
data.decrement();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
},"C").start();
new Thread(()->{
try {
for (int i = 0; i < 10; i++) {
data.decrement();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
},"D").start();
}
}
//资源类
/*
* 编写资源类的思路:
* 判断等待、业务、通知
* */
class Data01{
private int number = 0;
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
public synchronized void increment() throws InterruptedException {
//加锁
lock.lock();
try {
//业务代码
while (number!=0){
condition.await();
}
number++;
System.out.println(Thread.currentThread().getName()+"=>"+number);
//唤醒消费者进程进行“消费”
condition.signalAll();
} catch (Exception e) {
e.printStackTrace();
} finally {
//释放锁
lock.unlock();
}
}
public synchronized void decrement() throws InterruptedException {
lock.lock();
try {
while (number==0){
condition.await();
}
number--;
System.out.println(Thread.currentThread().getName()+"=>"+number);
//唤醒生产者进程进行“生产”
condition.signalAll();
} catch (Exception e) {
e.printStackTrace();
} finally {
//释放锁
lock.unlock();
}
}
}
Condition绝不仅仅只是覆盖原来的wait和notify,它肯定还有别的优势
–Condition实现精准通知唤醒
package com.my.pc;
/*
* 该例来了解Condition的优势和好处
* */
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
//让线程按照顺序执行,先执行线程A,再执行线程B,最后执行线程C,之后再从头执行线程A
public class Test02 {
public static void main(String[] args) {
Data02 data02 = new Data02();
new Thread(()->{
for (int i = 0; i < 10; i++) {
data02.printA();
}
},"A").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
data02.printB();
}
},"B").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
data02.printC();
}
},"C").start();
}
}
class Data02{
private Lock lock = new ReentrantLock();
//监视器
private Condition condition1 = lock.newCondition();
private Condition condition2 = lock.newCondition();
private Condition condition3 = lock.newCondition();
private int number = 1;
public void printA(){
lock.lock();
try {
//业务代码
while (number!=1){
condition1.await();
}
number=2;
System.out.println(Thread.currentThread().getName()+"=>AAAAAAAA");
//唤醒进程,唤醒指定的进程
condition2.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void printB(){
lock.lock();
try {
//业务代码
while (number!=2){
condition2.await();
}
number=3;
System.out.println(Thread.currentThread().getName()+"=>BBBBBBBB");
condition3.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void printC(){
lock.lock();
try {
//业务代码
while (number!=3){
condition3.await();
}
number=1;
System.out.println(Thread.currentThread().getName()+"=>CCCCCCCC");
condition1.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
之前锁在对象,之后锁在lock
其实就是关于锁的八个问题;通过8锁现象,来深入理解锁
1、标准情况下,是先打印“发短信”还是“打电话”–(发短息 打电话)
- 普通同步方法(被synchronized关键字)“锁”的是方法调用者–phone对象(变量名)
- 同一个资源类中所有方法共用一把“锁”
- 所有线程被顺序创建,也就顺序打印
2、sendMessage延迟4秒,是先打印“发短信”还是“打电话”–(发短息 打电话)
- 给第一个sendMessage方法延迟四秒
- 那么会出现一种状况:一秒后,在A线程还在等待“休眠”结束时,B线程开启,但是sleep方法仅仅是让正在执行的线程休眠而不是是否“锁”
package com.my.lock8;
import java.util.concurrent.TimeUnit;
/*
* 8锁现象:其实就是8个关于锁的问题
* 1、标准情况下,是先打印“发短信”还是“打电话”--(发短息 打电话)
* 2、sendMessage延迟4秒,是先打印“发短信”还是“打电话”--(发短息 打电话)
* 上述出现两种情况的原因:synchronized;
* */
public class Test01 {
public static void main(String[] args) {
Phone phone = new Phone();
new Thread(()->{
phone.sendMessage();
},"A").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{
phone.call();
},"B").start();
}
}
class Phone{
//synchronized:锁的对象是方法调用者--phone对象
//两个方法共用同一把锁,谁先拿到谁执行
public synchronized void sendMessage(){
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("发短信");
}
public synchronized void call(){
System.out.println("打电话");
}
}
3、较之前资源类中新增一个普通方法hello,让线程B去执行,那么输出结果是先打印“Hello”还是“发短信”–(Hello 发短信)
- hello是一个普通方法,给这个方法没有加锁
- 线程A要执行的方法中需要休眠4秒
- 但是hello方法没有加锁,更不可能和其他方法共用一把锁
- 因此,在休眠1秒后,B线程直接执行,之后等休眠4秒后A线程才执行
4、新建俩个资源类对象,第一个对象调用发短信方法,第二个调用打电话方法,那么输出结果是先打印“发短信”还是“打电话”–(打电话 发短信)
- 普通同步方法(被synchronized)它锁的对象是“方法调用者”–phone对象(变量名)
- 所有一个“方法调用者”就是一把“锁”
- 这里相当于有两把“锁”
- 那么两个AB线程可以随机执行,但是因为A线程的方法要休眠4秒,所有B线程先执行完毕
package com.my.lock8;
import java.util.concurrent.TimeUnit;
/*
* 8锁现象:其实就是8个关于锁的问题
* 3、较之前资源类中新增一个普通方法hello,让线程B去执行,那么输出结果是先打印“Hello”还是“发短信”--(Hello 发短信)
* 4、新建俩个资源类对象,第一个对象调用发短信方法,第二个调用打电话方法,那么输出结果是先打印“发短信”还是“打电话”--(打电话 发短信)
* */
public class Test02 {
public static void main(String[] args) {
//实例化两个对象
synchronized:锁的对象是方法调用者--phone对象,因此两个对象就是两把锁!!
Phone02 phone1 = new Phone02();
Phone02 phone2 = new Phone02();
new Thread(()->{
phone1.sendMessage();
},"A").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{
phone2.call();
},"B").start();
}
}
class Phone02{
//synchronized:锁的对象是方法调用者--phone对象
//两个方法共用同一把锁,谁先拿到谁执行
public synchronized void sendMessage(){
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("发短信");
}
public synchronized void call(){
System.out.println("打电话");
}
//普通方法:不是同步方法,不受锁的限制
public void hello(){
System.out.println("Hello");
}
}
5、将资源类中的两个方法修改为静态方法,创建一个资源类对象,调用两个方法,判断先打印“发短信”还是“打电话”–(发短信 打电话)
- 静态同步方法(被static修饰的普通同步方法)
- 资源类中的所有静态同步方法“锁”的是该资源类(Phone)的类模板(Class模板)
- 因此,还是按照创建线程的的顺序执行(A线程执行完后,释放锁;B线程拿到锁执行)
6、创建两个资源类对象,两个静态同步方法,调用两个方法,判断先打印“发短信”还是“打电话”–(发短信 打电话)
- 不要被误导:因为静态同步方法锁的是Class类模板,而不是普通同步方法的方法调用者phone对象(变量名)
- 所有还是按照线程的创建顺序执行
package com.my.lock8;
import java.util.concurrent.TimeUnit;
/*
* 8锁现象:其实就是8个关于锁的问题
* 5、将资源类中的两个方法修改为静态方法,创建一个资源类对象,调用两个方法,判断先打印“发短信”还是“打电话”--(发短信 打电话)
* 6、创建两个资源类对象,两个静态同步方法,调用两个方法,判断先打印“发短信”还是“打电话”--(打电话 发短信)
* */
public class Test03 {
public static void main(String[] args) {
//两个资源类对象共用一个Class模板,而且synchronized锁的是Class模板
Phone03 phone1 = new Phone03();
Phone03 phone2 = new Phone03();
new Thread(()->{
phone1.sendMessage();
},"A").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{
phone2.call();
},"B").start();
}
}
class Phone03{
//synchronized:锁的对象是Class模板
//static 静态方法
//类一旦被加载就有了,Class模板,一个类有且仅有一个唯一的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("打电话");
}
}
7、将两个方法一个设置为静态方法,一个设置为普通同步方法,创建一个资源类对象,执行两个方法,先打印“发短信”还是“打电话”(打电话 发短信)
- 静态同步方法锁的是Class类模板,普通同步方法锁的是方法调用者;因此在这里也相当于两把“锁”,线程之间互不干扰
- 线程A的休眠时间长,因此先执行了B线程
8、创建两个资源类对象,调用下面的两个方法,先打印“发短信”还是“打电话”(打电话 发短信)
- 两把“锁”
- A线程与B线程执行之间互不打扰
package com.my.lock8;
import java.util.concurrent.TimeUnit;
/*
* 8锁现象:其实就是8个关于锁的问题
* 7、将两个方法一个设置为静态方法,一个设置为普通同步方法,创建一个资源类对象,执行两个方法,先打印“发短信”还是“打电话”(打电话 发短信)
* 8、创建两个资源类对象,调用下面的两个方法,先打印“发短信”还是“打电话”(打电话 发短信)
* */
public class Test04 {
public static void main(String[] args) {
Phone04 phone1 = new Phone04();
Phone04 phone2 = new Phone04();
new Thread(()->{
phone1.sendMessage();
},"A").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{
phone2.call();
},"B").start();
}
}
class Phone04{
//静态的同步方法:锁的是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("打电话");
}
}
package com.my.list;
import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
//集合是不安全的
//java.util.ConcurrentModificationException:并发修改异常
public class ListTest {
public static void main(String[] args) {
/*
* 并发线程下,ArrayList集合不安全,怎么解决?
* 1、使用List接口的古老实现类:Vector--List list = new Vector<>();
* 2、使用Collections工具类:List
//就是当多个线程写入数据时,会造成写入的数据“覆盖问题”
// List list = new CopyOnWriteArrayList<>();
List<Object> list = new ArrayList<>();
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();
}
}
}
package com.my.list;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.CopyOnWriteArraySet;
public class SetTest {
public static void main(String[] args) {
/*
* 和ArrayList同样,线程不安全,解决方法?
* 1、Set set = Collections.synchronizedSet(new HashSet<>());
* 2、Set
// Set set = new HashSet<>();
Set<Object> set = new CopyOnWriteArraySet<>();
for (int i = 0; i < 50; i++) {
new Thread(()->{
set.add(UUID.randomUUID().toString().substring(0,5));
System.out.println(set);
},String.valueOf(i)).start();
}
}
}
package com.my.list;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
public class MapTest {
public static void main(String[] args) {
/*
* 工作中是这么用HashMap?--不是这么用的,工作中不要HashMap
* 它默认等价于什么?--new HashMap<>(16,0.75);
* */
//搞清楚什么是加载因子?什么是初始化容量?
/*
* map线程不安全怎么解决?
* 1、Map
// Map map = new HashMap<>();
Map<Object, Object> map = new ConcurrentHashMap<>();
for (int i = 0; i < 20; i++) {
new Thread(()->{
map.put(Thread.currentThread().getName(), UUID.randomUUID().toString().substring(0,5));
System.out.println(map);
}).start();
}
}
}
Callable:
package com.my.callable;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class CallableTest {
public static void main(String[] args) throws ExecutionException, InterruptedException {
MyThread myThread = new MyThread();
//怎么使用Callable?
/*
* 原来Runnable接口的实现方式:new Thread(new MyThread()).start();
*
* */
FutureTask futureTask = new FutureTask(myThread);//适配类,作为中间“驿站”,为了能够使用Callable接口的实现类
new Thread(futureTask,"A").start();
new Thread(futureTask,"B").start();//结果会被缓存!--call方法只执行一次!
//获取Callable方法中的返回值
Object str = futureTask.get();//这个get方法可能会产生阻塞,一般放在最后!或者用过异步通信来处理
System.out.println(str);
}
}
class MyThread implements Callable<String> {
@Override
public String call() throws Exception {
System.out.println("执行了call方法!");
return "哈哈哈哈";
}
}
常用方法:
package com.my.assist;
import java.util.concurrent.CountDownLatch;
//计算器
public class CountDownLatchTest {
public static void main(String[] args) throws InterruptedException {
CountDownLatch countDownLatch = new CountDownLatch(6);
for (int i = 0; i <= 6; i++) {
new Thread(()->{
System.out.println(Thread.currentThread().getName()+"go out!");
countDownLatch.countDown();//就是“-1”操作
},String.valueOf(i)).start();
}
countDownLatch.await();//阻塞,判断countDownLatch是否归零,归零后再往下执行
System.out.println("Closed door!");
}
}
package com.my.assist;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
//加法计数器
public class CyclicBarrierTest {
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) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}).start();
}
}
}
常用方法:
作用:多个共享资源互斥使用!并发限流,控制最大的线程数目!
package com.my.assist;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
//信号量
public class SemaphoreTest {
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();
}
}).start();
}
}
}
ReentrantReadWriteLock它是ReadWriteLock接口的实现类:
package com.my.rw;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/*
* ReentrantReadWriteLock它是ReadWriteLock接口的实现类
* 读-读:可以共享
* 读-写:不可以共享
* 写-写:不可以共享
* */
public class ReadWriteLockTest {
public static void main(String[] args) {
MyCacheLock myCache = new MyCacheLock();
//写线程
for (int i = 1; i <= 5; i++) {
final int temp = i;
new Thread(()->{
myCache.put(temp+"",temp+"");
},String.valueOf(i)).start();
}
for (int i = 1; i <= 5; i++) {
final int temp = i;
new Thread(()->{
myCache.get(temp+"");
},String.valueOf(i)).start();
}
}
}
class MyCacheLock{
//volatile:关键字
public volatile Map<String,Object> map = new HashMap<String,Object>();
/*
* 为了提高效率,只允许有一个线程来写,但允许多个线程来读
* */
//读写锁:(并不是普通的Lock锁),是一种更加“细粒度”的锁
private ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();
//存--》写的过程
public void put(String key,Object value){
reentrantReadWriteLock.writeLock().lock();
try {
System.out.println(Thread.currentThread().getName()+"写入"+key);
map.put(key,value);
System.out.println(Thread.currentThread().getName()+"写入成功");
} catch (Exception e) {
e.printStackTrace();
} finally {
reentrantReadWriteLock.writeLock().unlock();
}
}
//去--》读的过程
public void get(String key){
reentrantReadWriteLock.readLock().lock();
try {
System.out.println(Thread.currentThread().getName()+"读入"+key);
map.get(key);
System.out.println(Thread.currentThread().getName()+"读入成功");
} catch (Exception e) {
e.printStackTrace();
} finally {
reentrantReadWriteLock.readLock().unlock();
}
}
}
//自定义缓存
class MyCache{
//volatile:关键字
public volatile Map<String,Object> map = new HashMap<String,Object>();
/*
* 为了提高效率,只允许有一个线程来写,但允许多个线程来读
* */
//存--》写的过程
public void put(String key,Object value){
System.out.println(Thread.currentThread().getName()+"写入"+key);
map.put(key,value);
System.out.println(Thread.currentThread().getName()+"写入成功");
}
//去--》读的过程
public void get(String key){
System.out.println(Thread.currentThread().getName()+"读入"+key);
map.get(key);
System.out.println(Thread.currentThread().getName()+"读入成功");
}
}
什么情况下使用队列:
常用方法:
队列中常用的四组API:
方式 | 抛出异常 | 不会抛出异常,有返回值 | 阻塞等待 | 超时等待 |
---|---|---|---|---|
添加 | add | offer | put | offer(…) |
移除 | remove | poll | take | poll(…) |
获取队列首 | element | peak |
package com.my.bq;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.TimeUnit;
public class BlockingQueueTest {
public static void main(String[] args) throws InterruptedException {
test05();
}
/*
* 抛出异常
* */
public static void test01(){
//需要有个参数:表示该队列可以存放的元素的个数
ArrayBlockingQueue arrayBlockingQueue = new ArrayBlockingQueue<>(3);
//add方法:返回值为布尔类型
/*
* 如果添加的元素数量超过规定的数量,则程序会报异常:Queue full
* */
System.out.println(arrayBlockingQueue.add("a"));
System.out.println(arrayBlockingQueue.add("b"));
System.out.println(arrayBlockingQueue.add("c"));
//System.out.println(arrayBlockingQueue.add("d"));
//remove方法:从队列中“弹出”元素,按照先进先出的顺序
/*
* 如果弹出的次数大于已存在队列元素的数量,也会报异常:NoSuchElementException
* */
System.out.println(arrayBlockingQueue.remove());
System.out.println(arrayBlockingQueue.remove());
System.out.println(arrayBlockingQueue.remove());
//System.out.println(arrayBlockingQueue.remove());
}
/*
* 不抛出异常
* */
public static void test02(){
ArrayBlockingQueue<Object> arrayBlockingQueue = new ArrayBlockingQueue<>(3);
//offer方法:不抛出异常,当队列满时,后面要添加的语句直接返回--false
System.out.println(arrayBlockingQueue.offer("a"));
System.out.println(arrayBlockingQueue.offer("b"));
System.out.println(arrayBlockingQueue.offer("c"));
//System.out.println(arrayBlockingQueue.offer("d"));
//poll方法:弹出队列元素时,如果队列为空,后面的执行语句返回值--null
System.out.println(arrayBlockingQueue.poll());
System.out.println(arrayBlockingQueue.poll());
System.out.println(arrayBlockingQueue.poll());
//System.out.println(arrayBlockingQueue.poll());
}
/*
* 返回队列首元素
* */
public static void test03(){
ArrayBlockingQueue<Object> arrayBlockingQueue = new ArrayBlockingQueue<>(3);
System.out.println(arrayBlockingQueue.offer("a"));
System.out.println(arrayBlockingQueue.offer("b"));
System.out.println(arrayBlockingQueue.offer("c"));
System.out.println(arrayBlockingQueue.element());
System.out.println(arrayBlockingQueue.peek());
}
/*
* 阻塞等待--(一直等待)
* */
public static void test04() throws InterruptedException {
ArrayBlockingQueue<Object> arrayBlockingQueue = new ArrayBlockingQueue<>(3);
arrayBlockingQueue.put("a");
arrayBlockingQueue.put("b");
arrayBlockingQueue.put("c");
//arrayBlockingQueue.put("d");
System.out.println(arrayBlockingQueue.take());
System.out.println(arrayBlockingQueue.take());
System.out.println(arrayBlockingQueue.take());
}
/*
* 阻塞等待(超时等待)
* */
public static void test05() throws InterruptedException {
ArrayBlockingQueue<Object> arrayBlockingQueue = new ArrayBlockingQueue<>(3);
arrayBlockingQueue.offer("a");
arrayBlockingQueue.offer("b");
arrayBlockingQueue.offer("c");
//arrayBlockingQueue.offer("d",2, TimeUnit.SECONDS);
System.out.println(arrayBlockingQueue.poll());
System.out.println(arrayBlockingQueue.poll());
System.out.println(arrayBlockingQueue.poll());
arrayBlockingQueue.poll(2,TimeUnit.SECONDS);
}
}
本质:
原理:
package com.my.bq;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.TimeUnit;
/*
* SynchronousQueue 是 BlockingQueue 接口的实现类
* “同步队列”,没有容量,进去一个元素,必须要取出来以后才能往里面放
* */
public class SynchronizedQueueTest {
public static void main(String[] args) {
SynchronousQueue<String> synchronousQueue = new SynchronousQueue<>();
new Thread(()->{
try {
TimeUnit.SECONDS.sleep(2);
synchronousQueue.put("a");
System.out.println(Thread.currentThread().getName()+"线程,put了一个值:a");
TimeUnit.SECONDS.sleep(2);
synchronousQueue.put("b");
System.out.println(Thread.currentThread().getName()+"线程,put了一个值:b");
TimeUnit.SECONDS.sleep(2);
synchronousQueue.put("c");
System.out.println(Thread.currentThread().getName()+"线程,put了一个值:c");
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
new Thread(()->{
try {
synchronousQueue.take();
System.out.println(Thread.currentThread().getName()+"线程,take了一个值:a");
synchronousQueue.take();
System.out.println(Thread.currentThread().getName()+"线程,take了一个值:b");
synchronousQueue.take();
System.out.println(Thread.currentThread().getName()+"线程,take了一个值:c");
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
}
线程池:三大方法、七大参数、四种拒绝策略
package com.my.pool;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/*
* Executors:类似于“Collections”是一种工具类,用它可以来创建线程池
* 用它可以来创建线程池的“三大方法”
* Executors.newSingleThreadExecutor();//单个线程
Executors.newFixedThreadPool(5);//创建固定线程数量的线程池
Executors.newCachedThreadPool();//随机创建一定数量线程的线程池
* */
public class ExecutorsTest {
public static void main(String[] args) {
//ExecutorService threadPool = Executors.newSingleThreadExecutor();//单个线程
//ExecutorService threadPool = Executors.newFixedThreadPool(5);//创建固定线程数量的线程池
ExecutorService threadPool = Executors.newCachedThreadPool();//随机创建一定数量线程的线程池
/*
* 引入线程池后,执行线程的方式也发送了改变
* 1、执行线程:execute
* 2、线程池用完需要关闭:shutdown
* */
try {
for (int i = 0; i < 100; i++) {
threadPool.execute(()->{
System.out.println(Thread.currentThread().getName()+"==>OK");
});
}
} catch (Exception e) {
e.printStackTrace();
} finally {
threadPool.shutdown();
}
}
}
源码分析:
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
//本质:ThreadPoolExecutor
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.acc = System.getSecurityManager() == null ?
null :
AccessController.getContext();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
package com.my.pool;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/*
* 七大参数
*
* 四种拒绝策略
* AbortPolicy()//拒绝策略:就是当银行满的时候,如果再来顾客,就不处理了,然后抛出异常
* CallerRunsPolicy()//哪来的回哪去
* DiscardPolicy()//队列满了,就丢掉任务,不会抛出异常
*
* */
public class ThreadPoolExecutorTest {
public static void main(String[] args) {
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
2,
5,
3,
TimeUnit.SECONDS,
new LinkedBlockingQueue<>(3),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.DiscardOldestPolicy()//队列满了,刚来的尝试和队首竞争,也不抛出异常
);
try {
//最大承载:max+Queue
//超过最大承载就抛出异常:RejectedExecutionException
for (int i = 0; i < 10; i++) {
threadPoolExecutor.execute(()->{
System.out.println(Thread.currentThread().getName()+"==>OK");
});
}
} catch (Exception e) {
e.printStackTrace();
} finally {
threadPoolExecutor.shutdown();
}
}
}
最大核心线程池数量到底应该定义为多少?(调优)
- CPU密集型:就是根据电脑中CPU中的核数(逻辑处理器数量)
- 获取每个电脑中的CPU核数:System.out.println(Runtime.getRuntime().availableProcessors());
IO密集型:对于大型任务,io非常占资源,判断程序类似任务多少,最好分配的线程数量大于这样的任务数量(多的线程可以取执行其他任务,避免阻塞)
新时代的程序员:Lambda表达式、链式编程、函数式接口、Stream流计算
概念:定义的接口中有且仅含有一个方法
优势:简化编程模型,在新版的框架中大量应用
package com.my.function;
import java.util.function.Function;
//Function:函数型接口(接口中只有一个方法)
//接口实现类-》匿名内部类-》Lambda表达式
public class FunctionTest {
public static void main(String[] args) {
//匿名内部类
Function<String, String> function = new Function<String, String>() {
//该方法的作用:输出输入的值
@Override
public String apply(String str) {
return str;
}
};
//Lambda表达式
Function function1 = (str)->{return str;};
System.out.println(function.apply("麻腾飞"));
System.out.println(function1.apply("Matengfei"));
}
}
package com.my.function;
import java.util.function.Predicate;
/*
* 断定型接口
* */
public class PredicateTest {
public static void main(String[] args) {
//判断字符串是否为空
Predicate<String> predicate = new Predicate<String>() {
@Override
public boolean test(String str) {
return str.isEmpty();
}
};
Predicate<String> predicate1 = (str)->{return str.isEmpty();};
System.out.println(predicate.test(""));
System.out.println(predicate1.test(""));
}
}
package com.my.function;
import java.util.function.Consumer;
/*
* 消费型接口:只有输入
* */
public class ConsumerTest {
public static void main(String[] args) {
//只接收输入的参数
Consumer<String> consumer = new Consumer<String>() {
@Override
public void accept(String str) {
System.out.println(str);
}
};
Consumer<String> consumer1 = (str)->{
System.out.println(str);
};
consumer.accept("Matengfei");
consumer1.accept("Matengfei");
}
}
package com.my.function;
import java.util.function.Supplier;
/*
* 供给型接口:只输出,不输入
* */
public class SupplierTest {
public static void main(String[] args) {
Supplier<String> supplier = new Supplier<String>() {
@Override
public String get() {
System.out.println("get");
return "1024";
}
};
Supplier<String> supplier1 = ()->{return "110";};
System.out.println(supplier.get());
System.out.println(supplier1.get());
}
}
package com.my.stream;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
public class StreamTest {
public static void main(String[] args) {
/*
* 现在有五个用户,按条件进行筛选:
* 1、ID必须是偶数
* 2、年龄必须大于20岁
* 3、用户名转为大写字母
* 4、用户名字母倒着排序
* 5、只输出一个用户
* */
User user1 = new User(1, "a", 18);
User user2 = new User(2, "b", 12);
User user3 = new User(4, "d", 22);
User user4 = new User(5, "f", 30);
User user5 = new User(6, "z", 40);
//存储
List<User> list = Arrays.asList(user1, user2, user3, user4, user5);
//计算
//搞明白:System.out::println
//Lambda表达式、链式编程、函数式接口、Stream流式计算
list.stream().filter((u)->{return u.getId()%2==0;})
.filter((u)->{return u.getAge()>20;})
.map((u)->{return u.getName().toUpperCase();})
.sorted((uu1,uu2)->{return uu2.compareTo(uu1);})
.limit(1)
.forEach(System.out::println);
}
}
工作窃取:提高工作效率!(“双端队列”)
package com.my.forkjoin;
import java.util.concurrent.RecursiveTask;
public class ForkJoinDemo extends RecursiveTask<Long> {
private Long start;
private Long end;
//临界值
private Long temp=10000L;
public ForkJoinDemo(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 {//forkjoin递归
long middle = (start+end)/2;//中间值
ForkJoinDemo task1 = new ForkJoinDemo(start,middle);
task1.fork();//拆分任务,把任务压如线程队列
ForkJoinDemo task2 = new ForkJoinDemo(middle,end);
task2.fork();//拆分任务,把任务压如线程队列
return task1.join()+task2.join();
}
}
}
package com.my.forkjoin;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
import java.util.stream.LongStream;
public class Test {
public static void main(String[] args) throws ExecutionException, InterruptedException {
//test01();//13999
//test02();//10833
test03();//1147
}
public static void test01(){
Long sum = 0L;
Long start = System.currentTimeMillis();
for (Long i=1L;i<=10_0000_0000L;i++){
sum+=i;
}
Long end = System.currentTimeMillis();
System.out.println("sum="+sum+";时间:"+(end-start));
}
//使用ForkJoin,可以通过临界值(temp)来进行调优
public static void test02() throws ExecutionException, InterruptedException {
Long start = System.currentTimeMillis();
ForkJoinPool forkJoinPool = new ForkJoinPool();
ForkJoinDemo task = new ForkJoinDemo(0L, 10_0000_0000L);
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 test03(){
Long start = System.currentTimeMillis();
long sum = LongStream.rangeClosed(0L, 10_0000_0000L).parallel().reduce(0, Long::sum);
Long end = System.currentTimeMillis();
System.out.println("sum="+sum+";时间:"+(end-start));
}
}
Future设计的初衷:对未来的某个事件的结果进行建模
package com.my.future;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
/*
* 异步回调:ajax(前后端交互)、Future(后端)
* 异步执行
* 成功回调
* 失败回调
* */
public class FutureTest {
public static void main(String[] args) throws ExecutionException, InterruptedException {
//发起一个请求(没有返回值的异步回调)
/*CompletableFuture completableFuture = CompletableFuture.runAsync(()->{//执行一个异步任务
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"==>run");
});
System.out.println("主线程...");
completableFuture.get();//获取阻塞结果*/
//有返回值的异步回调
/*
* 它和ajax一样,如果有返回值的情况:
* 成功:返回结果
* 失败:打印错误信息
* */
CompletableFuture<Integer> completableFuture = CompletableFuture.supplyAsync(()->{
System.out.println(Thread.currentThread().getName()+"run");
int i=10/0;
return 1024;
});
System.out.println(
completableFuture.whenComplete((t,u)->{
System.out.println("t=>"+t);//返回的是正确的结果
System.out.println("u=>"+u);//返回的是错误的详细信息:u=>java.util.concurrent.CompletionException: java.lang.ArithmeticException: / by zero
}).exceptionally((e)->{
e.getMessage();
return 233;
}).get());
}
}
Volatile是Java虚拟机提供的轻量级同步机制
JMM:是一种理论概念(内存模型),实际中不存在,就是一个概念,一个约定
存在问题:
内存交互操作(规则):
问题引入:
package com.my.volatileTest;
/*
* 模拟8种操作存在的问题
* */
public class VolatileTest {
private static int num = 0;
public static void main(String[] args) {//main线程
new Thread(()->{//线程A
while (num==0){
}
}).start();
num = 1;
System.out.println("num=>"+num);
}
}
package com.my.volatileTest;
/*
* 模拟8种操作存在的问题
* */
public class VolatileTest {
//加上volatile关键字:保证可见性
private volatile static int num = 0;
public static void main(String[] args) {//main线程
new Thread(()->{
while (num==0){
}
}).start();
num = 1;
System.out.println("num=>"+num);
}
}
原子性:不可分割
线程在执行时,不能被干扰,不可分割;要么成功,要么失败
package com.my.volatileTest;
//证明:Volatile关键字不保证原子性
public class VolatileTest02 {
private volatile static int num = 0;
public static void add(){
num++;
}
public static void main(String[] args) {
for (int i = 0; i < 20; i++) {
new Thread(()->{
for (int i1 = 0; i1 < 1000; i1++) {
add();
}
}).start();
}
while (Thread.activeCount()>2){//main线程、gc线程
Thread.yield();
}
System.out.println(Thread.currentThread().getName()+"num=>"+num);
}
}
问:如果不使用lock和synchronized,怎么保证原子性?
使用原子类
package com.my.volatileTest;
import java.util.concurrent.atomic.AtomicInteger;
//证明:Volatile关键字不保证原子性
public class VolatileTest02 {
//private volatile static int num = 0;
//原子类的Integer
private static AtomicInteger num = new AtomicInteger();
public static void add(){
//num++;
num.getAndIncrement();//AtomicInteger中的+1方法,要想更详细了解:CAS
}
public static void main(String[] args) {
for (int i = 0; i < 20; i++) {
new Thread(()->{
for (int i1 = 0; i1 < 1000; i1++) {
add();
}
}).start();
}
while (Thread.activeCount()>2){//main线程、gc线程
Thread.yield();
}
System.out.println(Thread.currentThread().getName()+"num=>"+num);
}
}
注:这些原子类的底层和操作系统有关!在内存中修改值!Unsafe类是一个很特殊的存在
什么是指令重排?
int x=1;//a
int y=2;//b
x=x+y;//c
y=x*x;//d
我们期望的执行顺序:abcd;实际上可能是bacd
那可不可能是:cdab
处理器在进行指令重排的时候:考虑数据之间的依赖性;但是如果前后程序语句没有依赖关系,可能会存在影响结果的指令重排
volatile关键字可以避免指令重排:(内存屏障,cpu指令)
单例模式分类:
存在问题:
饿汉式在类加载时已经创建好该对象,在程序调用时直接返回该单例对象即可,即我们在编码时就已经指明了要马上创建这个对象,不需要等到被调用时再去创建。
package com.my.single;
//饿汉式单例模式:“饿”创建对象更早!
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class Hungry {
//类加载时,对象就已经被创建
private static final Hungry hungry = new Hungry();
public Hungry() {
synchronized (Hungry.class){
if (hungry!=null){
throw new RuntimeException("试图通过反射破坏单例模式!");
}
}
}
public static Hungry getInstance(){
return hungry;
}
//通过反射来破坏单例模式
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
//Hungry hungry = new Hungry();
Constructor<Hungry> constructor = Hungry.class.getDeclaredConstructor(null);
//通过反射获取这个类的构造器,然后通过构造器来创建对象
constructor.setAccessible(true);
Hungry hungry1 = constructor.newInstance();
Hungry hungry2 = constructor.newInstance();
System.out.println(hungry2==hungry1);//输出结果为false
}
/*
* 总结:
* 1、第一个对象通过构造器正常创建,第二个对象通过反射机制来创建(不加任何修改,第二个对象会创建成功)--解决:为了防止单例模式被破坏,需要对类的私有构造器加锁!
* 2、加入我创建的第一个对象是通过反射机制创建的,那么第二个通过反射机制创建的对象也会破坏单例模式(执行成功)--解决:通过“信号灯”法,设置一个标志位
* */
}
懒汉式创建对象的方法是在程序使用对象前,先判断该对象是否已经实例化(判空),若已实例化直接返回该类对象。,否则则先执行实例化操作。
package com.my.single;
/*
* 懒汉式单例模式
* “双重检测”懒汉式单例模式(Double Check(双重校验) + Lock(加锁))
* */
public class Lazy {
private static volatile Lazy lazy;
public Lazy() {
System.out.println(Thread.currentThread().getName()+"线程创建了Lazy对象!");
}
public static Lazy getInstance(){
//双重检测模式 DCL--但是还是会存在问题:原因lazy = new Lazy();不是原子性操作(存在指令重排问题!!!)
//为了解决这个问题:需要使用volatile关键字,来禁止指令重排
if (lazy==null){
synchronized (Lazy.class){
if (lazy==null){
lazy = new Lazy();
/*
* 分配内存空间
* 执行构造方法,初始化类对象
* 将创建的类对象的地址指向分配的内存空间
* */
}
}
}
return lazy;
}
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
new Thread(()->{
getInstance();
}).start();
}
}
}
枚举类是JDK1.5后,自带单例模式的类(该类定义单例模式,可以防止被反射机制破坏)
package com.my.single;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
//枚举类默认是单例模式
public enum EnumSingle {
INSTANCE;
public EnumSingle getInstance(){
return INSTANCE;
}
}
class Test{
public static void main(String[] args) throws NoSuchMethodException,
InvocationTargetException, InstantiationException, IllegalAccessException {
EnumSingle instance1 = EnumSingle.INSTANCE;
//EnumSingle instance2 = EnumSingle.INSTANCE;
/*
* 通过反射机制来验证:枚举类可以防止反射破坏单例模式
* 1、但是结果并不是按照我们想象的那样,结果报了这个异常:
* Exception in thread "main" java.lang.NoSuchMethodException: com.my.single.EnumSingle.()
*
* 2、原因就是枚举类创建对象是通过“有参构造器”来创建的
* 按照正常情况结果会报如下异常:Cannot reflectively create enum objects
* */
//Constructor constructor = EnumSingle.class.getDeclaredConstructor(String.class,int.class);
Constructor<EnumSingle> constructor = EnumSingle.class.getDeclaredConstructor(null);
constructor.setAccessible(true);
//EnumSingle enumSingle1 = constructor.newInstance();
EnumSingle instance2 = constructor.newInstance();
System.out.println(instance1==instance2);
}
}
什么是CAS?
缺点:
package com.my.cas;
import java.util.concurrent.atomic.AtomicInteger;
public class CASDemo {
public static void main(String[] args) {
AtomicInteger atomicInteger = new AtomicInteger(2022);
// public final boolean compareAndSet(int expect, int update)
//期望,更新
atomicInteger.compareAndSet(2022,2023);
System.out.println(atomicInteger.get());
System.out.println(atomicInteger.compareAndSet(2022, 2000));
System.out.println(atomicInteger.get());
}
}
Unsafe类
package com.my.cas;
import java.util.concurrent.atomic.AtomicInteger;
public class CASDemo {
public static void main(String[] args) {
AtomicInteger atomicInteger = new AtomicInteger(2022);
// public final boolean compareAndSet(int expect, int update)
//期望,更新
//捣乱的线程B
atomicInteger.compareAndSet(2022,2023);
System.out.println(atomicInteger.get());
atomicInteger.compareAndSet(2023,2022);
System.out.println(atomicInteger.get());
//期望的线程A
System.out.println(atomicInteger.compareAndSet(2022, 2000));
System.out.println(atomicInteger.get());
}
}
模拟ABA场景:
package com.my.cas;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicStampedReference;
public class ABATest {
public static void main(String[] args) {
//Integer:反是这种包装类(引用数据类型)看看有没有超出返回,如果超出范围,会new一个新的对象
AtomicStampedReference<Integer> stampedReference = new AtomicStampedReference<>(1,1);
new Thread(()->{
int stamp = stampedReference.getStamp();
System.out.println("a1=>"+stamp);
//a、b线程在这里一起休眠两秒
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
stampedReference.compareAndSet(1, 8, stampedReference.getStamp(), stampedReference.getStamp()+1);
System.out.println("a2=>"+stampedReference.getStamp());
stampedReference.compareAndSet(8, 1, stampedReference.getStamp(), stampedReference.getStamp()+1);
System.out.println("a3=>"+stampedReference.getStamp());
},"a").start();
new Thread(()->{
int stamp = stampedReference.getStamp();
System.out.println("b1=>"+stamp);
//a、b线程在这里一起休眠两秒
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
stampedReference.compareAndSet(1, 6, stampedReference.getStamp(), stampedReference.getStamp()+1);
System.out.println("b2=>"+stampedReference.getStamp());
},"b").start();
}
}
可重入锁(synchronized版):
package com.my.lock;
import java.util.concurrent.TimeUnit;
//可重入锁:synchronized版
public class Test01 {
public static void main(String[] args) {
Phone phone = new Phone();
new Thread(()->{
try {
phone.send();
} catch (InterruptedException e) {
e.printStackTrace();
}
},"A").start();
new Thread(()->{
try {
phone.send();
} catch (InterruptedException e) {
e.printStackTrace();
}
},"B").start();
}
}
class Phone{
public synchronized void send() throws InterruptedException {
System.out.println(Thread.currentThread().getName()+"=>发短信");
call();
}
public synchronized void call() throws InterruptedException {
TimeUnit.SECONDS.sleep(3);
System.out.println(Thread.currentThread().getName()+"=>打电话");
}
}
可重入锁(Lock版):
package com.my.lock;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
//可重入锁:Lock版
public class Test02 {
public static void main(String[] args) {
Phone02 phone = new Phone02();
new Thread(()->{
phone.send();
},"A").start();
new Thread(()->{
phone.send();
},"B").start();
}
}
class Phone02{
Lock lock = new ReentrantLock();
public void send() {
lock.lock();
try {
System.out.println(Thread.currentThread().getName()+"=>发短信");
call();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void call() {
lock.lock();
try {
System.out.println(Thread.currentThread().getName()+"=>打电话");
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
自旋锁核心:循环+CAS
自定义创建自旋锁:
package com.my.lock;
import java.util.concurrent.atomic.AtomicReference;
//定义自旋锁:循环+CAS
public class Test03 {
AtomicReference atomicReference = new AtomicReference();
//加锁
public void myLock(){
Thread thread = Thread.currentThread();
System.out.println(Thread.currentThread().getName()+"线程:进入了我的自旋锁!");
//就是如果当前线程不是“null”,就会一直循环
while (!atomicReference.compareAndSet(null,thread)){}
}
//解锁
public void myUnLock(){
Thread thread = Thread.currentThread();
System.out.println(Thread.currentThread().getName()+"线程:拿到了锁,开始释放锁!");
atomicReference.compareAndSet(thread,null);
}
}
调用自定义的自旋锁:
package com.my.lock;
import java.util.concurrent.TimeUnit;
//调用自己创建的自旋锁
public class Test04 {
public static void main(String[] args) {
Test03 lock = new Test03();
new Thread(()->{
lock.myLock();
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
lock.myUnLock();
},"A").start();
new Thread(()->{
System.out.println(Thread.currentThread().getName()+"线程:正在尝试获取锁!");
lock.myLock();
lock.myUnLock();
},"B").start();
}
}
Demo:
package com.my.lock;
import java.util.concurrent.TimeUnit;
//搭建“死锁”现场
public class Test05 {
public static void main(String[] args) {
String LockA = "LockA";
String LockB = "LockB";
new Thread(new MyThread(LockA,LockB),"A线程").start();
new Thread(new MyThread(LockB,LockA),"B线程").start();
}
}
class MyThread implements Runnable{
private String LockA;
private String LockB;
public MyThread(String lockA, String lockB) {
LockA = lockA;
LockB = lockB;
}
@Override
public void run() {
synchronized (LockA){
System.out.println(Thread.currentThread().getName()+":拿着LockA,试图想要LockB!");
//休眠一会,保证线程B,那到B锁
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (LockB){
System.out.println(Thread.currentThread().getName()+":拿着LockB,试图想要LockA!");
}
}
}
}
步骤:
面试或者工作中,解决问题: