1、简介
进程:
进程是操作系统结构的基础,计算机中正在运行的程序实例;可以分配给处理器并由处理器执行的一个实体;由单一顺序的执行显示,一个当前状态和一组相关的系统资源所描述的活动单元。简单的说,当我们启动一个应用程序,就会有个进程。可以通过任务管理器查看到当前系统的进程有哪些。系统给进程分配了独立的内存空间。一个正在运行的程序;
线程:
线程,有时被称为轻量级进程(Lightweight Process,LWP),是程序执行流的最小单元。一个标准的线程由线程ID,当前指令指针(PC),寄存器集合和堆栈组成。另外,线程是进程中的一个实体,是被系统独立调度和分派的基本单位,线程自己不拥有系统资源,只拥有一点在运行中必不可少的资源,但它可与同属一个进程的其它线程共享进程所拥有的全部资源。一个线程可以创建和撤消另一个线程,同一进程中的多个线程之间可以并发执行。由于线程之间的相互制约,致使线程在运行中呈现出间断性。一个执行路径
每一个程序都至少有一个线程,那就是程序本身。线程是程序中一个单一的顺序控制流程。 在单个程序中同时运行多个线程完成不同的工作,称为多线程。
单线程:同一个时刻,只允许执行一个线程
多线程:同一个时刻,可以执行多个线程
并发:同一个时刻,多个任务交替执行,造成一种“貌似同时”的错觉,简单的说,单核cpu实现的多任务就是并发
并行:同一个时刻,多个任务同时执行,多核cpu可以实现并行(也可以并发);
2、线程创建
方案一:
方式一:直接创建线程对象继承Thread类,重写run方法
方式二:使用匿名内部类创建Thread子类对象,重写run方法
//方式一:
优点:
可以创建多次线程对象
缺点:
不便书写,可扩展性差
//方式二:
优点:
书写方便
缺点:
只能创建一个对象
方案二:将线程(Thread)对象 与 线程任务(Runnable)分开1
方式一:实现Runnable接口,重写run方法,将线程任务放入线程对象。
方式二:使用匿名内部类,创建线程任务对象
//方式一:
优点:
将线程任务与线程对象分开
任务对象可以创建多个
缺点:
创建复杂
//方式二:
优点:
书写方便
缺点:
代码混乱
只能创建一个对象
3、线程的使用
package thread;
//继承Thread类实现线程
public class ClassThread01 extends Thread {
@Override
public void run() {
//run方法线程体
for (int i = 0; i < 20; i++) {
System.out.println("run线程体-----" + i);
}
}
public static void main(String[] args) {
//main方法,主线程
Runtime runtime = Runtime.getRuntime();
int i1 = runtime.availableProcessors();//获取本机电脑的cpu数量
System.out.println(i1);
ClassThread01 classThread01 = new ClassThread01();
classThread01.start();//启动线程
for (int i = 0; i < 20; i++) {
System.out.println("main线程体-----" + i);
}
}
}
4、线程方法
1、//设置线程名称
steName()
2、//启动线程
start()
3、//获取线程名称
getName()
4、//获取当前线程对象
currentThread()
5、//使当前线程休眠
sellp(时间)
6、//线程的优先级
线程的优先级取值范围为 1 ~ 10;
setPriority()
7、//获取线程的优先级
getPrioity()
8、//在指定的毫秒内让当前正在执行的线程休眠(即暂停执行),不会释放锁
sellp()
9、//中断线程休眠
interrupt()
10、//线程礼让。让出cpu,让其他线程执行,但礼让时间不确定,所以不一定礼让成功
yield()
11、//线程插队。插队的线程一旦插队成功,则肯定会先执行完插入的线程所有的任务
join()
12、//设置线程为守护线程
setDaemon( boolean b);
13、//线程的销毁,不建议使用
stop() 与 destroy() 不要用
当线程执行完run方法,就会等待被系统回收
线程的生命周期
![](https://i-blog.csdnimg.cn/blog_migrate/a9c671e227b3ad4795cea96030001d23.png)
新建 (New)
新建一个线程对象
就绪 (Runnable)
线程对象创建后,其他线程调用了该对象的start方法。该状态的线程位于可运行线程池中,变得可运行,等待获取CPU的使用权。
运行 (Running)
就绪状态的线程获取了CPU,执行程序代码。
阻塞 (Blocked)
阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态
死亡 (Dead)
线程执行完了或者因异常退出了run方法,该线程结束生命周期。
6、线程安全问题
多个线程同时操作同一个数据源
//方案一: 同步代码块
语法:
synchronized(锁对象){
}
//注意:保证多个线程的锁对象是同一个对象
//方案二: 同步方法
语法:
public synchronized void method() {
}
//注意:同步方法的锁对象是调用该方法的对象
//方案三: 同步静态方法
语法:
public static synchronized void method() {
}
/*
类对象:
当类被加载时,系统生成的一个对象,一个类只有一个类对象
获取类对象
类名.class
对象名.getClass()
*/
7、线程间通讯
多个线程间消息的传递
//继承于Object的方法
1、//表示线程一直等待,直到其他线程通知,与sleep不同,会释放锁
wait()
2、//指定等待的毫秒数
wait(long timeout)
2、//指定等待的毫秒数,与 指定的纳秒数
wait(long timeout,int x )
3、//唤醒一个处于等待状态的线程
notify()
4、//唤醒同一个对象上所有调用wait()方法的线程,优先级别搞的线程优先调度
notiflAll()
生产者与消费者模式
//工厂类
public class Factory {
private int num;
private final static int MAX = 100;
//生产方法
public synchronized void producerMethod() {
String name = Thread.currentThread().getName();
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (num <= MAX) {
System.out.println(name + "正在生产商品,当前商品总量:" + (++num));
notify();
} else {
System.out.println("仓库存储已满等待------售卖");
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
//消费方法
public synchronized void consumerMethod() {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
String name = Thread.currentThread().getName();
if (num > 0) {
System.out.println(name + "正在售卖商品,当前商品余量:" + (--num));
notify();
} else {
System.out.println("仓位储存不足等待------生产");
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
//生产者
public class producer implements Runnable {
private Factory factory;
public producer(Factory factory) {
this.factory = factory;
}
@Override
public void run() {
while (true) {
factory.producerMethod();
}
}
}
//消费者
public class Consumer implements Runnable{
private Factory factory;
public Consumer(Factory factory) {
this.factory = factory;
}
@Override
public void run() {
while (true) {
factory.consumerMethod();
}
}
}
//测试类
public class Text {
public static void main(String[] args) {
Factory factory = new Factory();
Consumer consumer = new Consumer(factory);
producer producer = new producer(factory);
Thread thread01 = new Thread(producer, "唐三");
Thread thread02 = new Thread(producer, "萧炎");
Thread thread03 = new Thread(consumer,"小舞");
Thread thread04 = new Thread(consumer,"萧薰儿");
thread01.start();
thread02.start();
thread03.start();
thread04.start();
}
}
8、线程池
原因:
一个线程大约占1MB的内存,在项目中大量创建线程会导致内存溢出;
线程在执行完run方法后,会等待系统回收,会导致内存浪费;
频繁的创建于回收线程,会导致资源浪费;
作用:
管理线程,如线程的创建、销毁、复用等
本质:
容纳多个线程的容器
//Executor(接口)
execute(),//执行线程任务
//ExecutorServicer(接口) 是Executor的子接口
shutdown()//关闭线程池
isShutdown()//判断线程池是否关闭
submit(Runnable task)//给线程池提交任务
submit(Callable<T> task)/
/给线程池提交线程任务
//ScheduledExecutorService(调度线程池)
public ScheduledFuture<?> scheduleAtFixedRate(
Runnable command,//线程任务
long initialDelay,//延迟时间
long period,//间隔时间
TimeUnit unit//时间单位
)
注意:下一次任务开始时间 - 上一次任务开始时间 = 间隔时间当任务执行时间大于间隔时间,下一次任务将会在上一次任务结束后,立即执行
//子类:
ThreadPoolExecutor
//构造函数:
public ThreadPoolExecutor(
int corePoolSize,//核心线程数量
int maximumPoolSize,//最大线程数量
long keepAliveTime,//线程回收时间
TimeUnit unit,//时间单位
BlockingQueue<Runnable> workQueue,//存储线程任务的队列
hreadFactory threadFactory,//线程工厂
RejectedExecutionHandler handler)//算法
线程池的使用步骤
创建线程池
提交任务
关闭线程池,注意抢占线程池中的线程默认为守护线程
Executors工具类
1、//创建 固定线程池(指定创建的线程数量)
static ExecutorService newFixedThreadPool(int nThreads)
2、//创建 单列线程池(1次创建1个线程数量)
static ExecutorService newSingleThreadExecutor()
3、//创建 可变线程池(int的最大值就是线程的上限,理论无上限)
static ExecutorService newCachedThreadPool()
4、//创建 抢占线程池(创建时指定线程数量可以传入线程的数量,不传入,则默认使用当前计算机中可用的cpu数量)
抢占线程池 中的线程是 守护线程
一个执行结束的线程会在其他正在执行的线程抢占执行的结果
static ExecutorService newWorkStealingPool(int parallelism)
5、//创建 单列调度线程池(一个线程的定时任务)
newSingleThreadScheduledExecutor()
6、//创建 调度线程池(多个线程的定时任务)
newScheduledThreadPool(int corePoolSize):
9、Callable
问题:
Runnabel接口无法返回数据
解决方案:
使用Callable
注意:Callable的使用依赖于线程池
Callable与Runnable的区别
callable提供call方法,有返回值
Runnable提供run方法,没有返回值
//Futre
作用:获取线程任务对象的返回值
方法:get()
注意:会阻塞当前线程,知道对应的线程任务执行完毕后,获取其返回值
10、Lock
作用:替换synchronized
体系:
//Lock(接口) 提供方法:lock()锁 与 unlock()开锁 //子类: ReentrantLock:重入锁 //ReentrantReadWriteLock:读写锁 提供的方法 Lock readLock();获取读锁 Lock writeLock();获取写锁 注意: 读-读 不互斥 读-写 互斥 写-写 互斥 经验: 读里面用读锁 写里面用写锁