并发和并行的区别:
并发:单核CPU同时处理多件事情,
并行:同一时间,多核CPU处理多个事情。
1. 多线程的四种创建方式- 继承Thread类实现Runable接口实现Callable接口线程池创建
线程池的好处(线程复用,控制最大并发数,管理线程)
- 降低资源的消耗降低响应速度方便管理
Runnable 和Callable 的区别 runnable run方法无返回值,callable call方法有返回值 runnable run方法只能抛出运行时异常,callable call方法能够捕获异常信息
// 实现方式一: 继承Thread类
public class MythreadDemo{
public static void main(String[] args) {
new Mythread().start();
}
}
class Mythread extends Thread{
@Override
public void run() {
System.out.println("mythread runu");
}
}
//实现方式二:继承Runnable接口
public class MyRunnableDemo {
public static void main(String[] args) {
new Thread(new MyRunnable()).start();
}
}
class MyRunnable implements Runnable{
@Override
public void run() {
System.out.println("myruunnable run");
}
}
//实现方式三:实现Callable接口
public class MyCallableDemo {
public static void main(String[] args) throws ExecutionException, InterruptedException {
FutureTask future = new FutureTask<>(new MyCallable());
new Thread(future,"A").start();
new Thread(future,"B").start();
System.out.println(future.get());
}
}
class MyCallable implements Callable{
@Override
public String call() throws Exception {
System.out.println("MyCallableDemo run"+Thread.currentThread().getName());
return "wdddong";
}
}
1.1 线程池的五种创建方式
- newFixThreadPool //创建单个线程newCachedThreadPool //创建一个可伸缩的线程池newScheduledThreadPool //newSingleThreadExecutor //创建一个固定的线程池大小newThreadPoolExecuted(自定义线程池)
为什么阿里巴巴开发手册中强制不建议使用Executors创建线程池,使用Executors创建的线程池有什么弊端?
public class Demo {
public static void main(String[] args) {
Executors.newCachedThreadPool();
Executors.newFixedThreadPool(1);
Executors.newSingleThreadExecutor();
Executors.newScheduledThreadPool(1);
}
}
//1. newCachedThreadPool
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue());
}
//2. newScheduledThreadPool
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue());
}
//3. newFixedThreadPool
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new linkedBlockingQueue());
}
public linkedBlockingQueue() {
this(Integer.MAX_VALUE);
}
//4. newSingleThreadExecutor
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new linkedBlockingQueue()));
}
- newCachedThreadPool,newScheduledThreadPool 允许的线程(非核心线程)数量为 Integer.MAX_VALUE,会创建大量的线程,可能会导致OOMnewFixedThreadPool,newSingleThreadExecutor 允许创建的请求队列长度为 Integer.MAX_VALUE,会堆积大量的请求,可能会导致OOM
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue workQueue,
RejectedExecutionHandler handler);
#参数说明
corePoolSize:核心线程数量
maximumPoolSize:最大线程数
keepAliveTime:存活时间,(除了核心线程之外,其他的线程超时了没有人调用就会释放的时间)
unit:存活单位
workQueue:阻塞队列
handler:拒绝策略
## 拒绝策略:默认是直接抛出异常
1. AbortPolicy:抛出异常
2. CallRunsPolicy: 只用调用者所在的线程处理任务
3. DiscurdOldestPlicy:队列满了,丢弃队列中最近的一个任务,来执行最新的任务(尝试和最早的任务做一个竞争)
4. DisCardPolicy:队列满了,不处理直接丢弃
1.2 自定义线程池中最大的线程数量应该如何设置
- IO密集型: 判断你程序中十分耗IO 的数量(假设你的程序中有15 个大型任务,IO十分占用资源 。设置最大线程数 > 15 就好)CPU 密集型: 查看自己的CPU 是几核的。可以保持CPU效率最高 (程序中获取线程数: Runtime.getRuntime().availableProcessores() )
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
int c = ctl.get();
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get();
}
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
if (! isRunning(recheck) && remove(command))
reject(command);
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
else if (!addWorker(command, false))
reject(command);
}
1.3.1 任务添加顺序
1.3.2 任务执行顺序
先执行线程(核心+最大线程)中的任务,最后再执行任务队列中的任务
2. 线程中的状态(6种状态) 2.1 线程的六种状态新生 (NEW )运行(RUNNABLE)阻塞(BLOCKED)超时等待(TIMED_WAITING)等待(WAITTING)销毁(TERMINATED ) 2.2 线程状态分析
3. 线程中的常用方法| 方法 | 备注 |
|---|---|
| setPriority() | 设置线程的优先级。0==》10 ,优先级越大,被执行的可能性越高 |
| sleep() | 让当前线程休眠,每个线程都有一把锁,sleep不会释放锁 |
| join() | 等待该线程停止 |
| yiled() | 释放CPU时间片,让线程进入就绪状态,重新争夺CPU时间片 |
| interupt() | 中断线程 |
| deamon() | 守护线程:(虚拟机是不用等待守护线程结束的) |
- wait 是Object 类的方法, sleep 是线程类 Thread 的静态方法wait 不需要捕获异常 ,sleep 需要捕获异常wait 会释放锁,sleep不会释放锁。wait方法需要notify,notifyAll唤醒notify 方法是随机唤醒一个线程。wait 的调用位置只能在同步代码块中,sleep 可以在任何地方进行调用
接收的参数不一样sumbit() 有返回值,而execute 没有submit方便exception处理 4. 多线程中的锁 4.1 Synchronized锁
Synchronize 锁对象的分析:
静态方法: 静态方法 synchronize 锁的是 Class实例方法: synchronize 锁的是 方法的调用对象代码块:可以任意的去指定是锁Class模板还是具体对象 4.2 Lock锁
private Lock lock = new ReentrantLock(); lock.lock(); lock.unlock();4.3 Synchronized 和 Lock 的区别
- Synchronized 是内置的 java关键字,Lock是一个Java类Synchronized 无法判断获取锁的状态,Lock可以判断是否获取到了锁Synchronized 可以自动释放锁,Lock需要手动释放(不释放会造成死锁)
奶箱类
public class Box {
//定义一个成员变量,表示第x瓶奶
private int milk;
//定义一个成员变量,表示奶箱的形态
private boolean state = false;
//提供存储牛奶和获取牛奶的操作
public synchronized void put(int milk) {
//如果有牛奶,等待消费
if(state){
try{
wait();//等待
}catch (InterruptedException e){
e.printStackTrace();
}
}
//如果没有牛奶,就生产牛奶
this.milk = milk;
System.out.println("送奶工将第"+this.milk+"瓶奶放入奶箱");
//生产完毕之后,修改奶箱状态
state = true;
//唤醒其他等待的线程
notifyAll();
}
public synchronized void get(){
//如果没有牛奶,等待生产
if(!state){
try{
wait();
}catch (InterruptedException e){
e.printStackTrace();
}
}
//如果有牛奶,就消费牛奶
System.out.println("用户拿到第"+this.milk+"瓶奶");
//消费完牛奶后,修改奶箱状态
state = false;
//唤醒其他等待的线程
notifyAll();
}
}
//生产者
public class Producer implements Runnable {
private Box b;
public Producer(Box b) {
this.b = b;
}
@Override
public void run() {
for(int i=1;i<=5;i++){
b.put(i);//送奶人往奶箱放入奶
}
}
}
//消费者
public class Customer implements Runnable {
private Box b;
public Customer(Box b) {
this.b = b;
}
@Override
public void run() {
while (true){
b.get();//得到牛奶
}
}
}
public class BaS {
public static void main(String[] args) {
Box box = new Box();//奶箱类
Producer p = new Producer(box);//生产者对象
Customer c = new Customer(box);//消费者对象
//创建2个线程对象,分别把生产者对象和消费者对象作为构造方法参数传递
Thread t1 = new Thread(p);
Thread t2 = new Thread(c);
//启动线程
t1.start();
t2.start();
}
}
5.2 线程的虚假唤醒问题
package com.example.testmodule;
public class test {
public static void main(String[] args) {
Box box = new Box();
Thread thread1 = new Thread(() -> {
for (int i = 0; i < 50; i++) {
try {
box.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "A");
Thread thread2 = new Thread(() -> {
for (int i = 0; i < 50; i++) {
try {
box.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "B");
Thread thread3 = new Thread(() -> {
for (int i = 0; i < 50; i++) {
try {
box.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "C");
Thread thread4 = new Thread(() -> {
for (int i = 0; i < 50; i++) {
try {
box.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "D");
thread1.start();
thread2.start();
thread3.start();
thread4.start();
}
}
class Box {
public int num;
public synchronized void increment() throws InterruptedException {
if (num != 0) {
// while (num != 0){
this.wait();
}
System.out.println("put:" + num++ + " have:" + num);
notifyAll();
}
public synchronized void decrement() throws InterruptedException {
if (num == 0) {
//while (num == 0){
this.wait();
}
System.out.println("get:" + num-- + " have:" + num);
notifyAll();
}
}
# 模拟的结果
put:0 have:1
get:1 have:0
put:0 have:1
get:1 have:0
get:0 have:-1
get:-1 have:-2
get:-2 have:-3
get:-3 have:-4
get:-4 have:-5
get:-5 have:-6 .......
5.3 Lock 版生产者消费者问题
package com.wddongtt.wddong.manyThread;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Box {
//定义一个成员变量,表示第x瓶奶
private int milk;
//定义一个成员变量,表示奶箱的形态
private boolean state = false;
private Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
//提供存储牛奶和获取牛奶的操作
public void put(int milk) {
try {
lock.lock();
//如果有牛奶,等待消费
while(state){
try{
condition.await();//等待
}catch (InterruptedException e){
e.printStackTrace();
}
}
//如果没有牛奶,就生产牛奶
this.milk = milk;
System.out.println("送奶工将第"+this.milk+"瓶奶放入奶箱");
//生产完毕之后,修改奶箱状态
state = true;
//唤醒其他等待的线程
condition.signalAll();
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
public void get(){
try {
lock.lock();
//如果没有牛奶,等待生产
while(!state){
try{
condition.await();
}catch (InterruptedException e){
e.printStackTrace();
}
}
//如果有牛奶,就消费牛奶
System.out.println("用户拿到第"+this.milk+"瓶奶");
//消费完牛奶后,修改奶箱状态
state = false;
//唤醒其他等待的线程
condition.signalAll();
}catch (Exception e){
}finally {
lock.unlock();
}
}
}
//生产者
class Producer implements Runnable {
private Box b;
public Producer(Box b) {
this.b = b;
}
@Override
public void run() {
for(int i=1;i<=5;i++){
b.put(i);//送奶人往奶箱放入奶
}
}
}
//消费者
class Customer implements Runnable {
private Box b;
public Customer(Box b) {
this.b = b;
}
@Override
public void run() {
while (true){
b.get();//得到牛奶
}
}
}
class BaS {
public static void main(String[] args) {
Box box = new Box();//奶箱类
Producer p = new Producer(box);//生产者对象
Customer c = new Customer(box);//消费者对象
//创建2个线程对象,分别把生产者对象和消费者对象作为构造方法参数传递
Thread t1 = new Thread(p);
Thread t2 = new Thread(c);
//启动线程
t1.start();
t2.start();
}
}
5.4 Lock 中 Condition 的精确唤醒
LOCK 对比 Synchronize 的优势在于 LOCK 能够精确唤醒。
LOCK 中的睡眠和唤醒
睡眠: await()唤醒: signal(),signalAll()
#精确的通知和唤醒线程
package com.sjmp.demo02PC;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class ConditionDemo {
public static void main(String[] args) {
Data3 data3 = new Data3();
new Thread(()->{
for (int i = 0; i < 10; i++) {
data3.printA();
}
},"A").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
data3.printB();
}
},"B").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
data3.printC();
}
},"C").start();
}
}
class Data3{
private Lock lock = new ReentrantLock();
private Condition condition1 = lock.newCondition();
private Condition condition2 = lock.newCondition();
private Condition condition3 = lock.newCondition();
private int num = 1; // 1A 2B 3C
public void printA(){
lock.lock();
try {
while (num != 1){
condition1.await();
}
System.out.println(Thread.currentThread().getName()+" Im A ");
num = 2;
//唤醒2
condition2.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void printB(){
lock.lock();
try {
while (num != 2){
condition2.await();
}
System.out.println(Thread.currentThread().getName()+" Im B ");
num = 3;
//唤醒3
condition3.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void printC(){
lock.lock();
try {
while (num != 3){
condition3.await();
}
System.out.println(Thread.currentThread().getName()+" Im C ");
num = 1;
// 唤醒1
condition1.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}



