目录
一.何为并发
我们先从生活场景中找个例子;大家有没有排队买过奶茶,往往会排很长很长的队伍,因为要做奶茶,做奶茶的速度赶不上买的人的速度。那么这个场景去其实就是并发。
那么映射到我们的并发编程,就是一个接口假如同时有100个人访问,那么这个就属于并发。
那么什么是高并发?
高并发就是我系统能承受的并发数,所以,高并发其实是跟我们的系统有关的,比如一个小奶茶店,一个上午只能做500杯奶茶,那么如果有个3,400人排队,其实就是已经属于高并发了,那么500就是你系统能承受的最大的并发数。但是这3,400如果对于大的奶茶店,大奶茶店一个上午能做4,5000杯。那么3,400的量对于大店来说,根本就算不上是高并发。
如何支撑高并发?
这个要从一个接口的生命周期去考虑。除了从软件层面,比如不写死循环,接口字段设计,sql优化,缓存等等。
还有硬件资源:比如: 内存 磁盘 网卡 cpu 宽带等等。
CPU由之前的单核 现在变成了多核!那么CPU能提高并发能力,避免不了并发编程的主角:线程!
二.多线程的意义和使用
1.什么是线程
定义:线程是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。
操作系统中并发和并行的区别
- 并发是在一段时间内可以执行多个任务
- 并行是在同一时刻可以执行多个任务
线程存在的意义
- 异步执行
- 在多核CPU中,利用多线程可以实现真正意义上的并行执行
- 在一个应用进程中,会存在多个同时执行的任务,如果其中一个任务被阻塞,将会引起不依赖该任务的任务也被阻塞。通过对不同任务仓建不同的线程去处理,可以提升程序处理的实时性
- 线程可以认为是轻量级的进程,所以线程的创建、销毁比进程更快
多线程的特点
- 异步
- 并行
2.线程的生命周期
Java线程从创建到销毁,一共经历6个状态
NEW:初始状态,线程被构建,但是还没有调用start方法之前
RUNNABLED:运行状态,JAVA线程把操作系统中的就绪和运行两种状态统一称为“运行中”
BLOCKED:阻塞状态,表示线程进入等待状态,也就是线程因为某种原因放弃CPU使用权,阻塞也分为几种情况
WAITING:等待状态,不带超时时间
TIME_WAITING:超时等待状态,超时以后自动返回
TERMINATED:终止状态,表示当前线程执行完毕
3.线程的创建
1.继承Thread类
Thread类也实现了Runnable接口
public class TreadDemo extends Thread{
@Override
public void run() {
//在run方法里写业务逻辑
System.out.println(Thread.currentThread().getName());
}
public static void main(String[] args) {
TreadDemo t1 = new TreadDemo();
//star方法启动线程
t1.start();
2.实现Runnable接口
public class RunnableDemo implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
public static void main(String[] args) {
Thread t2 = new Thread(new RunnableDemo());
t2.start();
System.out.println("---------");
//简化的创建一个线程
new Thread(()-> {
System.out.println(Thread.currentThread().getName());
}, "aa").start();
}
}
3.实现Callable接口
这种方法创建的线程可以有返回值
public class CallableDemo implements Callable<String> {
@Override
public String call() throws Exception {
System.out.println(Thread.currentThread().getName());
return "hello~~";
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
ExecutorService es = Executors.newFixedThreadPool(1);
Future<String> future = es.submit(new CallableDemo());
System.out.println(future.get());
}
}
4.线程池
后面写
4.线程的启动
new Thread().start()
start()底层是一个native方法 java本身不提供线程 线程是操作系统提供的
run和start的区别
run是线程执行逻辑的方法,start是启动线程的方法。
5.线程的通信
wait一定会释放锁
notify唤醒处于等待队列的线程
notifyAll
实现一个生产者和消费者的案例
Producer
public class Producer implements Runnable{
private Queue<String> bags;
private int size;
public Producer(Queue bags, int size) {
this.bags = bags;
this.size =size;
}
@Override
public void run() {
int i = 0;
while (true) {
i++;
synchronized (bags) {
while (bags.size() == size) {
System.out.println("bags以满");
try {
//wait 一定会释放锁
//要用while判断 而不是if 因为可能下次抢占到锁的还是自己
bags.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
try {
//一秒生产一个
Thread.sleep(1000);
} catch (InterruptedException interruptedException) {
interruptedException.printStackTrace();
}
bags.add("bags-"+i);