进程:一个程序,QQ.exe Music.exe 程序的集合;
一个进程往往可以包含多个线程,至少包含一个!
进程是cpu资源分配的最小单位!
Java默认有两个线程 main 和 GC
线程:开了一个进程 Typora,写字,自动保存(线程负责的)
线程是cpu调度的最小单位!
对于Java而言:Thread、Runnable、Callable
Java无法自己开启线程
public enum State {
NEW, //新建
RUNNABLE, //运行
BLOCKED, //阻塞
WAITING, //等待(死等)
TIMED_WAITING, //超时等待
TERMINATED; //终止
}
1、来自不同的类
wait => Object
sleep => Thread
2、关于锁的释放
wait 会释放锁
sleep不会释放!
3、是否需要被唤醒
wait需要被notify
sleep不需要
4、使用的范围
wait必须在同步代码块中
sleep可以在任何地方
/*
* 真正的多线程开发,要降低耦合性
* 每一个线程要执行的就是一个单独的资源类,没有任何附属的操作!
* */
public class SaleTicketBySyn {
public static void main(String[] args) {
Ticket1 ticket1 = new Ticket1();
//以前的写法,多线程操作同一个资源类,把资源类丢入线程即可
/* new Thread(new Runnable() {
@Override
public void run() {
ticket1.sale();
}
}).start();*/
//JDK1.8以后 使用lambada表达式 (参数)->{代码}
new Thread(() -> {
for (int i = 0; i < 50; i++)
ticket1.sale();
}, "A").start();
new Thread(() -> {
for (int i = 0; i < 50; i++) {
ticket1.sale();
}
}, "B").start();
new Thread(() -> {
for (int i = 0; i < 50; i++) {
ticket1.sale();
}
}, "C").start();
}
}
/*
* 资源类 oop 就是一个类,不要实现任何接口或者抽象方法
* */
class Ticket1 {
//属性、方法
private int number = 50;
//卖票的方式
public synchronized void sale() {
if (number > 0) {
System.out.println(Thread.currentThread().getName() + "卖出第" + (number--) + "张票" + ",还剩余" + number + "张");
}
}
}
Lock形式的锁
public class SaleTicketByLock {
public static void main(String[] args) {
Ticket2 ticket2 = new Ticket2();
new Thread(() -> {
for (int i = 0; i < 50; i++) {
ticket2.sale();
}
}, "A").start();
new Thread(() -> {
for (int i = 0; i < 50; i++) {
ticket2.sale();
}
}, "B").start();
new Thread(() -> {
for (int i = 0; i < 50; i++) {
ticket2.sale();
}
}, "C").start();
}
}
/*
1. 资源类 oop 就是一个类,不要实现任何接口或者抽象方法
2. */
class Ticket2 {
//属性、方法
private int number = 50;
//获得一把可重入锁
ReentrantLock 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();
}
}
}
public class FalseWakeBySyn {
public static void main(String[] args) {
Pro_Con res = new Pro_Con();
//开启一个负责+1操作的线程
new Thread(() -> {
for (int i = 0; i < 10; i++) {
try {
res.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "A").start();
//开启一个负责+1操作的线程
new Thread(() -> {
for (int i = 0; i < 10; i++) {
try {
res.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "B").start();
//开启一个负责-1操作的线程
new Thread(() -> {
for (int i = 0; i < 10; i++) {
try {
res.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "C").start();
//开启一个负责-1操作的线程
new Thread(() -> {
for (int i = 0; i < 10; i++) {
try {
res.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "D").start();
}
}
/*
* 一个线程+1后,等待,唤醒其他线程进行-1操作
* 一个线程-1后,等待,唤醒其他线程进行+1操作
* */
class Pro_Con {
private int number = 0;
public synchronized void increment() throws InterruptedException {
while (number != 0) {
//不等于0时,不进行操作,当前线程等待,其他线程运行
//(使用while避免虚假唤醒问题)
this.wait();
}
number++;
System.out.println(Thread.currentThread().getName() + "-->" + number);
//+1后,唤醒其他线程来-1
notifyAll();
}
public synchronized void decrement() throws InterruptedException {
while (number == 0) {
//等于0时,不进行操作,当前线程等待,其他线程运行
//(使用while避免虚假唤醒问题)
this.wait();
}
number--;
System.out.println(Thread.currentThread().getName() + "-->" + number);
//-1后,唤醒其他线程来+1
notifyAll();
}
}
虚假唤醒
当我们使用if作为判断等待的条件时
唤醒线程后,不会再判断一次if,而是直接执行之后的代码
当我们使用while作为判断等待的条件时
唤醒线程后,会再判断一次条件,再决定是否执行之后的代码
使用if时,若只有一个线程+1操作,一个线程-1操作,则无问题
若有多个线程进行相同操作,就会有线程问题
使用while作为判断即可
Condition 精准的通知和唤醒线程
package com.maki.lock;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/*
* 使用Lock的方式,替换掉synchronized中的wait,notify操作
* */
public class ProductConsumerByLock {
public static void main(String[] args) {
Product_Consumer pc = new Product_Consumer();
//需求:让三个线程轮流执行
new Thread(() -> {
for (int i = 0; i < 10; i++) {
pc.printA();
}
}, "A").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
pc.printB();
}
}, "B").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
pc.printC();
}
}, "C").start();
}
}
/*
* 需求,让三个线程轮流执行
* */
class Product_Consumer {
private int number = 1;
//Lock三部曲 获得锁,加锁,释放锁
Lock lock = new ReentrantLock(); //获得锁
Condition condition1 = lock.newCondition(); //condition对象,监视器,用来监视一个线程
Condition condition2 = lock.newCondition(); //condition对象,监视器,用来监视一个线程
Condition condition3 = lock.newCondition(); //condition对象,监视器,用来监视一个线程
public void printA() {
lock.lock();
try {
//当不是1时,A线程永久等待
while (number != 1) {
// 等待
condition1.await();
}
number = 2;
System.out.println(Thread.currentThread().getName());
// 唤醒B线程
condition2.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void printB() {
lock.lock();
try {
//当不是2时,B线程永久等待
while (number != 2) {
// 等待
condition2.await();
}
number = 3;
System.out.println(Thread.currentThread().getName());
// 唤醒C线程
condition3.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void printC() {
lock.lock();
try {
//当不是3时,C线程永久等待
while (number != 3) {
// 等待
condition3.await();
}
number = 1;
System.out.println(Thread.currentThread().getName());
// 唤醒A线程
condition1.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
考虑问题:有几把锁,锁的是谁?
1、锁的是具体的对象 new
2、锁的是唯一的模板 static
synchronized锁的对象是方法的调用者!
staitc synchronized锁的对象是唯一的class模板
1、第一种问题
/*
* 1、两个同步方法情况下,两个线程先打印 发短信?打电话?
* 2、线程A睡眠2s,两个线程先打印 发短信?打电话?
* */
public class Test1 {
public static void main(String[] args) {
//一个对象
phone1 phone1 = new phone1();
//A线程
new Thread(() -> {
phone1.sendSms();
}, "A").start();
//线程睡眠1s
try {
TimeUnit.SECONDS.sleep(1);
} catch (Exception e) {
e.printStackTrace();
}
//B线程
new Thread(() -> {
phone1.call();
}, "B").start();
}
}
class phone1 {
public synchronized void sendSms() {
//调用该方法的线程先睡眠2s
try {
TimeUnit.SECONDS.sleep(2);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "--->发短信!");
}
public synchronized void call() {
System.out.println(Thread.currentThread().getName() + "--->打电话!");
}
}
永远都是先执行发短信,再执行打电话!
该问题中,有一把锁,锁的是phone1具体的对象
A线程先拿到锁(操作phone1对象),即便睡眠了2s,在没有释放锁之前,B线程无法操作phone1对象(在同步的情况下)
2、第二种问题
/*
* 一个同步,一个普通方法情况下,先执行 发短信?打电话?
* */
public class Test2 {
public static void main(String[] args) {
//一个对象
phone2 phone2 = new phone2();
//A线程
new Thread(() -> {
phone2.sendSms();
}, "A").start();
//线程睡眠1s
try {
TimeUnit.SECONDS.sleep(1);
} catch (Exception e) {
e.printStackTrace();
}
//B线程
new Thread(() -> {
phone2.call();
}, "B").start();
}
}
class phone2 {
//同步的
public synchronized void sendSms() {
//调用该方法的线程先睡眠2s
try {
TimeUnit.SECONDS.sleep(2);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "--->发短信!");
}
//非同步的
public void call() {
System.out.println(Thread.currentThread().getName() + "--->打电话!");
}
}
永远都是先打电话,再发短信!
该问题中,有一把锁,锁的是phone2对象
虽然A线程先拿到锁,但是B线程要执行的方法是非同步的,不用判断锁的存在,可直接执行!
3、第三种问题
/*
* 两个对象,两个同步方法;先执行发短信?打电话?
* */
public class Test3 {
public static void main(String[] args) {
//两个对象
phone3 phone31 = new phone3();
phone3 phone32 = new phone3();
//A线程
new Thread(() -> {
phone31.sendSms();
}, "A").start();
//线程睡眠1s
try {
TimeUnit.SECONDS.sleep(1);
} catch (Exception e) {
e.printStackTrace();
}
//B线程
new Thread(() -> {
phone32.call();
}, "B").start();
}
}
class phone3 {
public synchronized void sendSms() {
//调用该方法的线程先睡眠2s
try {
TimeUnit.SECONDS.sleep(2);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "--->发短信!");
}
public synchronized void call() {
System.out.println(Thread.currentThread().getName() + "--->打电话!");
}
}
永远先执行打电话,再执行发短信!
该问题中,有两个锁,分别锁的是phone31,phone32对象
A线程拿到锁(phone31的)后睡眠,但是B线程拿的是(phone32的锁),操作的不是phone31的对象;所以B线程在A线程睡眠时,先执行!
4、第四种问题
/*
* 两个静态同步方法,只有一个对象;先执行 发短信?打电话?
* */
public class Test4 {
public static void main(String[] args) {
//一个对象
phone4 phone4 = new phone4();
//A线程
new Thread(() -> {
phone4.sendSms();
}, "A").start();
//线程睡眠1s
try {
TimeUnit.SECONDS.sleep(1);
} catch (Exception e) {
e.printStackTrace();
}
//B线程
new Thread(() -> {
phone4.call();
}, "B").start();
}
}
class phone4 {
//静态同步方法
public static synchronized void sendSms() {
//调用该方法的线程先睡眠2s
try {
TimeUnit.SECONDS.sleep(2);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "--->发短信!");
}
//静态同步方法
public static synchronized void call() {
System.out.println(Thread.currentThread().getName() + "--->打电话!");
}
}
永远先执行发短信,再执行打电话!
该问题中,有一把锁,锁的是class模板
A线程先拿到模板的锁,即便睡眠,在不释放锁之前,B线程无法操作这个class模板(同步的前提下)
5、第五种问题
/*
* 两个对象,两个静态同步方法;先执行发短信?打电话?
* */
public class Test5 {
public static void main(String[] args) {
//两个对象
phone5 phone51 = new phone5();
phone5 phone52 = new phone5();
//A线程
new Thread(() -> {
phone51.sendSms();
}, "A").start();
//线程睡眠1s
try {
TimeUnit.SECONDS.sleep(1);
} catch (Exception e) {
e.printStackTrace();
}
//B线程
new Thread(() -> {
phone52.call();
}, "B").start();
}
}
class phone5 {
//静态同步方法
public static synchronized void sendSms() {
//调用该方法的线程先睡眠2s
try {
TimeUnit.SECONDS.sleep(2);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "--->发短信!");
}
//静态同步方法
public static synchronized void call() {
System.out.println(Thread.currentThread().getName() + "--->打电话!");
}
}
永远先执行发短信,再执行打电话!
该问题中,还是一把锁,锁的是唯一的class模板
sendSms和call方法,他们都是静态的,存在于class模板中;而不是具体的对象中
即便A线程使用了phone51对象,但是操作的方法在class模板中,是class锁,在不释放锁的前提下,B线程无法操作class模板(同步的前提下)
6、第六种问题
/*
* 1个静态的同步方法,一个普通的同步方法,一个对象;先打印 发短信?打电话?
* */
public class Test6 {
public static void main(String[] args) {
//一个对象
phone6 phone6 = new phone6();
//A线程
new Thread(() -> {
phone6.sendSms();
}, "A").start();
//线程睡眠1s
try {
TimeUnit.SECONDS.sleep(1);
} catch (Exception e) {
e.printStackTrace();
}
//B线程
new Thread(() -> {
phone6.call();
}, "B").start();
}
}
class phone6 {
//静态同步方法
public static synchronized void sendSms() {
//调用该方法的线程先睡眠2s
try {
TimeUnit.SECONDS.sleep(2);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "--->发短信!");
}
//普通同步方法
public synchronized void call() {
System.out.println(Thread.currentThread().getName() + "--->打电话!");
}
}
永远先执行打电话,再执行发短信!
该问题中,有两把锁;锁的是唯一的class模板、phone6对象
A线程拿到class模板的锁,睡眠后;B线程拿到phone6对象的锁,执行call方法,它们是两把锁
7、第七种问题
/*
* 1个静态的同步方法,一个普通的同步方法,两个对象;先打印 发短信?打电话?
* */
public class Test7 {
public static void main(String[] args) {
//两个对象
phone7 phone71 = new phone7();
phone7 phone72 = new phone7();
//A线程
new Thread(() -> {
phone71.sendSms();
}, "A").start();
//线程睡眠1s
try {
TimeUnit.SECONDS.sleep(1);
} catch (Exception e) {
e.printStackTrace();
}
//B线程
new Thread(() -> {
phone72.call();
}, "B").start();
}
}
class phone7 {
//静态同步方法
public static synchronized void sendSms() {
//调用该方法的线程先睡眠2s
try {
TimeUnit.SECONDS.sleep(2);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "--->发短信!");
}
//普通同步方法
public synchronized void call() {
System.out.println(Thread.currentThread().getName() + "--->打电话!");
}
}
永远先执行打电话,再执行发短信!
该问题中,有两把锁;锁的是唯一的class模板、phone72对象
A线程拿到class模板的锁,睡眠后;B线程拿到phone72对象的锁,执行call方法,它们是两把锁
我们通常使用的集合类都是非同步的,除了vector
当多个线程,向同一个集合写入数据时
ConcurrentModificationException 并发修改异常!
public static void main(String[] args) {
//我们通常使用的List集合都是非同步的 除了vector
List<String> list=new ArrayList<>();
/*
* 解决方案
* 1、vector集合(效率低)
* 2、Collections.synchronized(list) (使用集合工具类,将非同步的集合变为同步的)
* 3、CopyOnWrite 写入时复制
* 当多个线程同时向list中写入数据时,存在写入覆盖问题
* 在写入的时候复制,避免另一个线程将某一线程未写完的数据覆盖
* */
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();
}
}
与上同理,CopyOnWriteArraySet
HashSet底层是什么?
public HashSet() {
map = new HashMap<>();
}
private static final Object PRESENT = new Object(); // 不变值!
// add set 本质就是 map key是无法重复的!
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
回顾Map基本操作
// ConcurrentModificationException
public static void main(String[] args) {
// map 是这样用的吗? 不是
// 默认等价于什么?
new HashMap<>(16,0.75);
//并发下的map
Map<String, String> map = new ConcurrentHashMap<>();
for (int i = 1; i <=30; i++) {
new Thread(()->{
map.put(Thread.currentThread().getName(),
UUID.randomUUID().toString().substring( 0,5));
System.out.println(map);
},String.valueOf(i)).start();
}
实际上,和Runnable的功能一样,区别
但是,开启线程的方式,只有new Thread(Runnable接口).start !
所以,我们需要找到一个类,该类与Runnable、Callable都有关系!
FutureTask类:该类是Runnable接口的实现类,可以接收Callable对象
public static void main(String[] args) throws ExecutionException, InterruptedException {
/*
* 开启线程,只能用Runnable接口才行
* FutureTask是Runnable的实现类,并且可以接收一个Callable对象
* */
MyThread myThread = new MyThread();
FutureTask futureTask = new FutureTask(myThread);
new Thread(futureTask).start();
String str = (String) futureTask.get();
System.out.println(str);
}
}
class MyThread implements Callable<String> {
@Override
public String call() throws Exception {
System.out.println("call方法执行了!");
return "call()";
}
减法计数器,当线程数量不减为0时,不往后执行!
/*
* 线程的计数器!
* 当线程数量不减为0时,不向下继续执行!
*
* 注意:匿名内部类要使用的变量,作用域为final
* */
public class CountDownLatchTest {
public static void main(String[] args) throws InterruptedException {
//总数是7,必须在线程执行后,让该数-1
CountDownLatch countDownLatch = new CountDownLatch(7);
for (int i = 1; i <= 7; i++) {
//lambada表达式,实际上就是一个匿名内部类,无法直接使用i变量
//需要final域的变量
final int temp=i;
new Thread(()->{
System.out.println(Thread.currentThread().getName()+"--->Go out");
//告诉CountDownLatch,线程数量-1
countDownLatch.countDown();
},String.valueOf(i)).start();
}
//进行等待操作,当线程数量不减为0,永久等待,之后的代码不执行!
countDownLatch.await();
System.out.println("线程数量为0了!!!");
}
}
加法计数器,当线程数量达到某个值后,开启一个新线程,执行一段代码
/*
* 加法计数器
* 当线程的数量达到某个值时,开启一个新线程,执行一段代码
* */
public class CyclicBarrierTest {
public static void main(String[] args) {
//加法计数器,线程数量达到7时,执行一段新的线程
CyclicBarrier cyclicBarrier = new CyclicBarrier(7, () -> {
System.out.println("召唤神龙成功!");
});
for (int i = 1; i <= 7; i++) {
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + "--->个龙珠");
// 每开启一个线程,cyclicBarrier对象中的值就会自动-1
// 减为0时,表示开启了这么多个线程,并去执行一段新的线程!
try {
cyclicBarrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}, String.valueOf(i)).start();
}
}
}
注意:匿名内部类要使用的变量,必须被声明为final
信号量机制,为了让多个共享资源,进行互斥的使用!
可以做限流功能,控制最大的线程数!
/*
* 信号量,让多个共享资源,进行互斥的使用
* */
public class SemaphoreTest {
public static void main(String[] args) {
//表示有3个信号量
//每个线程开启时,拿走一个信号量
//每个线程关闭后,释放一个信号量
Semaphore semaphore = new Semaphore(3);
for (int i = 1; i <= 6; i++) {
new Thread(()->{
try {
//开启一个线程后,拿走一个信号量 -1
semaphore.acquire();
System.out.println(Thread.currentThread().getName()+"--->线程开启了!");
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
//线程运行结束后,释放一个信号量 +1
semaphore.release();
System.out.println("--------------------------------------");
System.out.println(Thread.currentThread().getName()+"--->线程结束了!");
}
},String.valueOf(i)).start();
}
}
}
结果:在同一时刻,只有三个线程能进行操作!
也叫独占锁,共享锁
即,可以有多个线程读,但是只允许一个线程写!
public static void main(String[] args) {
MyThread myThread = new MyThread();
//开启写线程
for (int i = 1; i <= 5; i++) {
final String temp = String.valueOf(i);
//注意:匿名内部类,只能使用final作用域的变量
new Thread(() -> {
myThread.put(temp, temp);
}, String.valueOf(i)).start();
}
//开启读线程
for (int i = 1; i <= 5; i++) {
final String temp=i+"";
new Thread(()->{
myThread.get(temp);
},String.valueOf(i)).start();
}
}
}
class MyThread {
private volatile Map<String, Object> map = new HashMap<>();
//获得一把读写锁
private ReadWriteLock readWriteLock=new ReentrantReadWriteLock();
//写操作,只允许一个线程写
public void put(String key, Object value) {
//写操作,使用独占锁
readWriteLock.writeLock().lock();
try {
System.out.println(Thread.currentThread().getName() + "开始写入...");
map.put(key, value);
System.out.println(Thread.currentThread().getName() + "写入完成!");
} catch (Exception e) {
e.printStackTrace();
} finally {
readWriteLock.writeLock().unlock();
}
}
//读操作,可以有多个线程读
public void get(String key) {
//读操作,使用共享锁
readWriteLock.readLock().lock();
try {
System.out.println(Thread.currentThread().getName() + "开始读取...");
Object value = map.get(key);
System.out.println(Thread.currentThread().getName() + "读取完成!值为:" + value);
} catch (Exception e) {
e.printStackTrace();
} finally {
readWriteLock.readLock().unlock();
}
}
BlockingQueue阻塞队列
Queue队列
它并不是什么新东西
什么情况下我们会使用 阻塞队列:多线程并发处理,线程池!
/*
* 抛出异常,有返回值
* */
@Test
public void testAddRemove() {
//添加成功,返回true
System.out.println(blockingQueue.add('a'));
//添加失败,抛出IllegalStateException异常
System.out.println(blockingQueue.add('b'));
//取出成功,返回元素
System.out.println(blockingQueue.remove());
//取出失败,抛出NoSuchElementException异常
System.out.println(blockingQueue.remove());
}
/*
* 无异常,有返回值
* */
@Test
public void testOfferPoll() {
System.out.println(blockingQueue.offer('a'));
//添加失败,返回false
System.out.println(blockingQueue.offer('b'));
System.out.println(blockingQueue.poll());
//取出失败,返回null
System.out.println(blockingQueue.poll());
}
/*
* 永久阻塞,无返回值,无异常
* */
@Test
public void testPutTake() throws InterruptedException {
blockingQueue.put('a');
//添加失败,永久等待
blockingQueue.put('b');
System.out.println(blockingQueue.take());
//获取失败,永久等待
System.out.println(blockingQueue.take());
}
/*
* 超时等待,无异常,有返回值
* */
@Test
public void testOfferPollWait() throws InterruptedException {
System.out.println(blockingQueue.offer('a', 2, TimeUnit.SECONDS));
//添加失败,等待2s后,返回false
System.out.println(blockingQueue.offer('a', 2, TimeUnit.SECONDS));
System.out.println(blockingQueue.poll(2, TimeUnit.SECONDS));
//获取失败,等待2s后,返回null
System.out.println(blockingQueue.poll(2, TimeUnit.SECONDS));
}
线程池:3大方法、7大参数、4种拒绝策略
线程池的好处:线程复用、可以控制最大并发数、管理线程
Executors工具类,类似于Collections,Arrays;用来创建线程池的!
public static void main(String[] args) {
//1、创建线程池
//线程池中只有一个线程!
ExecutorService pool1 = Executors.newSingleThreadExecutor();
//2、使用线程池来开启线程
for (int i = 1; i < 5; i++) {
//executor开启线程池中的一个线程,并执行runnable中的run方法
pool1.execute(()->{
System.out.println(Thread.currentThread().getName()+" ok ");
});
}
//3、程序运行结束,关闭线程池
pool1.shutdown();
}
public static void main(String[] args) {
//1、创建线程池
//创建一个固定的线程池大小
ExecutorService pool2 = Executors.newFixedThreadPool(5);
//2、使用线程池来开启线程
for (int i = 1; i < 5; i++) {
//executor开启线程池中的一个线程,并执行runnable中的run方法
pool2.execute(()->{
System.out.println(Thread.currentThread().getName()+" ok ");
});
}
//3、程序运行结束,关闭线程池
pool2.shutdown();
}
public static void main(String[] args) {
//1、创建线程池
//创建一个可伸缩大小的线程池
ExecutorService pool3 = Executors.newCachedThreadPool();
//2、使用线程池来开启线程
for (int i = 1; i < 5; i++) {
//executor开启线程池中的一个线程,并执行runnable中的run方法
pool3.execute(()->{
System.out.println(Thread.currentThread().getName()+" ok ");
});
}
//3、程序运行结束,关闭线程池
pool3.shutdown();
}
注意:线程池用完,程序结束,记得关闭线程池!shutdown();
我们使用Executors工具类创建线程池时发现
不论创建哪一种线程池,都使用了ThreadPoolExecutor对象来创建线程!
//Executors.newSingleThreadExecutor
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor
(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
//Executors.newFixedThreadPool
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, //该值为21亿!
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
ThreadPoolExecutor对象有7大参数!
核心线程池大小;线程池一被创建,池中就有X个核心线程可以直接运行,不会被释放!
for (int i = 1; i < 2; i++) {
threadPool.execute(()->{
System.out.println(Thread.currentThread().getName()+"--->ok");
});
}
pool-1-thread-1--->ok
pool-1-thread-2--->ok
最大线程池大小;线程池中可以并发执行的全部线程数量;
当要开启一个新线程时,会先进入阻塞队列中;先由核心线程帮你完成功能
当阻塞队列满了,才会再开启一个线程!
for (int i = 1; i <=6; i++) {
threadPool.execute(()->{
System.out.println(Thread.currentThread().getName()+"--->ok");
});
}
pool-1-thread-1--->ok
pool-1-thread-1--->ok
//i=3时,想要开启一个新线程,先进入了阻塞队列;功能由核心线程帮你完成
pool-1-thread-1--->ok
pool-1-thread-1--->ok
pool-1-thread-2--->ok
//i=6时,阻塞队列满了,开启一个新线程
pool-1-thread-3--->ok
超时了没有人调用,就会释放,只保留核心线程!
超时单位
阻塞队列(阻塞队列也有四组API)!
除了核心线程外,当你想要再开启一个新的线程时,会先进入阻塞队列!
线程工厂:创建线程的,默认的即可!
Executors.defaultThreadFactory();
拒绝策略(4种拒绝策略)!
当达到了最大线程池数,阻塞队列也满了之后
再要开启线程时,使用拒绝策略!
ThreadPoolExecutor threadPool = new ThreadPoolExecutor(
2, //核心线程池大小
5, //最大线程池大小
2, //超时了没有人调用,就会释放
TimeUnit.SECONDS, //超时单位
new LinkedBlockingDeque<>(3), //阻塞队列
Executors.defaultThreadFactory(), //线程工厂:用来创建线程,默认即可
new ThreadPoolExecutor.AbortPolicy() //拒绝策略
);
最大线程池大小,表示线程池中的最大线程数,即你可以开启多少个线程,并发执行的最大线程数!
当你要开启一段线程,执行一段业务时,在阻塞队列未满之前,优先使用核心线程来处理业务!
当阻塞队列满了,才会再次开启一个新的线程,但是该线程在指定的时间内,没有被调用,会自动释放!
开启的线程数达到了最大,阻塞队列也满了后,再要开启一段线程执行业务时,使用拒绝策略!
当我们使用2.8中的方式:
2个核心线程,最大5个线程,阻塞队列为3时,再开启第9个线程后
抛出异常!
谁开启的这个线程,谁来执行!(例如:main线程)
丢掉任务,不会抛出异常!
尝试去和最早的竞争,也不会抛出异常!
CPU为几核,就将最大线程数设置为几,这样这些线程之间可以并行执行!保持CPU的效率最高!
//获取当前的CPU核数
System.out.println(Runtime.getRuntime().availableProcessors());
将最大线程数设置为:大于程序中十分耗IO的线程数
因为IO操作的效率很低,使用多线程来操作可以提高效率!将这些线程都存放在线程池中,使用线程池来开启多线程,执行IO操作!
只有一个方法的接口,例如Runnable接口
@FunctionalInterface
public interface Runnable {
public abstract void run();
}
任何的函数式接口,都可以使用lambada表达式 ( ) -> { }
( ) 中为方法的形参,他的数据类型默认为Object,若定义了泛型,则为泛型中的数据类型!
{ } 中写方法体的内容
接收参数1,返回参数2!
public static void main(String[] args) {
//接收参数1,返回参数2
/* Function<String, Integer> function = new Function<String, Integer>() {
@Override
public Integer apply(String str) {
return 1024;
}
};
System.out.println(function.apply("接收参数1,返回参数2"));*/
//lambada表达式写法!
Function<String, Integer> function = (str) -> {
return 1024;
};
System.out.println(function.apply("接收参数1,返回参数2"));
}
接收参数,返回boolean
public static void main(String[] args) {
/* Predicate<String> predicate=new Predicate<String>() {
@Override
public boolean test(String str) {
return str.isEmpty();
}
};
System.out.println(predicate.test("断定型接口,接收参数,返回boolean"));*/
//lambada表达式写法!
Predicate<String> predicate = (str) -> {
return str.isEmpty();
};
System.out.println(predicate.test("断定型接口的lambada表达式"));
}
接收一个参数,无返回值
public static void main(String[] args) {
/* Consumer<Integer> consumer=new Consumer<Integer>() {
@Override
public void accept(Integer integer) {
System.out.println("integer = " + integer);
}
};
consumer.accept(5);*/
//lambada表达式写法
Consumer<String> consumer = (str) -> {
System.out.println("str = " + str);
};
consumer.accept("消费型接口");
}
public static void main(String[] args) {
/* Supplier<String> supplier=new Supplier<String>() {
@Override
public String get() {
return "Supplier接口";
}
};
System.out.println(supplier.get());*/
//lambada表达式
Supplier<String> supplier = () -> {
return "Supplier接口";
};
System.out.println(supplier.get());
}
其实就是用来筛选数据的;所有关于计算的操作,应该交给流来操作
题目要求:一分钟内完成此题,只能用一行代码实现!
现在有5个用户!筛选:
User u1 = new User(1, "a", 28);
User u2 = new User(2, "b", 13);
User u3 = new User(3, "c", 23);
User u4 = new User(4, "d", 12);
User u5 = new User(5, "e", 41);
//1、先将数据存储到集合中
List<User> users = Arrays.asList(u1, u2, u3, u4, u5);
//2、计算交给Stream流(lambada表达式,链式编程,函数式接口)
//2.1 将集合转为一个Stream对象;该方法是collection接口中的方法;
// 集合上添加了泛型,这样Stream流对象就知道操作的是什么数据了
users.stream()
//2.2 filter方法进行过滤id为偶数的用户
//它接收一个Predicate断定式接口,接收一个参数,返回boolean
.filter((u)->{return u.getId()%2==0;})
//2.3 过滤年龄大于10的用户
.filter((u)->{return u.getAge()>10;})
//2.4 用户名转为大写
//它接收一个Function函数式接口,接收参数1,返回 参数2
//这里的参数1(u)是集合中的每一个User对象,参数2返回的是一个String
.map((u)->{return u.getName().toUpperCase();})
//2.5 倒序排序
//它接收一个Comparator接口,该接口也是函数式接口
//接收两个参数,返回int
.sorted((str1,str2)->{return str2.compareTo(str1);})
//2.6 只输出1个用户
.limit(1)
.forEach(System.out::println);
}
因篇幅问题不能全部显示,请点此查看更多更全内容