轻松学会-线程

轻松学会-线程,第1张

轻松学会-线程 一、线程

线程(英语:thread)是 *** 作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实 际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程 并行执行不同的任务

区别进程线程根本区别作为资源分配的单位调度和执行的单位开销每个进程都有独立的代码和数据空间 (进程上下文),进程间的切换会有较 大的开销线程可以看做轻量级的进程,同一类线程共享 代码和数据空间,每个线程有独立运行栈和程 序计数器(PC),线程切换的开销小所处环境在 *** 作系统中能同时运行多个任务(程 序)在同一应用程序中有多个顺序流同时执行分配内存系统在运行的时候会为每个进程分配不 同的内存区域线程间共享进程的所有资源,每个线程只有有 自己的堆栈和局部变量。线程由CPU独立调度 执行,在多CPU环境下就允许多个线程同时运 行包含关系没有线程的进程可以看作单线程,如果 一个进程拥有多个线程,则执行过程不 是一条线的,而是多条线(线程)共同 完成的线程是进程的一部分,所以线程有的时候会被 称为是轻量级进程或轻权进程

注意:有的多线程是模拟出来的,真正的多线程是指有多个 cpu,即多核,如服务器。如果是模拟出来的 多线程,即一个 cpu 的情况下,在同一个时间点,cpu 只能执行一个代码, 因为切换的很快,所以就 有同时执行的错觉。

多线程优缺点

优点 资源利用率更好,程序设计在某些情况下更简单,程序响应更快。 缺点 设计更复杂,虽然有一些多线程应用程序比单线程的应用程序要简单,但其他的一般都更复杂。在 多线程访问共享数据的时候,这部分代码需要特别的注意。线程之间的交互往 往非常复杂。不正确 的线程同步产 生的错误非常难以被发现,并且重现以修复。 上下文切换的开销较大, 当 CPU 从执行一个线程切换到执行另外一个线程的时候,它需要 先存储当 前线程的本地的数据,程序 指针等,然后载入另一个线程的本地数据,程序指针 等,最后才开始 执行。这种切换称为“上下文切换”(“context switch”)。CPU 会在一 个上下文中执行一个线程,然后 切换到另外一个上下文中执 行另外一个线程。上下文切换 并不廉价。如果没有必要,应该减少上 下文切换的发生。

二、创建线程

2.1、继承Thread类实现

线程: 执行的顺序流 多线程: 多任务执行,多路径执行 创建线程的方式: 1.继承Thread类,重写run() 2.实现Runnable接口,重写run() 接口可以多实现 实现资源共享 3.实现juc包下Callable接口,重写call()

public class Class001_Thread extends Thread {
    
    @Override
    public void run() {
        for(int i=1;i<=20;i++){
            System.out.println("一边喝水....");
            try {
                Thread.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
​
    public static void main(String[] args) {
        //创建线程
        Class001_Thread th = new Class001_Thread();
​
        //开启线程 void start() 导致此线程开始执行; Java虚拟机调用此线程的run方法。
        th.start();
​
        for(int i=1;i<=20;i++){
            System.out.println("一边讲课....");
            try {
                Thread.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

2.2 继承Runnable实现

实现Runnable接口,重写run方法 优点: 接口多实现,类的单继承 资源共享

public class Class002_Thread implements Runnable{
   
​
    public static void main(String[] args) {
        //创建线程
        Thread th = new Thread(new Class002_Thread());
​
        //开启线程
        th.start();
​
        for(int i=1;i<=20;i++){
            System.out.println("一边陪女朋友...");
            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
​
    
    @Override
    public void run() {
        for(int i=1;i<=20;i++){
            System.out.println("一边打游戏...");
            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

练习:

实现12306模拟购票

public class Class003_12306 implements Runnable{
    //共享资源: 100张票
    int tickets = 100;
​
    
    //ABC
    @Override
    public void run() {
        while(true){
            if(tickets<=0){
                break;
            }
            System.out.println(Thread.currentThread().getName()+"正在购买第"+tickets-- +"张票....");
            
        }
    }
​
    public static void main(String[] args) {
        //创建3个线程
        Class003_12306 web = new Class003_12306();
        Thread th1 = new Thread(web,"张三");
        Thread th2 = new Thread(web,"李四");
        Thread th3 = new Thread(web,"王五");
​
        //开启线程
        th1.start();
        th2.start();
        th3.start();
    }
}

模拟龟兔赛跑 一共100步,跑完100步就赢了,只要一个参赛者赢了另外一个参赛者就不跑了 乌龟正常跑,兔子每跑10步休息10ms 注意: 可以通过共享的标识控制多线程执行中线程的结束

public class Class004_Racer implements Runnable{
    private String winner = null;  //存储赢的人的名字
​
​
    @Override
    public void run() {
        for(int i=1;i<=100;i++){
            System.out.println(Thread.currentThread().getName()+"正在跑第"+i+"步...");
​
            if("兔子".equals(Thread.currentThread().getName()) && i%10==0){
                try {
                    Thread.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
​
            //检查是否结束
            if(checkOver(i)){
                break;
            }
        }
    }
​
    
    private boolean checkOver(int steps) {
        if(steps==100){
            winner = Thread.currentThread().getName();
            return true;
        }
        if(winner!=null){
            return true;
        }
        return false;
    }
​
    public static void main(String[] args) {
        Class004_Racer racer = new Class004_Racer();
        //创建2个线程
        Thread th1 = new Thread(racer,"乌龟");
        Thread th2 = new Thread(racer,"兔子");
        //开启线程
        th1.start();
        th2.start();
    }
}

2.3 collable--了解

测试第三种开启线程方式--> 了解 实现juc包下Callable接口,重写call方法,在call方法中定义线程体 创建与开启:使用线程池实现

优点: ​ 1.call中可以抛出异常

2.call可以存在返回值(线程执行完毕的结果)

ExecutorService : 提供了一些线程池管理线程执行的相关方法 ​ Future表示异步计算的结果。

public class Class005_Racer implements Callable {
    //共享资源 : 记录赢的人名字  标识
    private String winner = null;
​
    
    @Override
    public Integer call() {
        for(int steps =1;steps<=100;steps++){
            System.out.println(Thread.currentThread().getName()+"正在跑第"+steps+"几步");
​
            if("pool-1-thread-1".equals(Thread.currentThread().getName())&&steps%10==0){
                try {
                    Thread.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
​
            //检查是否结束
           boolean flag = checkOver(steps);
​
           if(!flag){
               return steps;
           }
        }
        return -1;
    }
​
    
    private boolean checkOver(int steps) {
        //已经存在其他参赛者赢了
        if(winner!=null){
            return false;
        }
        //当前线程赢了
        if(steps==100){
            winner = Thread.currentThread().getName();
            return false;
        }
        return true;
    }
​
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //一场比赛
        Class005_Racer racer = new Class005_Racer();
        //两个参赛者
        //创建执行服务: 线程池
        ExecutorService server = Executors.newFixedThreadPool(2);
        //提交执行
        Future future1 = server.submit(racer);
        Future future2 = server.submit(racer);
        //获取结果
        Integer result1 = future1.get();
        Integer result2 = future2.get();
        System.out.println(result1);
        System.out.println(result2);
​
        //关闭服务
        server.shutdown();
    }
}
​
​
//被重写方法上的异常抛出类型要求 >= 重写方法上异常抛出类型
class Fu{
    void test() throws FileNotFoundException{}
}
​
class Zi extends Fu{
    void test() throws FileNotFoundException {
        throw new FileNotFoundException();
    }
}

2.4 内部类定义线程体

public class Class006_Thread{
    //静态内部类
    static class Inner1 implements Runnable{
        @Override
        public void run() {
            for(int i=1;i<=20;i++){
                System.out.println("一边游泳....");
                try {
                    Thread.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    public static void main(String[] args) {
        //开启线程
        new Thread(new Inner1()).start();
​
        //匿名内部类
        new Thread(new Runnable() {
            @Override
            public void run() {
                for(int i=1;i<=20;i++){
                    System.out.println("一边喝水....");
                }
                try {
                    Thread.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
​
        new Thread(()->{
            for(int i=1;i<=20;i++){
                System.out.println("一边吐泡泡....");
            }
            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();
    }
}

三、线程状态

3.1线程状态

线程状态: 五种状态 新生状态 : new 就绪状态 : 调用start方法,线程就会进入就绪队列 运行状态 : 当cpu调度就绪的线程,线程开始执行 阻塞状态 : 线程一旦进入阻塞状态,需要等待阻塞解除 终止状态3.IO

当线程进入阻塞状态,阻塞解除之后不会直接恢复到运行状态,会直接进入就绪状态,等待cpu的下一次调度 ​ 如果一个线程已经进入终止状态,无法恢复

如何进入终止状态: ​ 1.正常执行完毕 2.stop()已过时不推荐 3.通过添加标识判断-->推荐

如何进入到就绪状态: ​ 1.start() ​ 2.阻塞解除 ​ 3.线程切换,cpu调度切换 ​ 4.yield()

如何进入到阻塞状态: ​ 1.sleep() ​ 2.join() ​ 3.wait() ​ 4.IO

sleep : 线程休眠睡眠 ​ static void sleep(long millis) 导致当前正在执行的线程休眠(暂时停止执行)指定的毫秒数 ​ 1s = 1000ms

特点: 一个线程进入到休眠状态,会自动让出cpu资源,cpu可能会调度其他线程 ​ 抱着资源睡觉-->资源值得是对象的锁资源

作用: ​ 1.模拟网络延迟 ​ 2.方法问题出现的可能性

3.2 yield 礼让线程

yield 礼让线程 让出cpu的资源,直接进入到就绪状态

public class Class002_Yield implements Runnable{
    public static void main(String[] args) {
        Class002_Yield cy = new Class002_Yield();
        new Thread(cy,"A").start();
        new Thread(cy,"B").start();
    }
​
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"开始了....");
        Thread.yield();
        System.out.println(Thread.currentThread().getName()+"结束了....");
    }
}

3.3 join 插队线程

join() 插队线程 void join() 等待这个线程死亡。 void join(long millis) 此线程最多等待 millis毫秒。 void join(long millis, int nanos) 此线程最多等待 millis毫秒加上 nanos纳秒。

被插队的线程会进入到阻塞状态

注意: 先就绪后插队

public class Class003_Join {
    public static void main(String[] args) {
        new Thread(new Father()).start();
    }
}
​
class Father implements Runnable{
    @Override
    public void run() {
        System.out.println("想吸烟了。。。。");
        System.out.println("给儿子100块。。。让儿子去买烟。。。。");
        //创建儿子线程
        Thread th = new Thread(new Son());
        //就绪
        th.start();
        //插队
        try {
            th.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("接过烟,吸一口。。。");
    }
}
​
class Son implements Runnable{
    @Override
    public void run() {
        System.out.println("接过钱,去买烟。。。。");
        System.out.println("路边看到一家游戏厅,进入玩10s钟。。。。");
        for(int i=1;i<=10;i++){
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(i+"s过去了。。。。。");
        }
        System.out.println("赶紧去买烟。。买一盒煊赫门。。。");
        System.out.println("把烟递给老爸。。。。");
    }
}

3.4 interrupt 中断线程

中断线程 void interrupt() 为该线程添加中断标识 boolean isInterrupted() 判断该线程是否曾经调用interrupt()方法添加过中断标识 static boolean interrupted() 判断该线程是否曾经调用interrupt()方法添加过中断标识,同时会复位这个标识

当线程处于休眠过程(执行sleep()方法),如果线程被添加了中断标识,会遇到此异常

InterruptedException - 如果有任何线程中断了当前线程。 抛出此异常时,将清除当前线程的中断状态 。

public class Class004_Interrupt implements Runnable{
​
    @Override
    public void run() {
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        for(int i = 1;i<=10;i++){
​
            if(Thread.interrupted()){  //复位
                System.out.println("添加了中断标识,即将结束线程");
                System.out.println(Thread.currentThread().isInterrupted());  //只判断
                break;
            }
            System.out.println("---------------------"+i+"---------------------");
        }
    }
​
    public static void main(String[] args) {
        Thread th = new Thread(new Class004_Interrupt());
        th.start();
​
        System.out.println(th.isInterrupted());  //false
        //添加中断标识
        th.interrupt();
        System.out.println(th.isInterrupted());  //true
    }
}

3.4 守护线程

线程的分类: 用户线程 : 创建的新的线程默认为用户线程 守护线程 : 守护用户线程的执行

如果存在多个用户线程,需要所有的用户线程全部执行完毕,JVM才会正常的退出 ​ 当所有的用户线程全部执行完毕,守护线程会直接结束退出

守护线程 : setDaemon(true) ​ 垃圾回收机制是一个典型的守护线程的案例

public class Class005_Daemon implements Runnable{
    public static void main(String[] args) {
        Thread th = new Thread(new Class005_Daemon());
​
        //为th线程设置为守护线程
        th.setDaemon(true);
        //开启线程
        th.start();
​
        //主线程具体代码
        for(int i = 1;i<=20;i++){
            try {
                Thread.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("正在吃第"+i+"根面条...");
        }
    }
​
    @Override
    public void run() {
        int i = 1;
        while(true){
            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("守护线程----->"+i++);
        }
    }
}

3.5 getState()获取进程状态

getState() 获取线程状态 Thread.State 线程状态。 线程可以处于以下状态之一: NEW -> 新生 尚未启动的线程处于此状态。

RUNNABLE -> 就绪|运行 ​ 在Java虚拟机中执行的线程处于此状态。

BLOCKED -> 等待获取对象锁资源的过程 ​ 被阻塞等待监视器锁定的线程处于此状态。

WAITING -> wait(),join()等 ​ 无限期等待另一个线程执行特定 *** 作的线程处于此状态。

TIMED_WAITING -> 与时间相关的等待 sleep(ms) ,wait(ms),join(ms)等 ​ 正在等待另一个线程执行最多指定等待时间的 *** 作的线程处于此状态。

TERMINATED -> 终止 ​ 已退出的线程处于此状态。

public class Class006_getState implements Runnable{
    public static void main(String[] args){
        Thread th = new Thread(new Class006_getState());
​
        System.out.println(th.getState());  //NEW
        th.start();
        try {
            Thread.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        while(true){
            try {
                Thread.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(th.getState());
            //如果th线程是终止状态,循环结束
            if(th.getState()==Thread.State.TERMINATED){
                break;
            }
        }
    }
​
    @Override
    public void run() {
        for(int i=1;i<=10;i++){
            if(i==5){
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println(i);
        }
    }
}

3.6 getPriority 编程优先级

线程优先级 getPriority : 优先执行哪一个线程概率高 1~10 1 : 最小 10 : 最大 5 : 默认

void setPriority(int newPriority) 更改此线程的优先级。

int getPriority() 返回此线程的优先级。

static int MAX_PRIORITY 线程可以拥有的最大优先级。 ​ static int MIN_PRIORITY 线程可以拥有的最低优先级。 ​ static int NORM_PRIORITY 分配给线程的默认优先级。

ublic class Class007_Priority implements Runnable{
​
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName());
    }
​
    public static void main(String[] args) {
        Class007_Priority cp = new Class007_Priority();
        Thread th1 = new Thread(cp,"A");
        Thread th2 = new Thread(cp,"B");
        Thread th3 = new Thread(cp,"C");
​
        th1.setPriority(1);
        //th3.setPriority(10);
        th3.setPriority(Thread.MAX_PRIORITY);
​
        System.out.println(th1.getPriority());
        System.out.println(th2.getPriority());
        System.out.println(th3.getPriority());
​
        th1.start();
        th2.start();
        th3.start();
    }
}

四、线程安全

线程安全: 多线程同时 *** 作同一份资源,才有可能遇到数据不安全情况

同步锁 : synchronized 使用分为2部分: 同步的条件 : 协调多个线程排队执行的条件(对象的锁资源:默认理解为只有一个) 同步的代码 : 多个线程之间排队执行的代码

4.1 同步方法:

synchronized关键字修饰方法 --> 简单,但是范围可能过大,效率

成员方法 条件: 相当于锁this 范围 : 当前方法体

静态方法 条件: 相当于锁类 范围 : 当前方法体

同步块: 需要多线程排队执行的代码定义在同步块中,满足条件要求才能执行 --> 排队执行的代码范围小,效率高,有可能不安全 synchronized(条件){ 排队执行的代码段; }

测试使用同步方法保证12306案例数据安全:

public class Class001_Synchronized {
    public static void main(String[] args) {
​
    }
}
class Web01_12306 implements Runnable{
    //共享资源: 100张票
    int tickets = 100;
​
    
    //ABC
    @Override
    public void run() {
        while(true){
            if(buyTicket()){
                break;
            }
            try {
                Thread.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
​
    
    public synchronized boolean buyTicket(){
        if(tickets<=0){
            return true;
        }
        System.out.println(Thread.currentThread().getName()+"正在购买第"+tickets-- +"张票....");
        try {
            Thread.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return false;
    }
​
    public static void main(String[] args) {
        //创建3个线程
        Web01_12306 web = new Web01_12306();
        Thread th1 = new Thread(web,"张三");
        Thread th2 = new Thread(web,"李四");
        Thread th3 = new Thread(web,"王五");
​
        //开启线程
        th1.start();
        th2.start();
        th3.start();
    }
}

4.2 同步块

同步块: 需要多线程排队执行的代码定义在同步块中,满足条件要求才能执行 --> 排队执行的代码范围小,效率高,有可能不安全 synchronized(条件){ 排队执行的代码段; }

条件: this : 默认指代当前调用成员方法的对象 类.class : 指代某一个类型的Class对象 资源 : 成员变量->对象

测试使用同步块保证12306案例数据安全: 锁类.class : 简单,因为一个类的Class对象只有一个,相当于锁了这个类的所有对象,如果存在多个对象,建议直接锁某个对象,使用this 锁this : 锁住当前调用成员方法的对象,相当于锁了当前对象的所有资源(成员),如果只想锁某一个资源,name建议直接锁资源 锁资源 : 锁当前对象的某一个成员,要求这个成员为自定义的引用数据类型的对象地址,保证能够锁住,效率最高,但是容易写错

注意: 锁要锁不变的东西才能锁住,自定义的引用数据类型的地址不变

锁类.class、锁this 例子

public class Class002_Synchronized {
    public static void main(String[] args) {
​
    }
}
​
class Web02_12306 implements Runnable{
    //共享资源: 100张票
    int tickets = 100;
​
    
    @Override
    public void run() {
        while(true){
            //ABC
            synchronized (Web02_12306.class){
            //synchronized (this){
                if(tickets<=0){
                    break;
                }
                System.out.println(Thread.currentThread().getName()+"正在购买第"+tickets-- +"张票....");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
​
    public static void main(String[] args) {
        //创建3个线程
        Web02_12306 web = new Web02_12306();
        Thread th1 = new Thread(new Web02_12306(),"张三");
        Thread th2 = new Thread(new Web02_12306(),"李四");
        Thread th3 = new Thread(new Web02_12306(),"王五");
​
        //开启线程
        th1.start();
        th2.start();
        th3.start();
    }
}

锁资源 例子

public class Class003_Synchronized {
    public static void main(String[] args) {
​
    }
}
​
class Web03_12306 implements Runnable{
    //共享资源: 100张票
    Tickets tickets = new Tickets();
​
    
    @Override
    public void run() {
        while(true){
            //ABC
            synchronized (tickets){
                if(tickets.num<=0){
                    break;
                }
                System.out.println(Thread.currentThread().getName()+"正在购买第"+tickets.num-- +"张票....");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
​
    public static void main(String[] args) {
        //创建3个线程
        Web03_12306 web = new Web03_12306();
        Thread th1 = new Thread(web,"张三");
        Thread th2 = new Thread(web,"李四");
        Thread th3 = new Thread(web,"王五");
​
        //开启线程
        th1.start();
        th2.start();
        th3.start();
    }
}
​
class Tickets{
    int num = 100;
}

五、线程通信

线程通信 wait() 等待,一个线程一旦调用wait(),会进入该对象的等待池中进行等待,等待被唤醒,等待的过程中处于等待阻塞的状态 wait-->会释放cpu的资源与对象的锁资源 sleep-->会释放cpu的资源不会释放对象的锁资源 notify() 唤醒,唤醒该对象等待池中正在等待的线程,被唤醒的线程相当于被激活,能够被cpu调用,但是是否能够执行要看是否能获取到对象的所资源--> 满足同步执行的条件 wait,notify都是Object类提供的成员方法,用来处理多线程之间共享数据的存储问题,必须使用在同步环境下,否则会遇到异常java.lang.IllegalMonitorStateException

通过信号灯法实现人车公用街道 -> 生产者消费者模式 人 : 人走东西走向 --> 绿灯走 车 : 车走南北走向 --> 红灯走 街道 成员 : 红绿灯 boolean : true->绿灯->人走 false->红灯->车走 功能: 东西走向 南北走向 注意: 人车要共享一个街道,才能共享一个红绿灯,才能通过一个红绿灯控制多线程的执行问题

public class Class004_Street {
    public static void main(String[] args) {
        Street street = new Street();
        new Thread(new Person(street)).start();
        new Thread(new Car(street)).start();
    }
}
​
//街道
class Street{
    //红绿灯
    boolean flag = false;
​
    //ns
    public synchronized void ns(){
        if(!flag){
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("车走");
            //红绿灯变灯
            flag = true;
​
            //唤醒对方线程
            this.notify();
​
            //自己等待
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
​
    //we
    public synchronized void we(){
        if(flag){
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("人走");
            //红绿灯变灯
            flag = false;
​
            //唤醒对方线程
            this.notify();
​
            //自己等待
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
​
class Person implements Runnable{
    private Street street = null;
​
    public Person(Street street) {
        this.street = street;
    }
​
    @Override
    public void run() {
        while(true){
            street.we();
        }
    }
}
​
class Car implements Runnable{
    private Street street = null;
​
    public Car(Street street) {
        this.street = street;
    }
​
    @Override
    public void run() {
        while (true){
            street.ns();
        }
    }
}

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/5606743.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-15
下一篇 2022-12-15

发表评论

登录后才能评论

评论列表(0条)

保存