版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_40990836/article/details/79039130
一、 多线程开发
进程 : 系统进行资源分配的基本单位
线程 : 是系统独立跳读和分配CPU的基本单位指令运行时的程序的调度单位
注意 : 进程内有一个相对独立的,可调度的执行单元
多线程 : 计算机内因有硬件支持,而能够在同一时间执行多与一个线程的开发技术
二、 线程和进程的关系
- 进程是线程的容器, 一个进程至少包含一个线程,包含多个线程的进程称之为多线程开发.
- 线程是进程中一个独立,可调度的执行单位,运行资源的内存有所属进程分配.
三、如何实现多线程
- 继承Thread类
i . 创建子类
ii . 生成子类对象
iii . 子类对象开启线程- 实现 Runnable 接口
i . 创建实现类,实现run 方法
ii . 生成实现类对象
iii . 为实现类对象分配线程并开启
// ① 继承Thread 类
public class Child extends Thread{
// 问题:
private String name;
public String getSelfName(){
return name;
}
public void setSelfName(String name){
this.name = name;
}
public child(){
super();
}
public Child(String name,String tName){
super(tName); // tName 线程名
this.name = name;
}
// 重写run方法
public void run(){
// 完成自身多线程功能
bowl();
// 功能后 线程任务上交父级
super.run();
}
public void bowl(){
for(int i = 0; i < 3; i++){
System.out.println("2018" + name + "梦想成真")
}
}
}
// 测试类
public class Test{
public static void main(String[] args){
// 创建实现类的对象
Child c1 = new Child("小明","明明进程");
Child c1 = new Child("小明","明明进程");
Child c1 = new Child("小明","明明进程");
System.out.println(c1.getName); // 打印明明进程
System.out.println(c1.getSelef); // 打印小明姓名
// 获取当前所在线程名 == Thread.currentThread().getName();
// 实现类对象开辟线程
c1.start();
c2.start();
c3.start();
}
}
问题 : 实现类拥有 name 属性时,标准的get set 方法会报错
原因 : Thread 类 也拥有同名的name 属性(用于记录当前线程名),错误原因出在name 属性的 get set 方法,也是标准写法,但是被final 所修饰,所以实现类的标准 get set 方法 出现了强行重写final修饰的方法的错误
解决 : 将实现类 get set 方法定义为非标准化的
规避 : 实现类不建议拥有 name 属性
- 实现 Runnable 接口 实现多线程
原因: 当资源(java类)作为多个线程共有的资源
结论: 将此类情况的java类定义为Runnable接口的实现类以供多个线程共同持有
// ① 实现 runnable 接口
public class Ticket implements Runnable{
private int num;
// get set 方法
public int getNum(){
return num;
}
public void setNum(int num){
this.num = num;
}
// 构造方法, 初始化 num 值 , 票数总数为 20
public Ticket(){
this.num = 20;
}
// 实现run 方法
@Override
public void run(){
// 调用卖票的方法
}
// 卖票
// 添加同步锁
// ① synchronized 作为方法的关键词
// i) 该方法为同步方法
// 目的: 同一时刻只能被一个线程锁调用
// 该方法为同步方法: 同一时刻只能被一个线程锁调用
// 应用场景: 方法功能单一,且方法需要整体做同步处理
// ② synchronized 作为同步代码块
// i) 代码块为同步代码块
// 目的: 代码块同一时刻,只能被一个线程锁使用
// 应用场景 : 方法功能逻辑较多,方法需局部做同步处理
public static final Object mutex = new Object();
private void sellTicketSuper(){
// 静态代码块 synchronized
// 参数: mutex 任意的唯一对象
// 通常: 互斥锁标识
// static 方法中: Ticket.class (当前类)
// 成员方法中: this
// this 不唯一时: static final Object
while(true){
// 卖票的窗口号: 线程名 Thread.currentThread().getName()
String name = Thread.currentThread().getName();
// 一票一锁: 抢到票,可以卖票,买了抢到那张票之后,才会开锁
synchronized(mutex){ // 加锁的开始
if(num > 0){
// 窗口所卖的票次
int temp = 20 - num + 1;
System.out.println(ctName + "抢到第" + temp + "张");
// 卖出一张, 票数减少1, 结果为剩余票数
num--;
System.out.println("剩余票数" + num);
}
} // 开锁的标识
// 结束卖票判断
if(num <=0){
break;
}
// 锁住的逻辑较多时,可能执行过程中多次让出CPU执行权.导致其他线程
// 在未开锁状状态区竞争资源,从而进入了锁池状态,失去下一次CPU第一时刻经证权
// 所有需要开锁的线程在开锁后 < 强行让出CPU执行权 >
Thread.yield();
}
}
}
// 测试类测试
public class Test{
public static void main(String[] args){
// 实例化一个对象
Ticket01 t1 = new Ticket01();
// 创建一个线程
Thread th1 = new Thread(t1,"一号窗口");
Thread th2 = new Thread(t1,"二号窗口");
Thread th3 = new Thread(t1,"三号窗口");
th1.start();
th2.start();
th3.start();
}
}
volatile
volatile 在多个线程访问一个资源时, 多个线程中的任意一个线程修改了资源值,会将资源修改信号和修改值马上同步给其他所有访问路线
内外都需要访问 – 定义public static
多个线程需要访问 – volatile