java util concurrent 简称, java并发编程
源码+官网文档 jdk1.8在线文档
先回顾点基础知识:
进程:大, 一个程序,QQ.exe , MUSIC.exe 程序的集成
一个进程可以包含多个线程,至少包含一个!!!
java 默认有几个线程?2个 main、GC
线程:开了一个Typora进程,写字,自动保存(线程负责的)
Thread、Runnable、Callable
java真的可以开启线程吗? 开不了 new Thread().start() 源码中是调用native start0()本地方法,底层中的C++, java 无法直接操作硬件
并发编程:并发、并行
并发:(多线程操作同一资源)
并行:(多个人一起行走)
/**
* 代码获取CPU核数
*
* @author sdm
* @version 1.0
* @date 2023/6/19 15:03
*/
public class test1 {
public static void main(String[] args) {
// 获取CPU的核数
// CPU密集型、IO密集型
System.out.println(Runtime.getRuntime().availableProcessors()); //8核
}
}
并发编程本质 充分利用CPU的资源
public enum State {
// 1.新生
NEW,
// 2.运行
RUNNABLE,
// 3.阻塞
BLOCKED,
// 4.等待(死死的等)
WAITING,
// 5.超时等待(等3年,三年后等不下去了)
TIMED_WAITING,
// 6.终止
TERMINATED;
}
1.来自不同的类
wait => Object
sleep => Thread
2.关于锁的释放
wait:会释放锁,是醒着等
sleep:睡觉了,抱着锁睡,不会释放!!!
3.使用的范围不同
wait:必选在同步代码块中
sleep:可以在任何地方睡
4.是否需要捕获异常
wait:必须需要捕获异常
sleep:必须要捕获异常,
因为可能会有超时等待 throws InterruptedException
Java 8 中文版 - 在线API中文手册 - 码工具 (matools.com)
传统Sychronize锁
package com.kuang.demo01;
/**
* 基本的卖票例子 不使用JUC(java.util.concurrent.locks 三个接口: 1.Condition 2.Lock 3.ReadWriteLock)
*
* @author sdm
* @version 1.0
* @date 2023/6/19 17:27
*/
public class SaleTicketDemo01 {
// 1. 准备资源类(属性、方法(sychnorized加锁,本质:队列、锁),对共享资源加锁,控制并发)
// 2. 创建多线程
// 3. 将资源类抛进去
// 参考文章:Java线程池的正确使用方式——不要再new Thread了 http://t.csdn.cn/ytpMd
public static void main(String[] args) {
Ticket ticket = new Ticket(); // 并发:多线程操作同一资源类,将资源类丢入线程
new Thread().start();
new Thread(new Runnable() { // @FunctionalInterface 函数式接口
public void run() {
// 匿名内部类,new Runnable(重写run方法)
}
}).start();
new Thread(()->{ }, "线程名").start(); // jdk1.8之后,简化为Lambda表达式
// 三个线程操作同一资源,三个线程同时卖票
new Thread(()->{ for (int i = 0; i < 10; i++) { ticket.sale();}}, "A").start(); // 线程A卖60次
new Thread(()->{ for (int i = 0; i < 10; i++) { ticket.sale();}}, "B").start(); // 线程B卖60次
new Thread(()->{ for (int i = 0; i < 10; i++) { ticket.sale();}}, "C").start(); // 线程C卖60次
// 期望出现结果:
/*
A卖出了第30票,剩余:29
A卖出了第29票,剩余:28
A卖出了第28票,剩余:27
A卖出了第27票,剩余:26
A卖出了第26票,剩余:25
A卖出了第25票,剩余:24
A卖出了第24票,剩余:23
A卖出了第23票,剩余:22
A卖出了第22票,剩余:21
A卖出了第21票,剩余:20
B卖出了第20票,剩余:19
B卖出了第19票,剩余:18
B卖出了第18票,剩余:17
B卖出了第17票,剩余:16
B卖出了第16票,剩余:15
B卖出了第15票,剩余:14
B卖出了第14票,剩余:13
B卖出了第13票,剩余:12
B卖出了第12票,剩余:11
B卖出了第11票,剩余:10
B卖出了第10票,剩余:9
B卖出了第9票,剩余:8
B卖出了第8票,剩余:7
B卖出了第7票,剩余:6
B卖出了第6票,剩余:5
C卖出了第5票,剩余:4
C卖出了第4票,剩余:3
C卖出了第3票,剩余:2
C卖出了第2票,剩余:1
C卖出了第1票,剩余:0
*/
// 实际出现结果, 买票方法需要加锁,排队 public synchronized void sale()
/*
A卖出了第30票,剩余:29
A卖出了第29票,剩余:28
A卖出了第28票,剩余:27
B卖出了第27票,剩余:26
A卖出了第26票,剩余:25
B卖出了第25票,剩余:24
A卖出了第24票,剩余:23
B卖出了第23票,剩余:22
A卖出了第22票,剩余:21
B卖出了第21票,剩余:20
A卖出了第20票,剩余:19
B卖出了第19票,剩余:18
A卖出了第18票,剩余:17
B卖出了第17票,剩余:16
A卖出了第16票,剩余:15
C卖出了第14票,剩余:13
C卖出了第12票,剩余:11
C卖出了第11票,剩余:10
C卖出了第10票,剩余:9
C卖出了第9票,剩余:8
C卖出了第8票,剩余:7
C卖出了第7票,剩余:6
C卖出了第6票,剩余:5
C卖出了第5票,剩余:4
B卖出了第15票,剩余:14
C卖出了第4票,剩余:3
A卖出了第13票,剩余:12
B卖出了第3票,剩余:2
B卖出了第2票,剩余:1
B卖出了第1票,剩余:0
*/
}
}
// 资源类OOP 属性+方法
class Ticket {
// 票数
private int number = 30;
// 卖票:每卖一次,票数减一
public synchronized void sale(){
if (number > 0){
System.out.println(Thread.currentThread().getName() + "卖出了第" + (number--) + "票,剩余:" + number);
}
}
}
Lock接口
公平锁、非公平锁区别:
公平锁:先来后到
非公平锁:可以插队(默认非公平锁)
package com.kuang.demo01;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* 基本的卖票例子 使用JUC(java.util.concurrent.locks 三个接口: 1.Condition 2.Lock 3.ReadWriteLock,使用Lock)
*
* @author sdm
* @version 1.0
* @date 2023/6/19 17:27
*/
public class SaleTicketDemo02 {
/*
公平锁、非公平锁区别:
公平锁:先来后到
非公平锁:可以插队(默认非公平锁)
public ReentrantLock() {
sync = new ReentrantLock.NonfairSync(); // 默认非公平锁
}
public ReentrantLock(boolean fair) {
sync = fair ? new ReentrantLock.FairSync() : new ReentrantLock.NonfairSync();
}
sychnorized 和 lock锁使用区别:
sychnorized:手动挡,需要手动创建线程
lock锁:自动挡,走lock三部曲就行
1. new ReentrantLock(), new可重入锁
2. Lock.lock()加锁,
3. finally {Lock.unlock()} 解锁
*/
public static void main(String[] args) {
// 并发:多线程操作同一资源类,将资源类丢入线程
Ticket1 ticket1 = new Ticket1();
new Thread(()->{for (int i = 0; i < 15; i++) ticket1.sale();}, "线程A").start(); // A线程卖15张票
new Thread(()->{for (int i = 0; i < 15; i++) ticket1.sale();}, "线程B").start(); // B线程卖15张票
new Thread(()->{for (int i = 0; i < 15; i++) ticket1.sale();}, "线程C").start(); // C线程卖15张票
/*
线程A卖出了第30票,剩余:29
线程A卖出了第29票,剩余:28
线程C卖出了第28票,剩余:27
线程C卖出了第27票,剩余:26
线程C卖出了第26票,剩余:25
线程C卖出了第25票,剩余:24
线程C卖出了第24票,剩余:23
线程C卖出了第23票,剩余:22
线程C卖出了第22票,剩余:21
线程C卖出了第21票,剩余:20
线程C卖出了第20票,剩余:19
线程C卖出了第19票,剩余:18
线程C卖出了第18票,剩余:17
线程C卖出了第17票,剩余:16
线程C卖出了第16票,剩余:15
线程C卖出了第15票,剩余:14
线程C卖出了第14票,剩余:13
线程B卖出了第13票,剩余:12
线程B卖出了第12票,剩余:11
线程B卖出了第11票,剩余:10
线程B卖出了第10票,剩余:9
线程B卖出了第9票,剩余:8
线程B卖出了第8票,剩余:7
线程B卖出了第7票,剩余:6
线程B卖出了第6票,剩余:5
线程B卖出了第5票,剩余:4
线程B卖出了第4票,剩余:3
线程B卖出了第3票,剩余:2
线程B卖出了第2票,剩余:1
线程B卖出了第1票,剩余:0
*/
}
}
class Ticket1 {
// new一个可重入锁
private final ReentrantLock lock = new ReentrantLock();
private int number = 30;
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(); // 释放锁
}
}
}
sychnorized:自动挡,不需要手动创建锁、释放锁
lock锁:手动挡,需要手动创建锁,释放锁,走lock三部曲就行
生产者和消费者 synorchized版 实现效果:交替打印A=>1、B=>0
package com.kuang.demo02;
/**
* 线程之间的通信问题:生产者消费者 (一个窗口,一次放一碗饭饭,阿姨这边打饭,同学这边拿饭)
* 线程交替执行 A B 线程操作同一个变量 等待唤醒 wait,通知唤醒 notify
* A num+1
* B num-1
*
* @author sdm
* @version 1.0
* @date 2023/6/24 11:12
*/
public class Test {
public static void main(String[] args) {
// 1.准备资源类
Goods goods = new Goods();
// 2.创建多线程, 3.将资源类抛进去
new Thread(() -> {
for (int i = 0; i < 10; i++) {
try {
goods.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "A").start(); // A线程+1
new Thread(() -> {
for (int i = 0; i < 5; i++) {
try {
goods.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "B").start(); // B线程-1
// 预期结果:A=>1 B=>0 A=>1 B=>0 ...
/*
为什么出现交替打印结果?
A=>1
B=>0
A=>1
B=>0
A=>1
B=>0
A=>1
B=>0
A=>1
B=>0
A=>1
线程A,判断number是否等于0,等于0,干活+1,然后唤醒B,不等于0,等着
线程B,判断number是否等于1,等于1,干活-1,然后唤醒A,不等于0,等着
*/
}
}
/*
资源类:饭 一个窗口
生产者消费者问题:六字真言: (判断)等待、业务、通知
*/
class Goods{
private int number = 0;
//+1 生产了一件物品
public synchronized void increment() throws InterruptedException {
if (number != 0){ //O的时候,干活,不是0的时候,等待
// 等待 窗口有一碗饭时,等会
this.wait();
}
// 业务
number ++;
System.out.println(Thread.currentThread().getName() + "=>" + number);
// 通知其他线程,我+1完毕了
this.notifyAll(); // 通知唤醒其他线程
}
//-1 消费了一件物品
public synchronized void decrement() throws InterruptedException {
if (number == 0){ //1的时候,干活,不是1的时候,等待
// 等待 窗口没有饭时,等会
this.wait();
}
// 业务
number --;
System.out.println(Thread.currentThread().getName() + "=>" + number);
// 通知其他线程,我-1完毕了
this.notifyAll(); // 通知唤醒其他线程
}
}
问题存在,A B C D 4个线程,还安全吗?
实现效果:交替打印 1、0 ,但是A B C D 不会精准交替
答:不安全 存在if判断等待时,发生虚假唤醒,需要替换为while判断等待
package com.kuang.demo02;
/**
* A B C D 线程交替打印
*/
public class ThreadDemoABCD {
public static void main(String[] args) {
// 1.准备资源类
Goods1 goods = new Goods1();
// 2.创建多线程, 3.将资源类抛进去
new Thread(() -> {
for (int i = 0; i < 10; i++) {
try {
goods.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "A").start(); // A线程+1
new Thread(() -> {
for (int i = 0; i < 10; i++) {
try {
goods.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "B").start(); // B线程-1
new Thread(() -> {
for (int i = 0; i < 10; i++) {
try {
goods.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "C").start(); // C线程+1
new Thread(() -> {
for (int i = 0; i < 10; i++) {
try {
goods.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "D").start(); // D线程-1
/*
打印结果:
...
B=>0
C=>1
A=>2
C=>3
B=>2
B=>1
B=>0
...
存在这种打印结果,为什么?
因为if判断,判断一次,会造成虚假唤醒(线程被唤醒,不会接到通、中断或超时,即虚假唤醒,所以唤醒条件防范,如果条件不满足,继续等待,等待应该总是出现在循环汇总),
解决办法:while循环判断 替换后,打印结果:1 0 交替
*/
}
}
class Goods1{
private int number = 0;
//+1 生产了一件物品
public synchronized void increment() throws InterruptedException {
while (number != 0){ //O的时候,干活,不是0的时候,等待
// 等待 窗口有一碗饭时,等会
this.wait();
}
// 业务
number ++;
System.out.println(Thread.currentThread().getName() + "=>" + number);
// 通知其他线程,我+1完毕了
this.notifyAll(); // 通知唤醒其他线程
}
//-1 消费了一件物品
public synchronized void decrement() throws InterruptedException {
while (number == 0){ //1的时候,干活,不是1的时候,等待
// 等待 窗口没有饭时,等会
this.wait();
}
// 业务
number --;
System.out.println(Thread.currentThread().getName() + "=>" + number);
// 通知其他线程,我-1完毕了
this.notifyAll(); // 通知唤醒其他线程
}
}
Sychronized : Object
Lock:JUC
实现效果:A B C D 线程精准唤醒,交替打印
package com.kuang.demo02;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* A B C D 线程通信 JUC Lock+Condition
*
* @author sdm
* @version 1.0
* @date 2023/6/24 15:23
*/
public class ProductConsumerConditionDemoABCDJUC {
public static void main(String[] args) {
// 获取资源类
Goods2 goods2 = new Goods2();
// 创建多线程,将资源类抛进去
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
goods2.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"A").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
goods2.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"B").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
goods2.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"C").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
goods2.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"D").start();
/* 打印结果:A B C D 线程无序交替打印1、0
A=>1
B=>0
A=>1
B=>0
A=>1
B=>0
A=>1
B=>0
A=>1
B=>0
A=>1
B=>0
A=>1
B=>0
A=>1
B=>0
A=>1
B=>0
A=>1
B=>0
C=>1
D=>0
C=>1
D=>0
C=>1
D=>0
C=>1
D=>0
C=>1
D=>0
C=>1
D=>0
C=>1
D=>0
C=>1
D=>0
C=>1
D=>0
C=>1
D=>0
*/
}
}
// 等待,业务,通知
class Goods2{
private int number = 0;
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
public void increment() throws InterruptedException {
lock.lock();
try {
// 判断等待
while (number != 0){
condition.await();
}
// 业务操作
number++;
System.out.println(Thread.currentThread().getName() + "=>" + number);
// 通知其他线程,我+1操作完成了
condition.signalAll();
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
public void decrement() throws InterruptedException {
lock.lock();
try {
// 判断等待
while (number == 0){
condition.await();
}
// 业务操作
number--;
System.out.println(Thread.currentThread().getName() + "=>" + number);
// 通知其他线程,我+1操作完成了
condition.signalAll();
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
}
package com.kuang.demo02;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.zip.DataFormatException;
/**
* A B C D 线程通信 JUC Lock+Condition 精准通知打印:循环执行A->B->C
*
* @author sdm
* @version 1.0
* @date 2023/6/24 15:23
*/
public class ProductConsumerConditionDemoABCDJUCOrderly {
public static void main(String[] args) {
// 获取资源类
Goods3 goods3 = new Goods3();
// 创建多线程,将资源类抛进去
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
goods3.printA();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"A").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
goods3.printB();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"B").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
goods3.printC();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"C").start();
/* 实现效果:
A=>线程A执行
B=>线程B执行
C=>线程C执行
A=>线程A执行
B=>线程B执行
C=>线程C执行
A=>线程A执行
B=>线程B执行
C=>线程C执行
A=>线程A执行
B=>线程B执行
C=>线程C执行
A=>线程A执行
B=>线程B执行
C=>线程C执行
A=>线程A执行
B=>线程B执行
C=>线程C执行
A=>线程A执行
B=>线程B执行
C=>线程C执行
A=>线程A执行
B=>线程B执行
C=>线程C执行
A=>线程A执行
B=>线程B执行
C=>线程C执行
A=>线程A执行
B=>线程B执行
C=>线程C执行
*/
}
}
// 等待,业务,通知
class Goods3{ //资源类
Lock lock = new ReentrantLock();
Condition condition1 = lock.newCondition(); // 同步监视器:一个监视器只监视一个
Condition condition2 = lock.newCondition(); // 同步监视器:一个监视器只监视一个
Condition condition3 = lock.newCondition(); // 同步监视器:一个监视器只监视一个
private int number = 1; // 1A 3B 5C
public void printA() throws InterruptedException {
lock.lock();
try {
// 业务 等待,执行,通知
while (number != 1){
condition1.await();
}
number = 2;
System.out.println(Thread.currentThread().getName() + "=>线程A执行");
// 唤醒指定的人,B
condition2.signal();
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
public void printB() throws InterruptedException {
lock.lock();
try {
// 业务 等待,执行,通知
while (number != 2){
condition2.await();
}
number = 3;
System.out.println(Thread.currentThread().getName() + "=>线程B执行");
// 唤醒指定的人,C
condition3.signal();
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
public void printC() throws InterruptedException {
lock.lock();
try {
// 业务 等待,执行,通知
while (number != 3){
condition3.await();
}
number = 1;
System.out.println(Thread.currentThread().getName() + "=>线程C执行");
// 唤醒指定的人,A
condition1.signal();
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
}
package com.kuang.lock8;
import java.util.concurrent.TimeUnit;
public class Test1 {
public static void main(String[] args) {
//Q1:无锁时,先发消息?先打电话?
//Phone1 phone1 = new Phone1();
//
//new Thread(phone1::sendMessage, "A").start();
//new Thread(phone1::call, "B").start();
/*
sendMessage
call
*/
//Q2: sychronized锁类中的方法,先发消息?先打电话?
// 有锁,锁了对象的调用者 phone2 ,所以肯定先 发短信,再打电话,
// 验证:发短信里面延迟4s,依然是先 发短信,再 打电话,因为 phone2 在4s内都被锁死了,B线程不能执行
// 两个方法用的同一把锁。谁先拿到谁先执行
//Phone2 phone2 = new Phone2();
//new Thread(phone2::sendMessage,"A").start();
延迟1s
//try {
// TimeUnit.SECONDS.sleep(1);
//} catch (InterruptedException e) {
// e.printStackTrace();
//}
//new Thread(phone2::call,"B").start();
/*
发短信
打电话
*/
//Q3:发短信有锁sychronized,hello无锁 先发消息?先hello?
//Phone2 phone2 = new Phone2();
//new Thread(phone2::sendMessage,"A").start();
//new Thread(phone2::hello,"B").start();
/*
hello
发短信
*/
//Q4:两个对象分别调用sychronized方法 哪个先发消息?
//Phone2 phone21 = new Phone2();
//Phone2 phone22 = new Phone2();
//new Thread(()->{
// for (int i = 0; i < 10; i++) {
// phone21.sendMessage();
// }
//},"A").start();
//new Thread(()->{
// for (int i = 0; i < 10; i++) {
// phone22.sendMessage();
// }
//},"B").start();
/*
...
发短信-A 没有规则顺序,因为调用的不是一个对象,所以锁没有什么作用
发短信-B
发短信-B
发短信-A
发短信-B
...
*/
//Q5:静态synchronized方法,先发消息?先打电话? 坑:线程中间需要加睡眠,否则锁没有效果
//Phone3 phone3 = new Phone3();
//new Thread(()->{phone3.sendMessage();},"A").start();
//new Thread(()->{phone3.call();},"B").start();
/*
发短信-A
打电话-B
*/
//Phone3 phone31 = new Phone3();
//Phone3 phone32 = new Phone3();
//new Thread(()->{phone31.sendMessage();},"A").start();
//new Thread(()->{phone32.sendMessage();},"B").start();
/*
发短信-A
打电话-B
*/
//new Thread(()->{
// for (int i = 0; i < 100; i++) {
// phone31.sendMessage();
// }
//},"A").start();
//new Thread(()->{
// for (int i = 0; i < 100; i++) {
// phone32.sendMessage();
// }
//},"B").start();
/*
...
发短信-A
发短信-B
发短信-A
发短信-A
...
为什么存在这种现象?既然静态锁的对象是类的模板,不应该是 发完消息,再打电话?
猜测:
会不会是执行完方法后就释放模板锁,在这个间隙,B线程获取到了模板锁,然后执行了?
原因:
参考文章:http://t.csdn.cn/K8e8e
近期在使用多线程开发时遇到一些有意思的东西—在线程run方法中是否需要当前线程睡眠一段时间。
要了解sleep方法,那么首先得了解到它的原理及其使用方法。
使用简介:
在这里以JAVA的API为例(当然其它语言亦可以),JAVA的API中是这么描述的,
public static void sleep(long millis,
int nanos)
throws InterruptedException
在指定的毫秒数加指定的纳秒数内让当前正在执行的线程休眠(暂停执行),
此操作受到系统计时器和调度程序精度和准确性的影响。
参数: millis - 以毫秒为单位的休眠时间。 nanos - 要休眠的另外 0-999999 纳秒。 抛出: IllegalArgumentException - 如果 millis 值为负或 nanos 值不在 0-999999 范围内。 InterruptedException - 如果任何线程中断了当前线程。当抛出该异常时,当前线程的中断状态 被清除 API中说的很明确,这个方法的目的就是让线程休眠,并且这个操作其实是没有释放锁的。
那么问题来了,sleep到底什么时候用呢?
我们在使用多线程的时候会发现,有时候需要程序运行时间特别长了会经常出现一些问题,或者当前我们开启了多个线程它们分别执行几个任务,但是因为执行的任务时间非常短,有时候cpu切换时候会出现一系列的问题,那么这时候可能的原因就有是否因为cpu一直在执行一个线程或者其他的原因呢。
当我们设置sleep时,等于告诉cpu,当前的线程不再运行,持有当前对象的锁。那么这个时候cpu就会切换到另外的线程了。这种操作有些时候是非常好的。
那么回归标题,究竟应不应该使用sleep呢,LZ认为还是具体业务具体分析,看是否需要添加此方法。
*/
//new Thread(()->{
// for (int i = 0; i < 100; i++) {
// phone31.sendMessage();
// }
//},"A").start();
//try {
// TimeUnit.SECONDS.sleep(5); // 这里为什么要睡一下,才能打印出预想结果?
// // 猜测:
// // A调用方法的时候,持有锁,循环下一次的间隙中,CPU会想要切换执行线程B
// // 如果不加睡眠,线程B会在这个间隙获取锁、获取CPU执行,这样就会有问题,锁失去了意义
// // 所以,在想开启线程B的时候加个睡眠,等待A下次循环调用发消息方法时唤醒A线程,且让其获取锁,这样保证A继续持有CPU,避免B线程执行
// // A执行完后,睡5秒,5秒内A没有循环调用持有锁了,就开始执行线程B,B获取锁
// // 原因:
// // 因为锁在for循环里面,所以锁会被释放,可以把锁放在for循环外面,如Test2,这样就不会有这个问题了
//} catch (InterruptedException e) {
// e.printStackTrace();
//}
//new Thread(()->{
// for (int i = 0; i < 100; i++) {
// phone32.sendMessage();
// }
//},"B").start();
//Q5:1对象分别调用static sychronized发消息, sychronized打电话,哪个先执行?
//Phone4 phone4 = new Phone4();
//new Thread(Phone4::sendMessage,"A").start();// 锁Class对象
//new Thread(phone4::call,"B").start();// 锁调用者
/*
打电话-B 锁的不是一个东西,发消息睡眠1s,就先打印出打电话了
发短信-A
*/
//Q6:2对象分别调用static sychronized发消息, sychronized打电话,哪个先执行?
Phone4 phone5 = new Phone4();
Phone4 phone6 = new Phone4();
new Thread(Phone4::sendMessage,"A").start();// 锁Class对象
new Thread(phone6::call,"B").start();// 锁调用者
/*
打电话-B 锁的不是一个东西,发消息睡眠1s,就先打印出打电话了
发短信-A
*/
}
}
/*
无锁
*/
class Phone1{
public void sendMessage(){
System.out.println("sendMessage");
}
public void call(){
System.out.println("call");
}
}
/*
sychronized锁
锁的对象是方法的调用者
*/
class Phone2{
public synchronized void sendMessage() {
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("发短信-"+Thread.currentThread().getName());
}
public synchronized void call(){
System.out.println("打电话-"+Thread.currentThread().getName());
}
public void hello(){
System.out.println("hello-"+Thread.currentThread().getName());
}
}
/*
static sychronized锁
锁的对象是类的模板
类一加载就有了Class对象
Phone3只有唯一的Class对象 Class phone3Class = Phone3.class;
sendMessage()和call()方法都被static修饰时,用的是同一个锁
*/
class Phone3{
public static synchronized void sendMessage(){
System.out.println("发短信-"+Thread.currentThread().getName());
}
public static synchronized void call(){
System.out.println("打电话-"+Thread.currentThread().getName());
}
}
/*
1个static sychronized锁
1个sychronized锁
*/
class Phone4{
// 锁的Class类模板
public static synchronized void sendMessage(){
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("发短信-"+Thread.currentThread().getName());
}
// 锁的调用者
public synchronized void call(){
System.out.println("打电话-"+Thread.currentThread().getName());
}
}
package com.kuang.lock8;
import javax.swing.plaf.synth.SynthCheckBoxMenuItemUI;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
/**
* 实现Runnable接口,重写run方法
*
* Java多线程实现-Runnable接口 http://t.csdn.cn/pJhXV
*/
class Person implements Runnable {
private String id;
private String name;
private String loginType;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getLoginType() {
return loginType;
}
public void setLoginType(String loginType) {
this.loginType = loginType;
}
@Override
public void run() {
// 类锁,锁类的Class对象(模板)
synchronized (Person.class){
for (int i = 0; i < 1000; i++) {
System.out.println(Thread.currentThread().getName()+"===========");
}
}
// for循环放在锁外面,线程之间就需要加睡眠,否则会出现CPU多次切换线程的情况
//synchronized (Person.class){
// System.out.println(Thread.currentThread().getName()+"===========");
//}
}
@Override
public boolean equals(Object obj) {
if (obj == null){
return false;
}
if (obj == this || getClass() == obj.getClass()){
return true;
}
if (obj instanceof Person){
Person person = (Person) obj;
return this.id.equals(person.getId()) && this.name.equals(person.getName());
}
return false;
}
@Override
public int hashCode() {
return Objects.hash(id,name);
}
@Override
public String toString() {
return "Person{" +
"id='" + id + '\'' +
", name='" + name + '\'' +
", loginType='" + loginType + '\'' +
'}';
}
public static void main(String[] args) {
Person personA = new Person();
Person personB = new Person();
new Thread(personA).start();
new Thread(personB).start();
//for循环放在锁外面,线程之间就需要加睡眠,否则会出现CPU多次切换线程的情况
//new Thread(()->{
// for (int i = 0; i < 1000; i++) {
// personA.run();
// }
//}).start();
//try {
// TimeUnit.SECONDS.sleep(1);
//} catch (InterruptedException e) {
// e.printStackTrace();
//}
//new Thread(()->{
// for (int i = 0; i < 1000; i++) {
// personB.run();
// }
//}).start();
}
}
List、单线程下是安全的,并发情况下不安全。
并发情况下,有些集合类会报错:并发修改异常 ConcurrentModificationException
package com.kuang.unsafe;
import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
/**
* List 单线程下是安全的,并发情况下不安全。
* 并发情况下,有些集合类会报错:并发修改异常 ConcurrentModificationException
*/
public class ListTest {
public static void main(String[] args) {
/**
* 并发下ArrayList不安全
*
* 解决方案:
* 1. Vector List list = new Vector<>();
* 2. Collections.synchronizedList List list = Collections.synchronizedList(new ArrayList<>());
* 3. CopyOnWriteArrayList() List list = new CopyOnWriteArrayList<>();
* 写入时复制,cow,计算机程序设计领域的一种优化策略
* 多个线程调用的时候,list, 读取的时候,固定的,写入(覆盖)
* 在写入的时候避免重复,造成数据问题!
* 读写分类
* CopyOnWriteArrayList比 Vector 好在哪里? vector 用的 sychronized,效率低一点,CopyOnWriteArrayList比 用了lock锁,效率搞一点
*
*/
// 单线程没有问题
//List list = new ArrayList<>();
//for (int i = 0; i < 1000; i++) {
// list.add(UUID.randomUUID().toString().substring(0,5));
// System.out.println(list);
//}
//出现并发修改异常
//List list = new ArrayList<>();
//for (int i = 0; i < 1000; i++) {
// new Thread(()->{
// list.add(UUID.randomUUID().toString().substring(0,5));
// System.out.println(list);
// }).start();
//}
// 方式1 Vector
//List list = new Vector<>();
//for (int i = 0; i < 1000; i++) {
// new Thread(()->{
// list.add(UUID.randomUUID().toString().substring(0,5));
// System.out.println(list);
// }).start();
//}
// 方式2 将list抛入Collections.synchronizedList()
//List list = Collections.synchronizedList(new ArrayList<>());
//for (int i = 1; i <= 10; i++) {
// new Thread(()->{
// list.add(UUID.randomUUID().toString().substring(0,5));
// System.out.println(list);
// }, String.valueOf(i)).start();
//}
// 方式3 new一个写入时复制list, CopyOnWriteArrayList()
List<String> list = new CopyOnWriteArrayList<>();
for (int i = 1; i <= 10; i++) {
new Thread(()->{
list.add(UUID.randomUUID().toString().substring(0,5));
System.out.println(list);
}, String.valueOf(i)).start();
}
}
}
package com.kuang.unsafe;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
/**
* Map 单线程下是安全的,并发情况下不安全。
* 并发情况下,有些集合类会报错:并发修改异常 ConcurrentModificationException
*
* 解决办法:
* 方式1 集合工具类+同步锁 Collections.synchronizedMap
* 方式2 并发哈希Map ConcurrentHashMap
*/
public class MapTest {
public static void main(String[] args) {
// 单线程下安全
//Map map = new HashMap<>();
加载因子,初始化容量 new HashMap<>(16, 0.75);
//for (int i = 0; i < 1000; i++) {
// map.put(Thread.currentThread().getName(), UUID.randomUUID().toString().substring(0,5));
// System.out.println(map);
//}
// 多线程下,发生并发修改异常
//Map map = new HashMap<>();
加载因子,初始化容量 new HashMap<>(16, 0.75);
//for (int i = 0; i < 100; i++) {
// new Thread(()->{
// map.put(Thread.currentThread().getName(), UUID.randomUUID().toString().substring(0,5));
// System.out.println(map);
// }).start();
//}
// 方式1 集合工具类+同步锁 Collections.synchronizedMap
//Map map = Collections.synchronizedMap(new HashMap<>());
加载因子,初始化容量 new HashMap<>(16, 0.75);
//for (int i = 0; i < 100; i++) {
// new Thread(()->{
// map.put(Thread.currentThread().getName(), UUID.randomUUID().toString().substring(0,5));
// System.out.println(map);
// }).start();
//}
// 方式2 并发哈希Map ConcurrentHashMap
Map map = new ConcurrentHashMap<>();
// 加载因子,初始化容量 new HashMap<>(16, 0.75);
for (int i = 0; i < 100; i++) {
new Thread(()->{
map.put(Thread.currentThread().getName(), UUID.randomUUID().toString().substring(0,5));
System.out.println(map);
}).start();
}
}
}
package com.kuang.unsafe;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.CopyOnWriteArraySet;
/**
* Set 单线程下是安全的,并发情况下不安全。
* 并发情况下,有些集合类会报错:并发修改异常 ConcurrentModificationException
*
* 解决办法:
* 方式1 工具类Collections.synchronizedSet
* 方式2 写入时复制 new CopyOnWriteArraySet<>()
*
*
* hashSet底层时什么?
* public HashSet() {
* map = new HashMap<>();
* }
* // set.add set本质是map,key无法重复!
* public boolean add(E e) {
* return map.put(e, PRESENT)==null;
* }
* // 不变的值
* private static final Object PRESENT = new Object();
*/
public class SetTest {
public static void main(String[] args) {
// 单线程下,没有问题
//HashSet