本教程内容来自「遇到狂神说」:视频地址
业务:普通的线程代码Thread
Runnable没有返回值,效率相比入Callable相对较低!
“JUC是java.util.concurrent的简写。在jdk官方手册中可以看到juc相关的jar包有三个。专门用于多线程的开发。”
线程、进程,如果不能使用一句话说出来的技术 ,不扎实!
进程: 一个程序,qq.exe Music.exe 程序的集合;
一个进程往往可以包含多个线程,至少包含一个!
java默认有几个线程?2个 main、GC
线程: 比如打开一个Typort,开启了一个自动保存,你在写的时候他会创建一个线程专门去保存数据。
java中创建线程的方法:Thread、runnable、callable
Java是否可以直接开启线程?
当然不行,java是无法直接操控硬件都。
public synchronized void start() {
if (threadStatus != 0)
throw new IllegalThreadStateException();
group.add(this);
boolean started = false;
try {
start0();
started = true;
} finally {
try {
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
}
}
}
//本地方法。底层都是c++和c,java无法直接操作硬件
private native void start0();
并发编程:并发、并行
并发(多线程操作同一个资源)
//获取CPU密集型,IO密集型
System.out.println(Runtime.getRuntime().availableProcessors());
编发编程的本质:充分利用CPU的资源
线程有几个状态
public enum State {
//新生
NEW,
//运行
RUNNABLE,
//阻塞
BLOCKED,
//等待
WAITING,
//超时等待
TIMED_WAITING,
//终止
TERMINATED;
}
wait/sleep区别
1、来自不同的类
wait=>Object
sleep=>Thread
2、关于锁的释放
wait会释放锁,sleep睡觉了,抱着锁睡觉,不会释放
3、使用范围不同的
package cn.qileyun;
/**
* 真正的多线程开发,公式的开发,降低耦合性
* 线程就是单独的资源类,没有任何附属操作!
* 1、属性、方法
*/
public class SaleTicketDemo01 {
public static void main(String[] args) {
//并发:多线程操作同一个资源类lambok表达式 (次数)->{代码}
Ticket ticket = new Ticket();
//@FunctionlInterface 函数式接口
new Thread(() -> {
for (int i = 1; i < 60; i++) {
ticket.sale();
}
}, "A").start();
new Thread(() -> {
for (int i = 1; i < 60; i++) {
ticket.sale();
}
}, "B").start();
new Thread(() -> {
for (int i = 1; i < 60; i++) {
ticket.sale();
}
}, "C").start();
}
}
//资源类 OOP
class Ticket {
//属性、方法
private int number = 50;
//卖票的方式
//synchronized 本质:队列,锁
public synchronized void sale() {
if (number > 0) {
System.out.println(Thread.currentThread().getName() + "卖出类" + number + "票,剩余:" + number--);
}
}
}
公平锁:十分公平:可以先来后到
非公平锁:十分不公平:可以插队(默认)
public class SaleTicketDemo02 {
public static void main(String[] args) {
//并发:多线程操作同一个资源类lambok表达式 (次数)->{代码}
Ticket2 ticket = new Ticket2();
//@FunctionlInterface 函数式接口
new Thread(() -> {
for (int i = 1; i < 60; i++) {
ticket.sale();
}
}, "A").start();
new Thread(() -> {
for (int i = 1; i < 60; i++) {
ticket.sale();
}
}, "B").start();
new Thread(() -> {
for (int i = 1; i < 60; i++) {
ticket.sale();
}
}, "C").start();
}
}
//Lock 三部曲
//1、 new ReentrantLock();
//2、lock 加锁
//3、unlock 释放锁
class Ticket2 {
//属性、方法
private int number = 50;
Lock lock = new ReentrantLock();
//卖票的方式
//synchronized 本质:队列,锁
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();//解锁
}
}
}
锁是什么,如何判断锁的是谁?
面试题:单列模式、排序算法、生成者和消费者、死锁
/**
* 线程之间的腾讯问题,生产者和消费者问题! 等待唤醒,通知唤醒
* 线程交替执行 A B操作同一个变量,num=0
* A num+1
* B num-1
*/
public class A {
public static void main(String[] args) {
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=>1
B=>0
A=>1
...
B=>0
A=>1
B=>0
A=>1
B=>0
A=>1
B=>0
问题存在,A、B、C、D四个线程同时执行
就需要把if改成while 去循环判断
/**
* 线程之间的腾讯问题,生产者和消费者问题! 等待唤醒,通知唤醒
* 线程交替执行 A B操作同一个变量,num=0
* A num+1
* B num-1
*/
public class A {
public static void main(String[] args) {
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;
//+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();
}
}
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.signalAll();
} catch (Exception e) {
e.printStackTrace();
} 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.signalAll();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
任何一个新的技术,绝对不是仅仅只覆盖原来的技术,优势和补充!
Condition精准的通知和唤醒线程
# 我们可以看到以下执行的顺序是随机的
A=>1
B=>0
A=>1
B=>0
C=>1
B=>0
C=>1
B=>0
C=>1
B=>0
C=>1
B=>0
C=>1
B=>0
C=>1
B=>0
C=>1
B=>0
C=>1
B=>0
C=>1
D=>0
C=>1
D=>0
A=>1
D=>0
A=>1
D=>0
A=>1
D=>0
A=>1
D=>0
A=>1
D=>0
A=>1
D=>0
A=>1
D=>0
A=>1
D=>0
代码测试:
/**
* A 执行完调用B,B执行完调用C,C执行完执行A
*/
public class C {
public static void main(String[] args) {
DataC data = new DataC();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
try {
data.printA();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "A").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
try {
data.printB();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "B").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
try {
data.printC();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "C").start();
}
}
//等待,业务,通知
/**
* A 执行完调用B,B执行完调用C,C执行完执行A
*/
public class C {
public static void main(String[] args) {
DataC data = new DataC();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
try {
data.printA();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "A").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
try {
data.printB();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "B").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
try {
data.printC();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "C").start();
}
}
//等待,业务,通知
class DataC {//数字 资源类
private int number = 1;// 通过为1 A执行 2 B执行 3 C执行
Lock lock = new ReentrantLock();
Condition condition1 = lock.newCondition();
Condition condition2 = lock.newCondition();
Condition condition3 = lock.newCondition();
public void printA() throws InterruptedException {
lock.lock();
try {
while (number != 1) {
//业务,判断->执行->通知
condition1.await();
}
System.out.println(Thread.currentThread().getName() + "=>AAAAAA" );
//唤醒指定的人 B
number=2;
condition2.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void printB() throws InterruptedException {
lock.lock();
try {
while (number != 2) {
//业务,判断->执行->通知
condition2.await();
}
System.out.println(Thread.currentThread().getName() + "=>BBBBBB" );
//唤醒C
number=3;
condition3.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void printC() throws InterruptedException {
lock.lock();
try {
while (number != 3) {
//业务,判断->执行->通知
condition3.await();
}
System.out.println(Thread.currentThread().getName() + "=>CCCCCC" );
//唤醒A
number=1;
condition1.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
//生产线:下单->支付->交易->物流
}
package cn.qileyun.lock8;
import java.util.concurrent.TimeUnit;
/**
* 7、一个静态的同步方法,1个普通方法,一个对象,先打印还是先打电话?打电话
* 8、一个静态的同步方法,1个普通方法的同步方法,两个对象,先打印还是先打电话?打电话
*/
public class Test4 {
public static void main(String[] args) {
//两个对象的Class类模版只有一个,static
Phone4 phone1 = new Phone4();
Phone4 phone2 = new Phone4();
//锁的存在
new Thread(() -> {
phone1.sendSms();
}, "A").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(() -> {
phone2.call();
}, "B").start();
}
}
//Phone3唯一的一个class对象
class Phone4{
//静态的同步方法 锁的是Class模版
public static synchronized void sendSms() {
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("发短信");
}
//普通方法 锁的是调用者
public synchronized void call() {
System.out.println("打电话");
}
}
package cn.qileyun.lock8;
import java.util.concurrent.TimeUnit;
/**
* 3、增加两一个普通方法!是先发短信还是hello? 普通方法
* 4、两个对象。两个同步方法,发短信还是打电话?
*/
public class Test2 {
public static void main(String[] args) {
//两个对象,两个调用者,两把锁互不干扰
Phone2 phone1 = new Phone2();
Phone2 phone2 = new Phone2();
//锁的存在
new Thread(() -> {
phone1.sendSms();
}, "A").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(() -> {
phone2.call();
}, "B").start();
}
}
class Phone2{
//Synchronized 锁的对象是方法的调用者!
public synchronized void sendSms() {
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");
}
}
package cn.qileyun.lock8;
import java.util.concurrent.TimeUnit;
/**
* 5、增加两个静态的同步方法,只有一个对象,先打电话还是先发短信?发短信
* 6、两个对象!增加两个静态的同步方法,先打印 发短信还是先打电话?发短信
*/
public class Test3 {
public static void main(String[] args) {
//两个对象的Class类模版只有一个,static 锁的是class
Phone3 phone1 = new Phone3();
Phone3 phone2 = new Phone3();
//锁的存在
new Thread(() -> {
phone1.sendSms();
}, "A").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(() -> {
phone2.call();
}, "B").start();
}
}
//Phone3唯一的一个class对象
class Phone3{
//Synchronized 锁的对象是方法的调用者!
//static 静态方法
//类一加载就有了!Class 模版 相当于锁的是Phone3.class
public static synchronized void sendSms() {
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("发短信");
}
public static synchronized void call() {
System.out.println("打电话");
}
}
package cn.qileyun.lock8;
import java.util.concurrent.TimeUnit;
/**
* 7、一个静态的同步方法,1个普通方法,一个对象,先打印还是先打电话?打电话
* 8、一个静态的同步方法,1个普通方法的同步方法,两个对象,先打印还是先打电话?打电话
*/
public class Test4 {
public static void main(String[] args) {
//两个对象的Class类模版只有一个,static
Phone4 phone1 = new Phone4();
Phone4 phone2 = new Phone4();
//锁的存在
new Thread(() -> {
phone1.sendSms();
}, "A").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(() -> {
phone2.call();
}, "B").start();
}
}
//Phone3唯一的一个class对象
class Phone4{
//静态的同步方法 锁的是Class模版
public static synchronized void sendSms() {
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("发短信");
}
//普通方法 锁的是调用者
public synchronized void call() {
System.out.println("打电话");
}
}
小结:
解决方法:
public static void main(String[] args) {
/** 并发下Arraylist 不安全的
* List list = new ArrayList<>();
* 1、List list = new Vector<>();
* 2、List list = Collections.synchronizedList(new ArrayList<>());
* 3、List list = new CopyOnWriteArrayList<>();
*/
//CopyOnWrite 写入时复制 COW 计算机程序设计领域的一种优化策略;
// 多个线程调用的时候,list,读取的时候固定的,写入(覆盖)
// 写入的时候避免覆盖,造成数据问题
//CopyOnWriteArrayList 比Vector 优势在哪里?
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();
}
list.forEach(System.out::println);
}
CopyOnWriteArrayList添加原理
拷贝一份数组 去修改不会去影响读的操作
public boolean add(E e) {
final ReentrantLock lock = this.lock;
//使用lock锁
lock.lock();
try {
Object[] elements = getArray();//获取数组
int len = elements.length;//获取成都
Object[] newElements = Arrays.copyOf(elements, len + 1);//数组长度+1 并且拷贝原有的数据
newElements[len] = e;//把新数据添加到最后一个下标
setArray(newElements);//设置成新的数组地址
return true;
} finally {
lock.unlock();
}
}
解决方法:
/**
* 1、Collections.synchronizedSet(new HashSet<>());
* 2、new CopyOnWriteArraySet<>();
*/
public class SetTest {
public static void main(String[] args) {
// Set set = new HashSet<>();
// Set set = Collections.synchronizedSet(new HashSet<>());
Set<String> set = new CopyOnWriteArraySet<>();
for (int i = 0; i < 30; i++) {
new Thread(()->{
set.add(UUID.randomUUID().toString().substring(0, 5));
System.out.println(set);//java.util.ConcurrentModificationException
},String.valueOf(i)).start();
}
}
}
hashSet的本质就是一个HashMap
public HashSet() {
map = new HashMap<>();
}
//add set本质就是map key是无法重复的! 相当于就是拿map的key做set的值,而且可以做到去重的效果
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
private static final Object PRESENT = new Object();//不变的值
/**
* 1、Collections.synchronizedMap(new HashMap())
* 2、new ConcurrentHashMap<>()
*/
public class MapTest {
public static void main(String[] args) {
//map 是这样用的吗? 不是,工作中不用HashMap
//默认等价是什么?map = new HashMap(16,0.75f) 初始化容量,加载因子
//Map map = new HashMap<>();//并发不安全
// Map map = Collections.synchronizedMap(new HashMap());
Map<String, String> map = new ConcurrentHashMap<>();
for (int i = 0; i <= 30; i++) {
new Thread(() -> {
map.put(Thread.currentThread().getName(), UUID.randomUUID().toString().substring(0, 5));
System.out.println(map);//java.util.ConcurrentModificationException
}, String.valueOf(i)).start();
}
}
}
public class CallableTest {
public static void main(String[] args) throws ExecutionException, InterruptedException {
//new Thread(new MyThread()).start(); 传统创建线程方法
MyThread myThread = new MyThread();
//适配类
FutureTask futureTask = new FutureTask(myThread);
new Thread(futureTask,"A").start();
new Thread(futureTask,"B").start();//结果会被缓存
//获取collable返回的结果
String o = (String) futureTask.get();//这个get可能会造成堵塞,因为他需要等待线程执行完毕,可以竟可能的放在方法最后一行执行
System.out.println(o);
}
}
/**
* Callable<需要返回的值类型>
*/
class MyThread implements Callable<String> {
@Override
public String call() {
System.out.println("call()");//结果只会打印一边
return "45678";
}
}
//计数器
public class CountDownLatchDemo {
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()+"Go out");
countDownLatch.countDown();//数量-1
},String.valueOf(i)).start();
}
countDownLatch.await();//等待计数器归零,然后在向下执行
System.out.println("Close Door");
}
}
加法计数器
public static void main(String[] args) {
/**
* 集齐七棵龙珠召唤神龙
*/
//召唤龙珠的线程
CyclicBarrier cyclicBarrier = new CyclicBarrier(7,()->{
System.out.println("召唤神龙成功!");
});
//必须执行完8条线程才能执行召唤神龙
for (int i = 0; i < 7; i++) {
//lambda能操作 到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();
}
}
结果:
Thread-2收集2个龙珠
Thread-0收集0个龙珠
Thread-1收集1个龙珠
Thread-3收集3个龙珠
Thread-6收集6个龙珠
Thread-5收集5个龙珠
Thread-4收集4个龙珠
召唤神龙成功!
Semaphore:信号量
抢车位!
6车—3个停车位置
123
456
public static void main(String[] args) {
//线程数量: 停车位!限流!
Semaphore semaphore = new Semaphore(3);
for(int i =1;i<=6;i++){
new Thread(()->{
//acquice() 得到
//release() 释放
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();
}
}
结果:
1抢到车位
3抢到车位
2抢到车位
3离开车位
1离开车位
4抢到车位
2离开车位
5抢到车位
6抢到车位
4离开车位
6离开车位
5离开车位