- 多线程
- 进程:正在运行的一个程序 系统会为这个进程分配独立的内存资源
- 线程:具体执行任务的最小单位
- 一个进程最少拥有一个线程(主线程 运行起来就执行的线程)
- 线程之间是共享内存资源的(进程申请的)
- 线程之间可以通信(数据传递 :多数为主线程和子线程)
- 每一个线程都有自己的运行回路(生命周期)
线程的生命周期状态:
- NEW:新建 线程刚被创建好
- RUNNABLE:就绪状态 只要抢到时间片就可以运行这个线程
- BLOCKED,:堵塞状态 sleep wait
- WAITING,:等待状态 wait
- TIMED_WAITING,
- TERMINATED;终止
为什么需要创建一个子线程
- 如果在主线程中存在又比较耗时的操作:下载视频 上传文件 数据处理
- 这些操作会阻塞主线程 ,后面的任务必须等这些任务处理完毕
- 之后才能执行 用户体验比较差
- 为了不阻塞主线程 需要将耗时的任务放入子线程中
如何创建一个子线程
- join :让当前这个线程阻塞 等join 的线程执行完毕在执行
- setName:设置线程名称
- getName:获取线程名称
- currentThread:获取当前运行的线程对象
- start:开启任务
.方法一 :定义一个类继承于Thread 实现run方法
main方法里面执行的代码 是在主线程里面执行的
主线程的名称是main 由系统定义 自己不能定义
获取线程名称
String name=Thread.currentThread().getName();
System.out.println(name);
创建对象 子线程
TestThread tt=new TestThread();
设置子线程的名字
tt.setName("子线程");
开启任务
对于同一个线程 不能开启两次
tt.start();
tt1=new TestThread();
tt1.start();
主线程
for (int i = 0; i < 5; i++) {
System.out.println("main"+i);
创建一个类 用于实现子线程
class TestThread extends Thread{
// 实现run方法
// 方法里面就是具体实现的代码
@Override
public void run() {
String name=Thread.currentThread().getName();
for (int i=0;i<100;i++)
{
System.out.println(name+i);
}
super.run();
}
}
方法二:实现runable接口 实现run方法
- 1.创建任务 创建实现runable接口
- 2.使用Thread 实现这个接口
- 3.开启任务 start
实现方法1.接口 抽象方法
创建一个任务:创建一个类实现Runable接口
PXDEThread pt=new PXDEThread();
//使用Thread操作这个任务
Thread t=new Thread(pt);
t.setName("子线程");
t.start();
Thread t1=new Thread(pt);
t1.setName("子线程1");
t1.start();
class PXDEThread implements Runnable
{
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName()+" "+i);
}
}
}
实现方法2:
这个任务只使用一次
Thread t=new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName()+" "+i);
}
}
});
t.setName("子线程");
t.start();
//创建线程的同时 开启线程任务
//不需要操作线程本身
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName()+" "+i);
}
}
}).start();
//使用Lambda
//不建议:阅读性太差
new Thread(()->{
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + " " + i);
}
}).start();
线程安全
- synchronized Lock 加锁解锁
- synchronized 同步监听器 需要一把锁
- 任何一个对象都有一把锁/监听器
- 如果多个线程操作同一个代码块 ,并需要是同步的
- 那么必须操作同一个对象的同一把锁
- synchronized (监听器/对象/锁){
- 需要同步的代码
- }
- 解决方法
- 1.同步代码块
- 2.同步方法 同步监听器是当前对象本身
- 必须确保多个对象调用的是同一个对象
- public synchronized void test()
- 本质就是同步代码快
- 等价于
- synchronized (this)
- {
- text();
- }
synchronized (obj){
/需要同步的代码
if (num>0)
{
System.out.println(name+"出票"+num);
num--;
try{
//唤醒其他线程
obj.notify();
//当前线程等待
obj.wait();
}catch (Exception e){
e.printStackTrace();
}
}else {
break;
}
}
- 死锁
- Lock
- ReentrainLock 可重入锁
- 使用接口实现线程间数据的回调
- Person 类
- 线程里面 Agent 类
- 1.如何创建一个线程
- 2.线程的同步
- 3.主线程和子线程之间使用接口回调数据
- 4.线程之间的通信:synchronized (wait notifi notifiall)
- ReentranLock lock;
- Condition c=lock.newCondition();
- await single singleAll
火车站买票
//全国的卖票系统 就一个
Ticket ticket=new Ticket("重庆");
Thread tr=new Thread(ticket);
tr.start();
Ticket ticket1=new Ticket("上海");
Thread tr1=new Thread(ticket1);
tr1.start();
//用于卖票的任务
class Ticket implements Runnable
{
//定义所有火车票的数量
public static int num=20;
String name;
public Ticket(String name)
{
this.name=name;
}
static final Object obj=new Object();
//创建一个可重新载入的锁
static ReentrantLock lock=new ReentrantLock();
@Override
public void run() {
for (int i = 0; i < 20; i++) {
//判断有没有票
lock.lock();
if (num > 0) {
System.out.println(name + "出票" + num);
num--;
try {
} catch (Exception e) {
e.printStackTrace();
}
} else {
break;
}
lock.unlock();
}
}
}