进程和线程
进程 : 正在运行的应用程序
线程 : 一个正在运行的应用程序中的一条执行流程
1. 线程存在于进程中
2. 一个进程中至少有一个线程
3. 市面上绝大部分的程序,都是多线程程序
并行和并发
//角度 : 同一时刻,同一时间段
并行 : 在同一时刻有多件事情发生
并发 : 在同一时间段有多件事情发生
并发不一定并行,并行一定并发
多线程的程序 : 多线程程序是一个并行的程序
CPU处理程序的逻辑
//站在CPU的角度
1. CPU收集所有进程中的线程并打乱
2. 随机执行其中一个线程 (CPU在多个线程之间高速切换)
//站在线程的角度
1. 线程抢夺CPU的执行权
2. 抢到了就执行,没抢到阻塞
结论 : 多线程的程序的随机性很强
多线程的体系结构
多线程的开启方式
线程的开启方式一 : 继承的方式
步骤 :
1. 准备一个类,继承Thread -> MyThread
2. 主动重写 run方法
3. 在run方法内,编写线程对象需要执行的任务
4. 在需要使用线程的地方,创建MyThread对象
5. MyThread对象调用start() 启动线程
线程类:
public class MyThread extends Thread {//线程类
//主动生成带有String name参数的构造方法
public MyThread() {
}
public MyThread(String name) {
super(name);
}
//主动重写run
@Override
public void run() {
//线程任务是 打印10次HelloWorld
for (int i = 1; i <= 10; i++) {
System.out.println(getName() + "HelloWorld" + i);
}
}
}
主线程:
public class Demo {//进程
public static void main(String[] args) {//主线程
//创建自己的线程类对象
MyThread mt = new MyThread("线程1 : ");
//mt.setName("线程1 : ");
//启动线程
mt.start();//新开了一个线程
//主线程的任务
for (int i = 1; i <= 10; i++) {
System.out.println("Java" + i);
}
}
}
线程的开启方式二 : 实现的方式
步骤 :
1. 准备一个类,实现Runnable --> 任务类: MyTarget
2. MyTarget强制重写run方法,在run方法内编写线程的任务
3. 在需要使用线程的地方,创建Thread对象和MyTarget对象,并把MyTarget传入到Thread对象类 --> 任务分配
4. Thread对象 启动线程 -> 调用start()方法
任务类:
public class MyTarget implements Runnable {
@Override
public void run() {
for (int i = 1; i <= 10; i++) {
System.out.println( Thread.currentThread().getName() + "HelloWorld" + i);
}
}
}
主线程:
public class Demo {
public static void main(String[] args) {
//创建任务对象
MyTarget target = new MyTarget();
//创建线程对象
Thread thread = new Thread(target,"线程2 : ");
//thread.setName("线程2 : ");
//启动线程
//匿名内部类的方式实现线程对象的开启
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 1; i <= 10; i++) {
System.out.println(Thread.currentThread().getName() + "Python" + i);
}
}
},"啦啦啦");
thread.start();
thread1.start();
//main线程的任务
for (int i = 1; i <= 10; i++) {
System.out.println(Thread.currentThread().getName() + "Java" + i);
}
}
}
线程的开启方式三 : 有任务结果的线程开启方式
Callable<V> : 带有任务结果的任务接口 -> 线程的任务接口
抽象方法 : V call()
承上启下的对象 : FutureTask<V>
构造方法 :
FutureTask(Callable<V> callable)
FutureTask(Runnable runnable, V result)
FutureTask<V> 类是Runnable的实现类
Thread类中的构造:
Thread(Runnable target)
看线程任务的结果:
FutureTask<V> 类中有 V get() -> 获取线程对象的结果并返回
注意 : 它一定是等到线程执行完毕后才能获取结果
步骤 :
//创建Callable的实现类,重写call方法
1. 创建有结果的任务实现类对象 -> Callable<V>接口的实现类对象 (task)
2. 创建FutureTask对象,把Callable<V>接口的实现类对象 (task)传入给FutureTask对象
3. 创建Thread对象,把FutureTask对象传递给Thread对象
4. Thread对象调用start方法,启动线程
Callable:
import java.util.concurrent.Callable;
public class MyCallable implements Callable<String> {
@Override
public String call() throws Exception {
for (int i = 1; i <= 100; i++) {
System.out.println("舔狗 : 女神,睡了吗 ?");
}
return "陌生男性 : 你哪位 ?";
}
}
主线程:
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class Demo {
public static void main(String[] args) throws ExecutionException, InterruptedException {
//创建任务对象
MyCallable call = new MyCallable();
//创建承接对象
FutureTask<String> fk = new FutureTask<String>(call);
//创建线程对象
//把任务对象分配给线程对象
Thread thread = new Thread(fk,"线程3: ");
//thread.setName("线程3: ");
//启动线程
thread.start();
//main线程
for (int i = 0; i < 100; i++) {
System.out.println("PHP");
}
//获取线程的结果
String str = fk.get();
System.out.println(str);
}
}
多线程程序的内存图
线程对象名称的操作
设置名称 :
Thread 构造方法 :
Thread(String name) : 在创建线程对象的同时给线程对象取名字;
Thread(Runnable target, String name) : 传入线程任务对象,再给线程对象取名字
Thread 成员方法 :
void setName(String name) : 对线程对象取名字
获取名称 :
Thread 成员方法 :
String getName()
Thread类中的静态方法 : 获取当前线程对象
static Thread currentThread()
非Thread类的子类中获取线程名称 : Thread.currentThread().getName()
线程休眠的方法
Thread类中 : static void sleep(long millis) : 让当前线程对象休眠millis毫秒
休眠 : 线程放弃抢夺CPU的执行权,不释放锁资源的
时间到了 : 自己醒来,抢夺CPU执行权
1. 立刻抢到,立刻执行
2. 没有抢到,阻塞状态
线程的优先级方法
优先级 : 优先级越高,抢到CPU执行权的 概率 越大
默认优先级 : 5
最低优先级 : 1
最高优先级 : 10
设置优先级的方法 :
void setPriority(int newPriority)
获取优先级的方法
int getPriority() :
设置守护线程
void setDaemon(boolean on) : 传入true,那么调用此方法的线程对象就被设置为守护线程
同步代码块
同步代码块的格式 :
synchronized(锁对象){
需要被保护的代码; //当一个线程在执行此段代码时,不让其他线程访问
}
锁对象 :
1. 可以是Java中任意引用类型的对象
2. 锁对象要被所有的线程对象共享 (共享的锁控制所有线程)
当线程代码被上锁 : 程序会执行的慢一些
1. 先获取锁
2. 执行代码
3. 释放锁
线程对象执行,以前只需要抢夺CPU的执行权,现在上锁的代码需要线程抢夺CPU执行权和锁资源,线程才可以执行
休眠 : 线程放弃抢夺CPU的执行权,不释放锁资源的
同步方法
同步方法的格式
权限修饰符 状态修饰符 synchronized 返回值类型 方法名(形参列表){
方法体;//当一个线程在执行此段代码时,不让其他线程访问
//return 值;
}
//非静态同步方法的锁对象是谁 ? this
//静态同步方法的锁对象是谁 ? 本类的字节码对象
Lock接口
Lock接口 :
lock() : 上锁
unlock(): 解锁
实现类对象 : ReentrantLock
线程的生命周期
新建 -> NEW : 线程对象被创建,但是没有调用start时的状态
阻塞 -> BLOCKED : 线程对象没有获取CPU执行权或者锁资源,线程对象处于阻塞状态(时刻抢夺资源的路上) //清醒
限时等待 -> TIMED_WAITING : 线程对象被调用了sleep(long millis)或者wait(long timeout)方法
sleep(long millis) : 仅仅只释放CPU执行权,不释放锁资源
wait(long timeout) : 释放CPU执行权和锁资源
无限等待 -> WAITING : 线程对象被调用wait()方法
wait() : 释放CPU执行权和锁资源
运行 -> RUNNABLE : 线程对象调用start之后,并抢到了CPU执行权和锁资源时,正在运行的状态
死亡 -> TERMINATED : 线程对象执行完任务,被移除内存
等待和唤醒的方法
等待 : 同时释放锁资源和CPU的执行权
void wait() : 无限等待
void wait(long timeout) : 限时等待
唤醒 :
void notify() : 随机唤醒一个正在等待的线程对象
void notifyAll() : 把所有正在等待的线程对象唤醒
一般使用锁对象调用等待和唤醒的方法 : //锁对象被所有的线程对象共享
所以等待唤醒的方法来自于 : Object 类 //锁对象可以是任意类型的引用数据类型对象
等待唤醒案例(线程之间的通讯)
包子案例:
BaoZi.java
import java.io.Serializable;
import java.util.Objects;
/*
1. 事物描述类
2. 属性 : 状态 -> 包子有无 -> 控制多个线程
3. 锁对象的类型
4. 增强了程序的趣味性 : 属性 -> 馅
*/
public class BaoZi implements Serializable {
private static final long serialVersionUID = -1927890984971954740L;
//包子馅
private String xianer;
//包子状态
private boolean flag;//false
public BaoZi() {
}
public BaoZi(String xianer, boolean flag) {
this.xianer = xianer;
this.flag = flag;
}
public String getXianer() {
return xianer;
}
public void setXianer(String xianer) {
this.xianer = xianer;
}
public boolean isFlag() {
return flag;
}
public void setFlag(boolean flag) {
this.flag = flag;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
BaoZi baoZi = (BaoZi) o;
return flag == baoZi.flag &&
Objects.equals(xianer, baoZi.xianer);
}
@Override
public int hashCode() {
return Objects.hash(xianer, flag);
}
@Override
public String toString() {
return "BaoZi{" +
"xianer='" + xianer + '\'' +
", flag=" + flag +
'}';
}
}
Customer.java
import java.io.Serializable;
import java.util.Objects;
/*
顾客线程类
1. 线程类 -> extends Thread
2. 事物描述类
姓名 : 继承Thread
包子
*/
public class Customer extends Thread implements Serializable {
private static final long serialVersionUID = -1260624229038066665L;
//属性 : 包子属性
private BaoZi bz;
public Customer() {
}
public Customer(String name, BaoZi bz) {
super(name);
this.bz = bz;
}
public BaoZi getBz() {
return bz;
}
public void setBz(BaoZi bz) {
this.bz = bz;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Customer customer = (Customer) o;
return Objects.equals(bz, customer.bz);
}
@Override
public int hashCode() {
return Objects.hash(bz);
}
@Override
public String toString() {
return "Customer{" +
"bz=" + bz +
'}';
}
//线程类
@Override
public void run() {
//死循环
while(true){
//上锁
synchronized (bz){
//什么时候顾客线程需要等待
if (!bz.isFlag()){//bz.isFlag() == false
//顾客线程需要等待
try {
bz.wait();//释放锁和CPU执行权
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//能从上面下来,说明包子的状态是 true
System.out.println("姓名是 : " + getName() + " 的顾客正在吃 " + bz.getXianer() + " 的包子~~");
//吃包子花时间
try {
Thread.sleep(2000);//不释放锁资源 释放CPU执行权
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(getName() + " : 包子吃完了, 老板再来一份~");
System.out.println("-------------------------------");
//真正吃包子的代码
bz.setFlag(false);
//唤醒厨师线程
bz.notify();
}
}
}
}
Cook.java
import java.io.Serializable;
import java.util.Objects;
/*
1. 事务描述类 : 厨师
2. 线程类 : run
*/
public class Cook extends Thread implements Serializable {
private static final long serialVersionUID = -6881458292011075477L;
//属性 : 包子
private BaoZi bz;
public Cook() {
}
public Cook(String name, BaoZi bz) {
super(name);
this.bz = bz;
}
public BaoZi getBz() {
return bz;
}
public void setBz(BaoZi bz) {
this.bz = bz;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Cook cook = (Cook) o;
return Objects.equals(bz, cook.bz);
}
@Override
public int hashCode() {
return Objects.hash(bz);
}
@Override
public String toString() {
return "Cook{" +
"bz=" + bz +
'}';
}
int count = 0;
@Override
public void run() {
while(true){
//给代码上锁
synchronized (bz){
//什么时候厨师休息
if (bz.isFlag()){//bz.isFlag() == true
try {
bz.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//能下来说明 没有包子 包子的状态是false
//奇数次和偶数次做的包子馅不一样
if (count % 2 == 0){
bz.setXianer("茴香肉末馅");
}else{
bz.setXianer("西葫芦鸡蛋馅");
}
System.out.println("姓名是 : " + getName() + " 的厨师正在做 " + bz.getXianer() + " 的包子~~");
//吃包子花时间
try {
Thread.sleep(4000);//不释放锁资源 释放CPU执行权
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(getName() + " : 香喷喷的 "+bz.getXianer()+" 包子出炉啦, 快来抢购吧~");
//真正做包子的代码
bz.setFlag(true);
//唤醒顾客
bz.notify();
//统计变量
count++;
}
}
}
}
测试类Demo:
public class Demo {
public static void main(String[] args) {
//创建包子对象
BaoZi bz = new BaoZi();
//创建线程对象
Customer customer = new Customer("猪八戒",bz);
Cook cook = new Cook("中华小当家",bz);
//启动线程
customer.start();
cook.start();
}
}
运行结果: