1、线程创建方式![](https://i-blog.csdnimg.cn/blog_migrate/4f9c937b921f8d786a3a69e207e3cf96.png)
1.1、Thread
1.2、Runnable
package com.aliyun.server;
/**
* 多个线程操作同一个对象
*/
public class TestThread implements Runnable{
private int ticketNum = 10;
@Override
public void run() {
while (true){
if (ticketNum <= 1){
break;
}
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"-->拿到了第" + ticketNum-- + "张票");
}
}
public static void main(String[] args) {
TestThread ticket = new TestThread();
new Thread(ticket,"小明").start();
new Thread(ticket,"👩").start();
new Thread(ticket,"黄牛").start();
}
}
1.2.1龟兔赛跑
package com.aliyun.server;
/**
* 龟兔赛跑
*/
public class TestThread implements Runnable{
private static String winner;
@Override
public void run() {
for (int i = 1; i < 101; i++) {
//模拟兔子睡觉
if (Thread.currentThread().getName().equals("🐰")){
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//判断比赛是否结束
boolean flag = gameOver(i);
if (flag){
break;
}
System.out.println(Thread.currentThread().getName()+"-->跑了第" + i +"步");
}
}
//判断是否完成比赛
private boolean gameOver(int i) {
if (i == 100){
winner = Thread.currentThread().getName();
System.out.println("胜利者是"+winner);
return true;
}
return false;
}
public static void main(String[] args) {
TestThread ticket = new TestThread();
new Thread(ticket,"🐢").start();
new Thread(ticket,"🐰").start();
}
}
1.3.实现Callable接口
好处:
可以定义返回值
可以抛出异常
2、静态代理![](https://i-blog.csdnimg.cn/blog_migrate/92d170cf95979568af7f126a3c446de6.png)
package com.aliyun.server;
/**
* 静态代理
*/
public class StaticProxy {
public static void main(String[] args) {
WeddingCompany weddingCompany = new WeddingCompany(new You());
weddingCompany.HappyMarry();
}
}
interface Marry{
void HappyMarry();
}
//真实角色,新郎结婚
class You implements Marry{
@Override
public void HappyMarry() {
System.out.println("要结婚了");
}
}
//代理角色,婚庆公司帮你结婚
class WeddingCompany implements Marry{
//代理谁?真实目标角色
private Marry target;
public WeddingCompany(Marry target){
this.target = target;
}
@Override
public void HappyMarry() {
//结婚前
before();
this.target.HappyMarry();//这就是真实对象
//结婚后
after();
}
private void before(){
System.out.println("结婚前布置婚礼");
}
private void after(){
System.out.println("结婚后收钱");
}
}
2.1静态代理总结:
真实对象和代理对象都要实现同一个接口
代理对象要代理真实的角色(代码中的target)
2.2好处:
代理对象可以做很多真实对象做不了的事情
真实对象专注做自己的事情
2.3多线程中静态代理的应用
new Thread(new Runnable() {
@Override
public void run() {
}
});
Thread为代理对象,Runnable为真实对象。公共实现了run()方法
3、Lamda表达式![](https://i-blog.csdnimg.cn/blog_migrate/8ca25d8961f71270584174df00d28dc2.png)
推导:
原来的方式:
/**
* 推导Lambda
*/
public class StaticProxy {
public static void main(String[] args) {
ILike like = new Like();
like.lambda();
}
}
interface ILike{
void lambda();
}
class Like implements ILike{
@Override
public void lambda() {
System.out.println("i like lambda");
}
}
静态内部内:
/**
* 推导Lambda
*/
public class StaticProxy {
static class Like implements ILike{
@Override
public void lambda() {
System.out.println("i like lambda");
}
}
public static void main(String[] args) {
ILike like = new Like();
like.lambda();
}
}
interface ILike{
void lambda();
}
局部内部类:
/**
* 推导Lambda
*/
public class StaticProxy {
public static void main(String[] args) {
class Like implements ILike{
@Override
public void lambda() {
System.out.println("i like lambda");
}
}
ILike like = new Like();
like.lambda();
}
}
interface ILike{
void lambda();
}
匿名内部类:
/**
* 推导Lambda
*/
public class StaticProxy {
public static void main(String[] args) {
new ILike(){
@Override
public void lambda() {
System.out.println("i like lambda");
}
}.lambda();
}
}
interface ILike{
void lambda();
}
lambda:
public class StaticProxy {
public static void main(String[] args) {
ILike like = ()-> System.out.println("i like");
like.lambda();
}
}
interface ILike{
void lambda();
}
4、线程的五大状态![](https://i-blog.csdnimg.cn/blog_migrate/6e16f5d5c5968342b2da1cd4f2c390f6.png)
5、停止线程
/**
* 停止线程
* 1。建议线程正常停止,利用次数不建议死循环
* 2。建议使用标识位,设置一个标识位
* 3。不要使用stop或者destroy等果实或者jdk不建议使用的方法
*/
public class Stop implements Runnable {
//设置标识位
private boolean flag = true;
@Override
public void run() {
int i = 0;
while (flag){
System.out.println("run Thread" + i);
}
}
//设置一个公开的方法停止线程,转换标志位
public void stop(){
this.flag = false;
}
public static void main(String[] args) {
Stop testStop = new Stop();
new Thread(testStop).start();
for (int i = 0; i < 1000; i++) {
if (i == 900){
System.out.println(i);
testStop.stop();
System.out.println("线程停止");
}
}
}
}
6、 线程休眠![](https://i-blog.csdnimg.cn/blog_migrate/8393d4fa98b9a1026fea72529d55c7bb.png)
7、线程礼让![](https://i-blog.csdnimg.cn/blog_migrate/e330396a3233d31b0e6db9157cd009a7.png)
/**
* 线程礼让
*礼让不一定会成功,看cpu心情
*/
public class Stop{
public static void main(String[] args) {
MyYield myYield = new MyYield();
new Thread(myYield,"a").start();
new Thread(myYield,"b").start();
}
}
class MyYield implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+":开始执行");
Thread.yield();
System.out.println(Thread.currentThread().getName()+":开始执行");
}
}
8、Join(某条线程强制执行)
/**
* Join
*
*/
public class Stop implements Runnable{
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("线程VIP来了-->"+i);
}
}
public static void main(String[] args) throws InterruptedException {
Stop stop = new Stop();
Thread thread = new Thread(stop);
thread.start();
//主线程
for (int i = 0; i < 100; i++) {
if (i==10){
//插队
thread.join();
}
System.out.println("mian线程-->" + i);
}
}
}
9、线程状态观测 ![](https://i-blog.csdnimg.cn/blog_migrate/1db502d833d266c65f6b396db8a79690.png)
10、线程优先级![](https://i-blog.csdnimg.cn/blog_migrate/2946c16edd9fa09c0efb93f926df1857.png)
/**
* 测试线程优先级
*/
public class Stop{
public static void main(String[] args) {
System.out.println(Thread.currentThread().getName()+"->"+Thread.currentThread().getPriority());
MyPriority myPriority = new MyPriority();
Thread t1 = new Thread(myPriority);
Thread t2 = new Thread(myPriority);
Thread t3 = new Thread(myPriority);
Thread t4 = new Thread(myPriority);
//先设置优先级再启动
t1.start();//默认是5
t2.setPriority(1);
t2.start();
t3.setPriority(Thread.MAX_PRIORITY);//最大值10
t3.start();
t4.setPriority(6);
t4.start();
}
}
class MyPriority implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"->"+Thread.currentThread().getPriority());
}
}
优先级不是绝对的,只是概率会大一些
11、守护(daemon)线程![](https://i-blog.csdnimg.cn/blog_migrate/75c521a257a600b6e4676a3313a4f3b6.png)
/**
* 测试线程优先级
*/
public class Stop{
public static void main(String[] args) {
God god = new God();
You you = new You();
Thread godT = new Thread(god);
godT.setDaemon(true);//默认是false(不是守护线程)
godT.start();
new Thread(you).start();
}
}
//上帝:守护线程
class God implements Runnable{
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println("上帝啊");
}
}
}
//你:用户线程
class You implements Runnable{
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("你的一生");
}
System.out.println("===goodbye word===");
}
}
12、并发
![](https://i-blog.csdnimg.cn/blog_migrate/c8530b2ae09a7b99bf71e7e21b83857b.png)
12.1、线程同步
13、线程同步安全和不安全案例
13.1.不安全的买票
/**
* 线程安全和不安全测试
*/
public class Stop{
public static void main(String[] args) {
BuyTicket stop = new BuyTicket();
new Thread(stop,"小黑").start();
new Thread(stop,"小白").start();
new Thread(stop,"小红").start();
}
}
class BuyTicket implements Runnable{
private int ticketNum = 10;
private boolean flag = true;
@Override
public void run() {
while (flag){
try {
buy();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
private void buy() throws InterruptedException {
if (ticketNum<=0){
flag = false;
return;
}
System.out.println(Thread.currentThread().getName() + "拿到了第" + ticketNum +"几张票");
ticketNum --;
Thread.sleep(200);
}
}
14、线程同步方法![](https://i-blog.csdnimg.cn/blog_migrate/22ac3da4ecb781818c281fec7bde5f4a.png)
14.1.同步方法弊端
14.2.不安全买票改造
public class Stop{
public static void main(String[] args) {
BuyTicket stop = new BuyTicket();
new Thread(stop,"小黑").start();
new Thread(stop,"小白").start();
new Thread(stop,"小红").start();
}
}
class BuyTicket implements Runnable{
private int ticketNum = 10;
private boolean flag = true;
@Override
public void run() {
while (flag){
try {
buy();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
private synchronized void buy() throws InterruptedException {
if (ticketNum<=0){
flag = false;
return;
}
System.out.println(Thread.currentThread().getName() + "拿到了第" + ticketNum +"几张票");
ticketNum --;
Thread.sleep(200);
}
}
14.3.同步块: ![](https://i-blog.csdnimg.cn/blog_migrate/f3470262c049c7b887402f0ad90756aa.png)
个人理解:obj就是谁进行了增删改操作就写哪个对象
15、 死锁![](https://i-blog.csdnimg.cn/blog_migrate/2f02b68b9d8a380e77db7710b56e3107.png)
举例子:
/**
* 死锁:多个线程互相抱着对方需要的资源,然后形成僵持
*/
public class DeadLock{
public static void main(String[] args) {
Makeup g1 = new Makeup(0,"灰姑凉");
Makeup g2 = new Makeup(0,"白雪");
new Thread(g1,"灰姑凉").start();
new Thread(g2,"白雪").start();
}
}
//口红
class Lipstick{
}
//镜子
class Mirror{
}
class Makeup implements Runnable{
//需要的资源只有一份,用static来保证只有一份
static Lipstick lipstick = new Lipstick();
static Mirror mirror = new Mirror();
int choice;//选择
String girlName;//使用化妆品的人
Makeup(int choice,String girlName){
this.choice = choice;
this.girlName = girlName;
}
@Override
public void run() {
//化妆
try {
makeup();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private void makeup() throws InterruptedException {
if (choice == 0) {
synchronized (lipstick) {//获取口红的锁
System.out.println(Thread.currentThread().getName() + ":获取到了口红的锁");
Thread.sleep(1000);
synchronized (mirror){//一秒后想获得镜子
System.out.println(Thread.currentThread().getName()+":获得了镜子的锁");
}
}
}else {
synchronized (mirror) {//获取镜子的锁
System.out.println(Thread.currentThread().getName() + ":获取到了口红的锁");
Thread.sleep(1000);
synchronized (lipstick){//一秒后想获得口红
System.out.println(Thread.currentThread().getName()+":获得了口红的锁");
}
}
}
}
}
解决:
package com.aliyun.server;
/**
* 死锁:多个线程互相抱着对方需要的资源,然后形成僵持
*/
public class DeadLock{
public static void main(String[] args) {
Makeup g1 = new Makeup(0,"灰姑凉");
Makeup g2 = new Makeup(0,"白雪");
new Thread(g1,"灰姑凉").start();
new Thread(g2,"白雪").start();
}
}
//口红
class Lipstick{
}
//镜子
class Mirror{
}
class Makeup implements Runnable{
//需要的资源只有一份,用static来保证只有一份
static Lipstick lipstick = new Lipstick();
static Mirror mirror = new Mirror();
int choice;//选择
String girlName;//使用化妆品的人
Makeup(int choice,String girlName){
this.choice = choice;
this.girlName = girlName;
}
@Override
public void run() {
//化妆
try {
makeup();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private void makeup() throws InterruptedException {
if (choice == 0) {
synchronized (lipstick) {//获取口红的锁
System.out.println(Thread.currentThread().getName() + ":获取到了口红的锁");
Thread.sleep(1000);
}
synchronized (mirror){//一秒后想获得镜子
System.out.println(Thread.currentThread().getName()+":获得了镜子的锁");
}
}else {
synchronized (mirror) {//获取镜子的锁
System.out.println(Thread.currentThread().getName() + ":获取到了口红的锁");
Thread.sleep(1000);
}
synchronized (lipstick){//一秒后想获得口红
System.out.println(Thread.currentThread().getName()+":获得了口红的锁");
}
}
}
}
不相互抱对方的锁就可以了
死锁产生条件:
16、Lock锁![](https://i-blog.csdnimg.cn/blog_migrate/8ad585500852028474440b12394ce9ba.png)
ReentrantLock:可重入锁
/**
* lock锁
*/
public class TestLock {
public static void main(String[] args) {
BuyTicket stop = new BuyTicket();
new Thread(stop,"小黑").start();
new Thread(stop,"小白").start();
new Thread(stop,"小红").start();
}
}
class BuyTicket implements Runnable{
private int ticketNum = 10;
private boolean flag = true;
//定义lock锁
private ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
while (flag){
try {
lock.lock();//加锁
try {
buy();
} catch (InterruptedException e) {
e.printStackTrace();
}
}finally {
lock.unlock();//解锁
}
}
}
private void buy() throws InterruptedException {
if (ticketNum<=0){
flag = false;
return;
}
System.out.println(Thread.currentThread().getName() + "拿到了第" + ticketNum +"几张票");
ticketNum --;
Thread.sleep(200);
}
}
synchronized和Lock的对比![](https://i-blog.csdnimg.cn/blog_migrate/779e8c52db1915a4876408265c4e7c43.png)
17、线程通信
17.1.线程协作:生产者消费者模式
18、线程池![](https://i-blog.csdnimg.cn/blog_migrate/ae37a0a7afbc9a2e7f9afdb2448a5ea2.png)
/**
* 测试线程池
*/
public class TestPool {
public static void main(String[] args) {
//1.创建服务,创建线程池
//参数:线程池大小
ExecutorService service = Executors.newFixedThreadPool(10);
//2。执行
service.execute(new BuyTicket());
service.execute(new BuyTicket());
service.execute(new BuyTicket());
service.execute(new BuyTicket());
//3.关闭连接
service.shutdownNow();
}
}
class BuyTicket implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}
19、什么是JUC ![](https://i-blog.csdnimg.cn/blog_migrate/8ea712a577ec85f92ec62a2bc7495cb5.png)
20、进程和线程
进程:一个程序 。比如QQ.exe,一个进程往往包含多个线程,至少包含一个
java默认包含几个线程?两个:main线程和gc线程
线程:操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位
21、java能开启线程吗?不能
public synchronized void start() {
/**
* This method is not invoked for the main method thread or "system"
* group threads created/set up by the VM. Any new functionality added
* to this method in the future may have to also be added to the VM.
*
* A zero status value corresponds to state "NEW".
*/
if (threadStatus != 0)
throw new IllegalThreadStateException();
/* Notify the group that this thread is about to be started
* so that it can be added to the group's list of threads
* and the group's unstarted count can be decremented. */
group.add(this);
boolean started = false;
try {
start0();
started = true;
} finally {
try {
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
/* do nothing. If start0 threw a Throwable then
it will be passed up the call stack */
}
}
}
//本地方法,底层调用的是c++
private native void start0();
java是运行在虚拟机上的无法直接操作硬件,start底层调用的是 native方法,实则是c++
21、并发和并行
并发:(多线程操作同一个资源):cpu一核,多个线程快速交替。比如食堂一个窗口,多人排队
并行:(多个人一起行走):cpu多核,多个线程同时执行。比如食堂多个窗口,一人一个窗口
22、并发编程的本质:充分利用CPU的资源
23、sleep和wait区别
1、来自不同的类,wait来自object类,sleep来自Thread
2、锁的释放:wait会释放锁。sleep不会释放,抱着锁睡觉
3、使用的范围不一样:wait:必须在同步代码块中使用。sleep可以在任何地方睡
4、是否需要捕获异常:wait不需要捕获异常。sleep需要捕获异常
24、JUC Lock锁
24.1.Lock接口
api:
实现类:
24.1.1 ReentrantLock(可重入锁)源码:![](https://i-blog.csdnimg.cn/blog_migrate/7d93370ad3601a0d31277477e4dbfb60.png)
公平锁:十分公平:先来后到。比如一个3h的线程排前面,一个3s的排后面。必须等3h的执行完才能执行3s的
非公平锁:十分的不公平:可以插队(默认的)
传统卖票方式(synchronized)
/**
* 传统卖票方式:synchronized
*/
public class test {
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();
}
}
class Ticket{
//属性,方法
private int number = 50;
//卖票方式
public synchronized void sale(){
if (number>0){
System.out.println(Thread.currentThread().getName()+"卖出了第" + (number--)+"张票");
}
}
}
改进卖票方式(lock)
public class test {
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();
}
}
class Ticket{
//属性,方法
private int number = 50;
Lock lock = new ReentrantLock();
//卖票方式
public synchronized void sale(){
lock.lock();//加锁
try {
if (number>0){
System.out.println(Thread.currentThread().getName()+"卖出了第" + (number--)+"张票");
}
}finally {
lock.unlock();//解锁
}
}
}
24.1.2.synchronized和Lock锁的区别
1.synchronized是内置的java关键字,Lock是一个java类
2.synchronized无法判断获取锁的状态,Lock可以判断是否获取到锁
3.synchronized会自动释放,Lock需要手动释放
4.synchronized线程会一直等待,Lock就不一定会等待下去
5.synchronized是可重入锁,不可中断的非公平锁。Lock是可重入锁,可以设置为非公平和公平锁
6. synchronized适合锁少量代码同步问题,Lock适合锁大量的同步代码
25.生产者和消费者问题
25.1.传统版本的代码实现
/**
* 线程之间的通信问题
* 线程交替执行 A B 操作同一个变量 num=0
* A num+1 的同时 B num-1 保持num守恒
*/
public class test {
public static void main(String[] args) throws Exception{
Data data = new Data();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
data.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"A").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
data.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"B").start();
}
}
//等待、业务、通知
class Data{
private int number = 0;
//+1
public synchronized void increment() throws InterruptedException {
if(number != 0){
//等待
this.wait();
}
number ++;
System.out.println(Thread.currentThread().getName() + ":" + number);
//通知其他线程,我+1完毕了
this.notifyAll();
}
//-1
public synchronized void decrement() throws InterruptedException {
if (number == 0){
//等待
this.wait();
}
number --;
System.out.println(Thread.currentThread().getName() + ":" + number);
//通知其他线程,我-1完毕了
this.notifyAll();
}
}
问题存在,A C B D 4个线程会存在虚假唤醒
解决,使用while判断:if判断不会停,while判断一但拿到锁就不会停
/**
* 线程之间的通信问题
* 线程交替执行 A B 操作同一个变量 num=0
* A num+1 的同时 B num-1 保持num守恒
*/
public class test {
public static void main(String[] args) throws Exception{
Data data = new Data();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
data.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"A").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
data.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"B").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
data.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"C").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
data.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"D").start();
}
}
//等待、业务、通知
class Data{
private int number = 0;
//+1
public synchronized void increment() throws InterruptedException {
while (number != 0){
//等待
this.wait();
}
number ++;
System.out.println(Thread.currentThread().getName() + ":" + number);
//通知其他线程,我+1完毕了
this.notifyAll();
}
//-1
public synchronized void decrement() throws InterruptedException {
while (number == 0){
//等待
this.wait();
}
number --;
System.out.println(Thread.currentThread().getName() + ":" + number);
//通知其他线程,我-1完毕了
this.notifyAll();
}
}
25.2.JUC版代码实现![](https://i-blog.csdnimg.cn/blog_migrate/b4d4e29fe99375350c4afeb18f54f62e.png)
/**
* 线程之间的通信问题
* 线程交替执行 A B 操作同一个变量 num=0
* A num+1 的同时 B num-1 保持num守恒
*/
public class test {
public static void main(String[] args) throws Exception{
Data data = new Data();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
data.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"A").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
data.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"B").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
data.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"C").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
data.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"D").start();
}
}
//等待、业务、通知
class Data{
private int number = 0;
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
//+1
public void increment() throws InterruptedException {
lock.lock();
try {
while (number != 0){
//等待
condition.await();
}
number ++;
System.out.println(Thread.currentThread().getName() + ":" + number);
//通知其他线程,我+1完毕了
condition.signal();
}finally {
lock.unlock();
}
}
//-1
public void decrement() throws InterruptedException {
lock.lock();
try {
while (number == 0){
//等待
condition.await();
}
number --;
System.out.println(Thread.currentThread().getName() + ":" + number);
//通知其他线程,我-1完毕了
condition.signal();
}finally {
lock.unlock();
}
}
}
Condition可以做到精准唤醒,比如说A线程可以直接唤醒B线程类似
26.八锁现象
如果判断锁的是谁?
26.1.如下代码 思考:先打印打电话还是发短信?
public class test {
public static void main(String[] args) throws InterruptedException {
Phone phone = new Phone();
Lock lock = new ReentrantLock();
new Thread(()->{
try {
phone.sendSms();
} catch (InterruptedException e) {
e.printStackTrace();
}
},"A").start();
TimeUnit.SECONDS.sleep(1);
new Thread(()->{
phone.call();
},"B").start();
}
}
//等待、业务、通知
class Phone{
public synchronized void sendSms() throws InterruptedException {
TimeUnit.SECONDS.sleep(4);
System.out.println("发短信");
}
public synchronized void call(){
System.out.println("打电话");
}
}
结论:synchronized锁的是对象的调用者,谁调用我就锁谁,两个方法用的是同一个锁(phone),谁先拿到谁先执行。公平锁
26.2.如下代码 思考:先打印发短信还是hello?
public class test {
public static void main(String[] args) throws InterruptedException {
Phone phone = new Phone();
new Thread(()->{
try {
phone.sendSms();
} catch (InterruptedException e) {
e.printStackTrace();
}
},"A").start();
TimeUnit.SECONDS.sleep(1);
new Thread(()->{
phone.hello();
},"B").start();
}
}
//等待、业务、通知
class Phone{
public synchronized void sendSms() throws InterruptedException {
TimeUnit.SECONDS.sleep(4);
System.out.println("发短信");
}
//这里没有锁!不是同步方法,不受锁的影响
public void hello(){
System.out.println("hello");
}
}
hello方法没有锁!不是同步方法,不受锁的影响。因为sendSms方法有休眠,所以hello方法一直先执行,如果sendSms方法没休眠就是先调用哪个方法哪个方法就先执行
26.3. 如下代码 思考:先打印打电话还是发短信?
public class test {
public static void main(String[] args) throws InterruptedException {
Phone phone1 = new Phone();
Phone phone2 = new Phone();
new Thread(()->{
try {
phone1.sendSms();
} catch (InterruptedException e) {
e.printStackTrace();
}
},"A").start();
TimeUnit.SECONDS.sleep(1);
new Thread(()->{
phone2.call();
},"B").start();
}
}
//等待、业务、通知
class Phone{
public synchronized void sendSms() throws InterruptedException {
TimeUnit.SECONDS.sleep(4);
System.out.println("发短信");
}
public synchronized void call() throws InterruptedException {
System.out.println("打电话");
}
}
分析:phone1和phoen2是两个对象,所以是两把锁,不需要排队。因为sendSms方法有休眠,所以call方法一直先执行,如果sendSms方法没休眠就是先调用哪个方法哪个方法就先执行
26.4. 如下代码 思考:先打印打电话还是发短信?(两个方法变成了静态方法stattic修饰的)
public class test {
public static void main(String[] args) throws InterruptedException {
Phone phone = new Phone();
new Thread(()->{
try {
phone.sendSms();
} catch (InterruptedException e) {
e.printStackTrace();
}
},"A").start();
TimeUnit.SECONDS.sleep(1);
new Thread(()->{
phone.call();
},"B").start();
}
}
class Phone{
public static synchronized void sendSms() throws InterruptedException {
TimeUnit.SECONDS.sleep(4);
System.out.println("发短信");
}
public static synchronized void call(){
System.out.println("打电话");
}
}
分析:加上static后锁的就是全局唯一的class对象(phone.class)代码验证如下 26.5
26.5.如下代码 思考:先打印打电话还是发短信?(一个静态方法,一个非静态方法)
public class test {
public static void main(String[] args) throws InterruptedException {
Phone phone = new Phone();
new Thread(()->{
try {
phone.sendSms();
} catch (InterruptedException e) {
e.printStackTrace();
}
},"A").start();
TimeUnit.SECONDS.sleep(1);
new Thread(()->{
phone.call();
},"B").start();
}
}
class Phone{
public static synchronized void sendSms() throws InterruptedException {
TimeUnit.SECONDS.sleep(4);
System.out.println("发短信");
}
public synchronized void call(){
System.out.println("打电话");
}
}
分析:phone.sendSms()锁的是方法调用者,phone.call锁的是phone.class对象,因为sendSms方法有休眠,所以call方法一直先执行,如果sendSms方法没休眠就是先调用哪个方法哪个方法就先执行
26.6. 如下代码 思考:先打印打电话还是发短信?(两个静态方法stattic修饰的,两个对象调用不同的方法)
public class test {
public static void main(String[] args) throws InterruptedException {
Phone phone1 = new Phone();
Phone phone2 = new Phone();
new Thread(()->{
try {
phone1.sendSms();
} catch (InterruptedException e) {
e.printStackTrace();
}
},"A").start();
TimeUnit.SECONDS.sleep(1);
new Thread(()->{
phone2.call();
},"B").start();
}
}
class Phone{
public static synchronized void sendSms() throws InterruptedException {
TimeUnit.SECONDS.sleep(4);
System.out.println("发短信");
}
public static synchronized void call(){
System.out.println("打电话");
}
}
分析:加上static后锁的就是全局唯一的class对象(phone.class),phone1和phoen2都是Phone.class对象
27.CopyOnWriteArrayList
public class test {
public static void main(String[] args) throws InterruptedException {
List<String> 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();
}
}
}
ConcurrentModificationException:并发修改异常
结论:ArrayList在并发的情况下是不安全的
解决方案:
1.使用Vector(),不推荐
2.List<String> list = Collections.synchronizedList(new ArrayList<>());
3.juc类下的CopyOnWreArrayList:写入时候复制 COW是计算机程序设计领域的一种优化策略,多个线程调用的时候,list,读取的时候,固定的写入(覆盖),在写入的时候避免覆盖,造成数据问题
4.CopyOnWreArrayList比Vector效率要高使用的是lock锁,Vector使用的是的synchronized
28.CopyOnWriteArraySet
public class test {
public static void main(String[] args) {
Set<String> set = new HashSet<>();
for (int i = 0; i < 10; i++) {
new Thread(()->{
set.add(UUID.randomUUID().toString().substring(0,5));
System.out.println(set);
},String.valueOf(i)).start();
}
}
}
结论:HashSet在并发的情况下是不安全的
解决方法:
1.Set<Object> set = Collections.synchronizedSet(new HashSet<>());
2.CopyOnWriteArraySet<Object> set = new CopyOnWriteArraySet<>();
HashSet底层就是HashMap:本质就是hashMap的key,是无法重复的
29.ConcurrentHashMap
public class test {
public static void main(String[] args) {
HashMap<Object, Object> map = new HashMap<>();
for (int i = 0; i < 10; i++) {
new Thread(()->{
map.put(Thread.currentThread().getName(),UUID.randomUUID().toString().substring(0,5));
System.out.println(map);
},String.valueOf(i)).start();
}
}
}
结论:HashMap在并发的情况下是不安全的
解决:
1.Map<Object, Object> map = Collections.synchronizedMap(new HashMap<>());
2.ConcurrentHashMap<String, Object> map = new ConcurrentHashMap<>();
ConcurrentHashMap原理:
1.8之前是采用的分段锁
1.8之后采用的是cas
30.Callable
1.可以有返回值
2.可以抛出异常
3.方法不同 ,call()方法
代码实现:
public class test {
public static void main(String[] args) throws ExecutionException, InterruptedException {
MyThread thread = new MyThread();
FutureTask futureTask = new FutureTask(thread);//适配类
new Thread(futureTask,"A").start();
System.out.println(futureTask.get());
}
}
class MyThread implements Callable<String>{
@Override
public String call() throws Exception {
System.out.println("call");
return "12345";
}
}
31.常用的辅助类(CountDownlatch,CyclicBarrier,Semaphore)
31.1.CountDownlatch(减法计数器)![](https://i-blog.csdnimg.cn/blog_migrate/e27506e0303786f2ac185a84290fefd6.png)
public class test {
public static void main(String[] args) throws InterruptedException {
//总数是6,必须要执行任务的时候,再使用
CountDownLatch countDownLatch = new CountDownLatch(6);
for (int i = 1; i <= 6; i++) {
new Thread(()->{
System.out.println(Thread.currentThread().getName()+"走了");
countDownLatch.countDown();//-1
}, String.valueOf(i)).start();
}
countDownLatch.await();//等待计数器归零,再执行后续操作
System.out.println("Close Door");
}
}
原理:
countDownLatch.countDown();//-1
countDownLatch.await();//等待计数器归零,再执行后续操作
每次有线程调用countDown()-1,假设计数器变为0,countDownLatch.await()就会被唤醒,继续执行。
31.2.CyclicBarrier(加法计数器)
public class test {
public static void main(String[] args) throws InterruptedException {
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();
}
}
}
31.3.Semaphore(信号量、限流 )![](https://i-blog.csdnimg.cn/blog_migrate/4c842921e6dd1fb1694b3410c0c21073.png)
public class test {
public static void main(String[] args) throws InterruptedException {
//线程数量,限流
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();
}
}
}
原理:
semaphore.acquire()获得,加入已经满了,等待被释放为止
semaphore.release():释放,会将当前的信号量释放,然后唤醒等待的线程
作用:多个共享资源互斥的作用!并发限流,控制最大的线程数
32.读写锁(ReadWriteLock)
public class test {
public static void main(String[] args){
MyCache myCache = new MyCache();
//写入
for (int i = 0; i < 5; i++) {
final int temp = i;
new Thread(()->{
myCache.put(temp+"",temp+"");
},String.valueOf(i)).start();
}
//读取
for (int i = 0; i < 5; i++) {
final int temp = i;
new Thread(()->{
myCache.get(temp+"");
},String.valueOf(i)).start();
}
}
}
class MyCache{
private final Map<String,Object> map = new HashMap<>();
//存、写
public void put(String key,Object value){
System.out.println(Thread.currentThread().getName()+"写入"+key);
map.put(key,value);
System.out.println(Thread.currentThread().getName()+"写入ok");
}
//取、读
public void get(String key){
System.out.println(Thread.currentThread().getName()+"读取"+key);
Object o = map.get(key);
System.out.println(Thread.currentThread().getName()+"读取ok");
}
}
从结果来看有写入未完成被插队写入的情况出现,是不正常的。
用读写锁改良后:
public class test {
public static void main(String[] args){
MyCache myCache = new MyCache();
//写入
for (int i = 0; i < 5; i++) {
final int temp = i;
new Thread(()->{
myCache.put(temp+"",temp+"");
},String.valueOf(i)).start();
}
//读取
for (int i = 0; i < 5; i++) {
final int temp = i;
new Thread(()->{
myCache.get(temp+"");
},String.valueOf(i)).start();
}
}
}
class MyCache{
private final Map<String,Object> map = new HashMap<>();
private final ReadWriteLock lock = new ReentrantReadWriteLock();
//存、写
public void put(String key,Object value){
lock.writeLock().lock();
try {
System.out.println(Thread.currentThread().getName()+"写入"+key);
map.put(key,value);
System.out.println(Thread.currentThread().getName()+"写入ok");
}finally {
lock.writeLock().unlock();
}
}
//取、读
public void get(String key){
lock.readLock().lock();
try {
System.out.println(Thread.currentThread().getName()+"读取"+key);
Object o = map.get(key);
System.out.println(Thread.currentThread().getName()+"读取ok");
}finally {
lock.readLock().unlock();
}
}
}
读-读:可以共存
读-写:不可以共存
写-写:不可以共存
33.阻塞队列![](https://i-blog.csdnimg.cn/blog_migrate/fed5f402a17cd384d60ba4e1b5b7b6ba.png)
BlockingQueue:阻塞队列
什么情况下我们会使用 阻塞队列:多线程和线程池
33.1.四组API:
1.抛出异常
新增:add()
删除:remove()
查看队首元素:peek()
public class test {
public static void main(String[] args){
test1();
}
public static void test1(){
//队列大小为3
ArrayBlockingQueue<Object> blockingQueue = new ArrayBlockingQueue<>(3);
System.out.println(blockingQueue.add("a"));
System.out.println(blockingQueue.add("b"));
System.out.println(blockingQueue.add("c"));
//报错:Queue full,超出了队列大小
// System.out.println(blockingQueue.add("d"));
System.out.println("--------------");
System.out.println(blockingQueue.remove());
System.out.println(blockingQueue.remove());
System.out.println(blockingQueue.remove());
//报错:NoSuchElementException 队列为空,没有可删除的元素
//System.out.println(blockingQueue.remove());
}
}
2.不抛出异常
新增:offer()
弹出、删除:poll()
查看队首元素:element()
public class test {
public static void main(String[] args){
test1();
}
public static void test1(){
//队列大小为3
ArrayBlockingQueue<Object> blockingQueue = new ArrayBlockingQueue<>(3);
System.out.println(blockingQueue.offer("a"));
System.out.println(blockingQueue.offer("b"));
System.out.println(blockingQueue.offer("c"));
// false
System.out.println(blockingQueue.offer("d"));
System.out.println("--------------");
System.out.println(blockingQueue.poll());
System.out.println(blockingQueue.poll());
System.out.println(blockingQueue.poll());
// null
System.out.println(blockingQueue.poll());
}
}
3.阻塞
新增:put()
移除:take()
public class test {
public static void main(String[] args) throws InterruptedException {
test1();
}
public static void test1() throws InterruptedException {
//队列大小为3
ArrayBlockingQueue<Object> blockingQueue = new ArrayBlockingQueue<>(3);
blockingQueue.put("a");
blockingQueue.put("b");
blockingQueue.put("c");
blockingQueue.put("d");//队列没位置了,一直阻塞,直到存进去为止
System.out.println(blockingQueue.take());
System.out.println(blockingQueue.take());
System.out.println(blockingQueue.take());
System.out.println(blockingQueue.take());
System.out.println(blockingQueue.take());//没有这个元素,一直阻塞。直到拿到为止
}
}
4.超时等待(实际这个用的多)
新增:offer()
弹出、删除:poll(,,)
查看队首元素:element(,)
public class test {
public static void main(String[] args) throws InterruptedException {
test1();
}
public static void test1() throws InterruptedException {
//队列大小为3
ArrayBlockingQueue<Object> blockingQueue = new ArrayBlockingQueue<>(3);
System.out.println(blockingQueue.offer("a"));
System.out.println(blockingQueue.offer("b"));
System.out.println(blockingQueue.offer("c"));
System.out.println(blockingQueue.offer("d", 2, TimeUnit.SECONDS));//等待2s就退出
System.out.println("---------");
System.out.println(blockingQueue.poll());
System.out.println(blockingQueue.poll());
System.out.println(blockingQueue.poll());
System.out.println(blockingQueue.poll(2,TimeUnit.SECONDS));//等待2s就退出
}
}
33.2. 同步队列
进去一个元素,必须等待取出来之后,才能再往里面放一个元素!(容量为1)
put、take
34、线程池
池化技术:实现准备好一些资源,有人要用,就来我这里拿,用完后还给我
好处:
1、降低资源消耗
2、提高响应速度
3、方便管理
34.1.Executors
34.1.1.Executors.newSingleThreadExecutor():单个线程
public class test {
public static void main(String[] args) throws InterruptedException {
ExecutorService service = Executors.newSingleThreadExecutor();
try {
for (int i = 0; i < 5; i++) {
final int tem = i;
service.execute(() -> {
System.out.println(Thread.currentThread().getName()+"->"+tem);
});
}
}catch (Exception e){
e.printStackTrace();
} finally {
service.shutdownNow();//关机
}
}
34.1.2.Executors.newFixedThreadPool():固定大小线程池
public class test {
public static void main(String[] args) throws InterruptedException {
ExecutorService service = Executors.newFixedThreadPool(4);
try {
for (int i = 0; i < 5; i++) {
final int tem = i;
service.execute(() -> {
System.out.println(Thread.currentThread().getName()+"->"+tem);
});
}
}catch (Exception e){
e.printStackTrace();
} finally {
service.shutdownNow();//关机
}
}
}
34.1.3.Executors.newCachedThreadPool():遇强则强,可伸缩的
public class test {
public static void main(String[] args) throws InterruptedException {
ExecutorService service = Executors.newCachedThreadPool();
try {
for (int i = 0; i < 10; i++) {
final int tem = i;
service.execute(() -> {
System.out.println(Thread.currentThread().getName()+"->"+tem);
});
}
}catch (Exception e){
e.printStackTrace();
} finally {
service.shutdownNow();//关机
}
}
}
35.七大参数
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
public static ExecutorService newWorkStealingPool(int parallelism) {
return new ForkJoinPool
(parallelism,
ForkJoinPool.defaultForkJoinWorkerThreadFactory,
null, true);
}
public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(),
threadFactory);
}
通过以上源码分析可以知道,这些方法底层实质上都是调用的ThreadPoolExecutor()方法
public ThreadPoolExecutor(int corePoolSize,//核心线程池大小
int maximumPoolSize,//最大核心线程池大小
long keepAliveTime,//超时时间
TimeUnit unit,//超时单位
BlockingQueue<Runnable> workQueue,//阻塞队列
ThreadFactory threadFactory,//线程工厂,创建线程的
RejectedExecutionHandler handler//拒绝策略
) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
35.1.七大参数讲解![](https://i-blog.csdnimg.cn/blog_migrate/378d0faa36e44f0965173885963d452f.png)
图解:其中一二号窗口为核心线程数,会一直开放。三四五号窗口加上一二号窗口是最大线程数,但是不会一直开开放,会设置超时等待时间,当过了超时等待时间,没人办理业务,三四五窗口会关闭。当队列和核心线程数都满了,在有线程进来的时候才会逐个开放。候客区就是队列的长度。当最大线程数加上队列都满了会触发拒绝策略(四种)。
最大承载 = 队列+最大线程数
35.1.1.四种拒绝策略![](https://i-blog.csdnimg.cn/blog_migrate/1be694340b6ce053353e238ba3725477.png)
35.1.1.1.AbortPolicy:超过最大承载会抛出异常
public class test {
public static void main(String[] args) throws InterruptedException {
ThreadPoolExecutor threadPool = new ThreadPoolExecutor(
2,//核心线程数
5,//最大线程数
3,//超时时间
TimeUnit.SECONDS,//超时时间单位
new LinkedBlockingQueue<>(3),//队列
Executors.defaultThreadFactory(),//线程工厂
new ThreadPoolExecutor.AbortPolicy()//拒绝策略
);
//最大承载数:max(5) + queue(3)
try {
for (int i = 1; i <= 11 ; i++) {
final int tem = i;
threadPool.execute(() -> {
System.out.println(Thread.currentThread().getName()+"->"+tem);
});
}
}catch (Exception e){
e.printStackTrace();
} finally {
threadPool.shutdownNow();//关机
}
}
}
35.1.1.2.CallerRunsPolicy:哪来的去哪里(由main线程执行多余的)
public class test {
public static void main(String[] args) throws InterruptedException {
ThreadPoolExecutor threadPool = new ThreadPoolExecutor(
2,//核心线程数
5,//最大线程数
3,//超时时间
TimeUnit.SECONDS,//超时时间单位
new LinkedBlockingQueue<>(3),//队列
Executors.defaultThreadFactory(),//线程工厂
new ThreadPoolExecutor.CallerRunsPolicy()//拒绝策略->哪来的去哪里
);
//最大承载数:max(5) + queue(3)
try {
for (int i = 1; i <= 11 ; i++) {
final int tem = i;
threadPool.execute(() -> {
System.out.println(Thread.currentThread().getName()+"->"+tem);
});
}
}catch (Exception e){
e.printStackTrace();
} finally {
threadPool.shutdownNow();//关机
}
}
}
35.1.1.3.DiscardPolicy:哪来的去哪里(不会抛出异常,满了会丢掉任务)
public class test {
public static void main(String[] args) throws InterruptedException {
ThreadPoolExecutor threadPool = new ThreadPoolExecutor(
2,//核心线程数
5,//最大线程数
3,//超时时间
TimeUnit.SECONDS,//超时时间单位
new LinkedBlockingQueue<>(3),//队列
Executors.defaultThreadFactory(),//线程工厂
new ThreadPoolExecutor.DiscardPolicy()//不会抛出异常,满了会丢掉任务
);
//最大承载数:max(5) + queue(3)
try {
for (int i = 1; i <= 11 ; i++) {
final int tem = i;
threadPool.execute(() -> {
System.out.println(Thread.currentThread().getName()+"->"+tem);
});
}
}catch (Exception e){
e.printStackTrace();
} finally {
threadPool.shutdownNow();//关机
}
}
}
35.1.1.4.DiscardOldestPolicy:尝试和最早的线程竞争,也不会抛出异常
public class test {
public static void main(String[] args) throws InterruptedException {
ThreadPoolExecutor threadPool = new ThreadPoolExecutor(
2,//核心线程数
5,//最大线程数
3,//超时时间
TimeUnit.SECONDS,//超时时间单位
new LinkedBlockingQueue<>(3),//队列
Executors.defaultThreadFactory(),//线程工厂
new ThreadPoolExecutor.DiscardOldestPolicy()//尝试和最早的线程竞争,也不会抛出异常
);
//最大承载数:max(5) + queue(3)
try {
for (int i = 1; i <= 11 ; i++) {
final int tem = i;
threadPool.execute(() -> {
System.out.println(Thread.currentThread().getName()+"->"+tem);
});
}
}catch (Exception e){
e.printStackTrace();
} finally {
threadPool.shutdownNow();//关机
}
}
}
36.CPU密集型和IO密集型
36.1.池的最大大小怎么去设置
36.1.1.CPU密集型
机器是几核,就是几,可以保持cpu的效率最高
获取机器核数代码
Runtime.getRuntime().availableProcessors()
代码改造
public class test {
public static void main(String[] args) throws InterruptedException {
ThreadPoolExecutor threadPool = new ThreadPoolExecutor(
2,//核心线程数
Runtime.getRuntime().availableProcessors(),//最大线程数
3,//超时时间
TimeUnit.SECONDS,//超时时间单位
new LinkedBlockingQueue<>(3),//队列
Executors.defaultThreadFactory(),//线程工厂
new ThreadPoolExecutor.DiscardOldestPolicy()//尝试和最早的线程竞争,也不会抛出异常
);
//最大承载数:max(5) + queue(3)
try {
for (int i = 1; i <= 11 ; i++) {
final int tem = i;
threadPool.execute(() -> {
System.out.println(Thread.currentThread().getName()+"->"+tem);
});
}
}catch (Exception e){
e.printStackTrace();
} finally {
threadPool.shutdownNow();//关机
}
}
}
36.1.2.IO密集型
会判断程序中十分耗IO的线程。然后最大线程数一般设置是这个值的两倍
37.ForkJoin
37.1.什么是forkJoin
forkJoin 在JDK1.7,并行执行任务!提高效率。大数据量!(把大任务拆为小人物)
37.2.特点
工作窃取:维护的都是双端队列
B执行完了,A会把B窃取过来执行,提高工作效率
38.异步回调(Future)
设计的初衷:对将来的某个事件的结果进行建模
38.1.没有返回值
/**
* 并行执行
* 成功回调
* 失败回调
*/
public class test{
public static void main(String[] args) throws Exception {
CompletableFuture<Void> completableFuture =CompletableFuture.runAsync(()->{
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
System.out.println("111");
completableFuture.get();//获取阻塞执行结果
}
}
38.2.有返回值
public class test{
public static void main(String[] args) throws Exception {
CompletableFuture<String> completableFuture =CompletableFuture.supplyAsync(()->{
return "1024";
});
System.out.println("111");
completableFuture.get();//获取阻塞执行结果
}
}
39.JMM
39.1.什么是jmm
java内存模型,概念
39.2.JMM的同步约定
1.线程解锁前,必须立刻把共享变量刷回主存
图解:线程拷贝主存的东西,而不是直接从主存拿来用。当拷贝的被修改,就必须马上把共享变量同步回主线程
2.线程加锁前,必须读取主存中的最新值到工作内存。
3.加锁和解锁是同一把锁
39.3.线程、工作内存、主内存8种操作
·read 读取:作用于主内存,将共享变量从主内存传送到线程的工作内存中。
·load 载入:作用于工作内存,把 read 读取的值放到工作内存中的副本变量中。
·store 存储:作用于工作内存,把工作内存中的变量传送到主内存中。
·write 写入:作用于主内存,把从工作内存中 store 传送过来的值写到主内存的变量中。
·use 使用:作用于工作内存,把工作内存的值传递给执行引擎,当虚拟机遇到一个需要使用这个变量的指令时,就会执行这个动作。
·assign 赋值:作用于工作内存,把执行引擎获取到的值赋值给工作内存中的变量,当虚拟机栈遇到给变量赋值的指令时,就执行此操作。
·lock锁定: 作用于主内存,把变量标记为线程独占状态。
·unlock解锁: 作用于主内存,它将释放独占状态。
public static void main(String[] args) throws Exception {
new Thread(()->{
while (num == 0){
System.out.println("a");
}
}).start();
TimeUnit.SECONDS.sleep(2);
num =1;
System.out.println(num);
}
问题:程序不知道主内存的值已经被修改(用Volatile解决)
40.Volatile
是java虚拟机提供轻量级的同步机制
40.1.特点
1.保证可见性
public class test{
//不加volatile就会死循环
private volatile static int num = 0;
public static void main(String[] args) throws Exception {
new Thread(()->{
while (num == 0){
System.out.println("a");
}
}).start();
TimeUnit.SECONDS.sleep(1);
num =1;
System.out.println(num);
}
}
2.不保证原子性
原子性:不可分割
比如线程在执行任务的时候是不能被打扰的,也不能被分割。要么同时成功,要么同时失败。
public class test{
private volatile static int num = 0;
public static void add(){
num ++;
}
public static void main(String[] args) throws Exception {
//理论上结果应该为2万
for (int i = 1; i <= 20; i++) {
new Thread(()->{
for (int j = 0; j < 1000; j++) {
add();
}
}).start();
}
while (Thread.activeCount()>2){//获取线程数量 2:java程序默认有main线程和gc线程
Thread.yield();//礼让
}
System.out.println(num);
}
}
可以看出,结果不对,体现了不保证原子性
如果不加synchronized和lock怎样保证原子性呢?
num ++ 不是原子性的操作,分为了三步
使用原子类解决原子性的问题
public class test{
private static AtomicInteger num = new AtomicInteger();
public static void main(String[] args){
//理论上结果应该为2万
for (int i = 1; i <= 20; i++) {
new Thread(()->{
for (int j = 0; j < 1000; j++) {
num.getAndIncrement();
}
}).start();
}
while (Thread.activeCount()>2){//获取线程数量 2:java程序默认有main线程和gc线程
Thread.yield();//礼让
}
System.out.println(num);
}
}
原子类可以保证原子性,这些类的底层都和操作系统挂钩!在内存中修改值!Unsafe类是个很特殊的存在
源代码-->编译器优化的重拍-->指令并行也可能重排-->内存系统也会重排-->执行
3.禁止指令重排
什么是指令重排:你写的程序,计算机并不是按照你写的那样去执行的
int x = 1; // 1
int y = 2; // 2
x = x + 5; //3
y = x + y; // 4
我们期望的执行顺序:1234
但是有可能的执行顺序: 2134 1324
有没有可能是 4123呢?不可能
处理器在进行指令重排的时候会考虑数据之间的依赖性
可能造成影响的结果:abxy这四个值默认都是0:
线程A | 线程B |
x=a | y=b |
b=1 | a=2 |
正常的结果:x=0;y=0;但是有可能b=1先执行等情况从而改变x,y的初始值。
41.彻底玩转单例模式
恶汉式单例
一开始就加载
public class Hungry {
private Hungry(){
}
private final static Hungry HUNGRY = new Hungry();
public static Hungry getInstance(){
return HUNGRY;
}
}
public class Hungry {
//一开始就把所有东西全部加载进来,会导致内存浪费
private byte[] bytes1 = new byte[1924*1024];
private byte[] bytes2 = new byte[1924*1024];
private byte[] bytes3 = new byte[1924*1024];
private byte[] bytes4 = new byte[1924*1024];
Hungry(){
}
private final static Hungry HUNGRY = new Hungry();
public static Hungry getInstance(){
return HUNGRY;
}
}
懒汉式单例
需要的时候才加载,以下单线程下是ok的,多线程下会有问题
/**
* 懒汉式:需要的时候才加载,单线程下是ok的,多线程下会有问题
*/
public class LazyMan {
private LazyMan(){
}
private static LazyMan lazyMan;
public static LazyMan getInstance(){
if (lazyMan == null){
lazyMan = new LazyMan();
}
return lazyMan;
}
}
public class LazyMan {
private LazyMan(){
System.out.println(Thread.currentThread().getName()+":ok");
}
private static LazyMan lazyMan;
public static LazyMan getInstance(){
if (lazyMan == null){
lazyMan = new LazyMan();
}
return lazyMan;
}
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
new Thread(()->{
LazyMan.getInstance();
}).start();
}
}
}
41.2.1.双重检测锁模式的懒汉式单里 DCL懒汉式
public class LazyMan {
private LazyMan(){
System.out.println(Thread.currentThread().getName()+":ok");
}
private volatile static LazyMan lazyMan;
public static LazyMan getInstance(){
if (lazyMan == null){
synchronized (LazyMan.class) {
if (lazyMan == null) {
lazyMan = new LazyMan();
}
}
}
return lazyMan;
}
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
new Thread(()->{
LazyMan.getInstance();
}).start();
}
}
}
42.深入理解CAS
什么是cas: 比较并交换
CAS:比较当前工作内存中的值和主内存中的值,如果这个值是期望的,那么则执行操作!如果不是就一直循环(底层是自选锁)
缺点:
1.循环耗时
2.一次只能保证一个共享变量的原子性
3.ABA问题
public class LazyMan {
public static void main(String[] args) {
AtomicInteger atomicInteger = new AtomicInteger(2023);
//如果我期望的值(2023)达到了,那么就更新,否则不更新
boolean a = atomicInteger.compareAndSet(2023, 2024);
System.out.println(atomicInteger.get());
System.out.println(a);
boolean b = atomicInteger.compareAndSet(2023, 2024);
System.out.println(atomicInteger.get());
System.out.println(b);
}
}
Unsafe类![](https://i-blog.csdnimg.cn/blog_migrate/a62c96c6d6374c3cc21b3f02e5bc8659.png)
43.ABA问题
图解问题说明:主内存被来A = 1,A线程和B线程同时拿到了a=1.但是B线程从1换到了3,然后又改成了1.导致A线程不知道主线程的值已经改过了(狸猫换太子)
public class LazyMan {
public static void main(String[] args) {
AtomicInteger atomicInteger = new AtomicInteger(2023);
//如果我期望的值(2023)达到了,那么就更新,否则不更新
//============捣乱的线程===============
boolean a = atomicInteger.compareAndSet(2023, 2024);
System.out.println(a);
boolean b = atomicInteger.compareAndSet(2024, 2023);
System.out.println(b);
//===========期望的线程=================
boolean c = atomicInteger.compareAndSet(2023, 666);
System.out.println(a);
}
}
44.原子引用解决ABA问题
带版本号的原子操作:
public class LazyMan {
public static void main(String[] args) {
AtomicStampedReference<Integer> reference = new AtomicStampedReference<>(3, 1);
new Thread(()->{
int stamp = reference.getStamp();//获取版本号
System.out.println("a1=>" + stamp);
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(reference.compareAndSet(3,
4,
reference.getStamp(),//版本号
reference.getStamp() + 1//版本号+1
));
System.out.println("a2=>" + reference.getStamp());
System.out.println(reference.compareAndSet(4,
3,
reference.getStamp(),//版本号
reference.getStamp() + 1//版本号+1
));
System.out.println("a3=>" + reference.getStamp());
},"a").start();
new Thread(()->{
int stamp = reference.getStamp();//获取版本号
System.out.println("b1=>"+stamp);
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(reference.compareAndSet(3, 6, stamp, stamp + 1));
System.out.println("b2=>"+reference.getStamp());
},"b").start();
}
}
45.各种锁的理解
公平锁、非公平锁
公平锁:非常公平,不能够插队,必须先来后到
非公平锁:非常不公平,可以插队(默认都是非公平)
可重入锁
递归锁
自旋锁
不断的尝试,直到成功为止
//自旋锁
public class LazyMan {
AtomicReference<Object> atomicReference = new AtomicReference<>();
//加锁
public void myLock(){
Thread thread = Thread.currentThread();
System.out.println(Thread.currentThread().getName() + "==>myLock");
//自选锁
while (!atomicReference.compareAndSet(null,thread)){
}
}
//解锁
public void myUnLock(){
Thread thread = Thread.currentThread();
System.out.println(Thread.currentThread().getName()+"==>myUnLock");
atomicReference.compareAndSet(thread,null);
}
public static void main(String[] args) {
LazyMan lock = new LazyMan();
lock.myLock();
lock.myUnLock();
}
}