关于实现Runnable接口的类中有公共属性问题

探讨了在多线程环境下,如何确保共享变量的线程安全性,分析了不同调用方式对线程安全的影响,并提供了加锁控制的具体实现。

背景:多线程开发的时候,一般都是继承Runnable接口,但是有可能类中有一个公共变量,那么这个变量是不是线程安全的呢?

代码如下:

public class TestThread implements Runnable{

    private int number = 0;

    public TestThread(int num) {
        number = num;
    }

    @Override
    public void run() {
        for(int i = 0;i<10;i++) {
            System.out.println("当前线程为:"+Thread.currentThread().getName()+",num="+(number++));
        }
    }

}

当使用线程池进行操作时,可以分为2种情况。

1,共享某一种元素

2,不想共享某一个元素

代码如下:

1,

public class Test {
    public static void main(String[] args) {
        TestThread t = new TestThread(0);
        for(int i = 0;i<10;i++) {
            CommonThreadPool.getInstance().getFixedThreadPool().execute(t);
        }
    }
}

此种写法会有多线程问题,因为共享了一个元素

2,

public class Test {
    public static void main(String[] args) {
//        TestThread t = new TestThread(0);
        for(int i = 0;i<10;i++) {
            CommonThreadPool.getInstance().getFixedThreadPool().execute(new TestThread(0));
        }
    }
}

此种写法某种程度来说不会有多线程问题,因为new对象之后并没有共享同一个元素

如果想避免某一些问题,那么可以加锁看来控制,如:

public class TestThread implements Runnable{

    private int number = 0;

    public TestThread(int num) {
        number = num;
    }

    @Override
    public synchronized void run() {
        for(int i = 0;i<10;i++) {
            System.out.println("当前线程为:"+Thread.currentThread().getName()+",num="+(number++));
        }
    }

}

以上只是示例一下,因为加锁的地方是要看具体的情况的。

所以,最初的这段代码:

public class TestThread implements Runnable{

    private int number = 0;

    public TestThread(int num) {
        number = num;
    }

    @Override
    public void run() {
        for(int i = 0;i<10;i++) {
            System.out.println("当前线程为:"+Thread.currentThread().getName()+",num="+(number++));
        }
    }

}

其实某种程度上来说并不安全,因为依赖于外部的调用方式。

 

<think>我们正在讨论Java中继承抽象重写方法与实现接口重写方法的区别。 根据引用内容: [1] 抽象不能实例化,必须有子继承。抽象不一定含有抽象方法,但拥有抽象方法的一定是抽象。抽象方法只有声明没有方法体,子必须重写实现。 [2] 展示了一个子继承抽象并重写构造方法和抽象方法的例子。 [3] 提到抽象是对一系列具体概念的抽象,不能生成对象。并提到实现接口时,必须重写接口中的所有方法吗?这个引用没有直接回答,但我们可以结合知识补充。 我们需要区分: 1. 继承抽象重写方法: - 抽象可以包含普通方法和抽象方法。 - 子必须重写父中所有的抽象方法(除非子也是抽象)。 - 子可以选择是否重写父的非抽象方法(即普通方法)。 2. 实现接口重写方法: - 接口中的方法默认都是抽象的(在Java 8之前,接口中只能有抽象方法;从Java 8开始,接口可以有默认方法和静态方法,但这里我们讨论抽象方法)。 - 实现接口必须重写接口中所有的抽象方法(除非这个是抽象)。 - 从Java 8开始,接口可以有默认方法(default method),默认方法可以被重写,也可以不重写(直接使用默认实现)。 另外,还需要注意: - 抽象只能单继承,而接口可以多实现。 - 抽象可以包含成员变量、构造方法、普通方法、抽象方法等,而接口在Java 8之前只能有常量和抽象方法(现在可以有默认方法、静态方法、私有方法)。 - 接口强调功能的实现,具有“契约式”的语义;抽象强调所属关系,即子属于抽象的一个具体别。 因此,区别总结如下: 1. 继承关系:一个只能继承一个抽象,但可以实现多个接口。 2. 方法重写要求: - 对于抽象:子必须重写所有抽象方法,非抽象方法可选择性重写。 - 对于接口实现必须重写所有抽象方法(默认方法可以不重写)。 3. 设计层面:抽象是对的抽象,是“is-a”关系;接口是对行为的抽象,是“has-a”关系(即具备某种能力)。 我们还可以举一个简单的例子: 抽象示例: ```java abstract class Animal { // 抽象方法 public abstract void makeSound(); // 普通方法 public void sleep() { System.out.println("This animal sleeps"); } } class Dog extends Animal { @Override public void makeSound() { System.out.println("Bark bark"); } // sleep()方法可以不重写 } ``` 接口示例: ```java interface SoundMaker { void makeSound(); // 抽象方法 default void stopSound() { // 默认方法 System.out.println("Stop making sound"); } } class Cat implements SoundMaker { @Override public void makeSound() { System.out.println("Meow meow"); } // stopSound()方法可以不重写,因为它是默认方法 } ``` 注意:在Java中,如果一个同时继承了一个抽象实现了一个接口,且它们有相同的方法声明,那么只需要重写一次这个方法即可(同时满足两者的要求)。 另外,引用[3]中提到了一个实现接口是否必须重写所有方法?答案是:如果这个是非抽象,那么它必须重写接口中所有的抽象方法(但不包括默认方法和静态方法)。如果这个是抽象,那么它可以不重写,留给它的子去重写。 因此,区别的核心点在于:继承抽象时,重写方法的要求只针对抽象方法,而实现接口时(在考虑Java 8及以后),重写方法的要求只针对抽象方法(不包括默认方法和静态方法)。 最后,我们还要注意构造方法: 抽象可以有构造方法(但不能被实例化,其构造方法用于被子调用),而接口不能有构造方法。 根据引用[2]的例子,子在继承抽象时,可以通过super调用抽象的构造方法,用于初始化抽象中定义的属性。 </think>### Java中继承抽象重写方法与实现接口重写方法的区别 在Java中,通过**继承抽象**和**实现接口**重写方法存在以下核心区别: --- #### 1. **设计目的不同** - **抽象** 表示"是什么",用于建立**层级继承关系**(如`Animal→Dog`)。抽象可包含: - 成员变量 - 构造方法(需子通过`super()`调用[^2]) - 具体方法(可选择是否重写) - 抽象方法(强制子重写) ```java abstract class Animal { protected String name; public Animal(String name) { this.name = name; } // 构造方法 public void sleep() { System.out.println("Sleeping"); } // 可重写 public abstract void makeSound(); // 必须重写 } ``` - **接口** 表示"能做什么",用于定义**行为契约**(如`Flyable`)。接口主要包含: - 抽象方法(Java 8前) - 默认方法(`default`,可不重写) - 静态方法 ```java interface Flyable { void fly(); // 必须重写 default void land() { System.out.println("Landing"); } // 可不重写 } ``` --- #### 2. **方法重写要求** | **特性** | 抽象 | 接口 | |------------------------|---------------------------------------------|-------------------------------| | **抽象方法** | 子必须重写所有抽象方法(除非子也是抽象) | 实现必须重写所有抽象方法 | | **非抽象方法** | 子可选择是否重写 | 无具体方法(Java 8前) | | **默认方法** | 不存在 | 实现可选择是否重写`default`方法 | | **构造方法** | 子需通过`super()`调用父构造方法[^2] | 接口无构造方法 | **示例对比**: ```java // 继承抽象:必须重写抽象方法,可选重写普通方法 class Dog extends Animal { public Dog(String name) { super(name); } // 调用父构造器[^2] @Override public void makeSound() { System.out.println("Woof!"); } // 强制重写 } // 实现接口:必须重写抽象方法 class Bird implements Flyable { @Override public void fly() { System.out.println("Flying with wings"); } // 强制重写 } ``` --- #### 3. **继承与实现机制** - **继承数量** - 抽象:单继承(一个只能继承**一个**抽象)。 - 接口:多实现(一个实现**多个**接口)。 ```java class Robot extends Machine implements Runnable, Speakable { ... } ``` - **成员变量** - 抽象:可包含非静态成员变量。 - 接口:变量默认为`public static final`(常量)。 --- #### 4. **使用场景** - **选择抽象** 当多个子有**共享的状态和行为**时(如公共字段/方法)。 - **选择接口** 当需要定义**跨继承树的通用能力**时(如序列化`Serializable`)。 --- #### 5. **版本演进差异** - **Java 8+**: - 接口引入`default`方法,允许提供默认实现。 - 抽象仍保持完整能力(构造器/字段/私有方法等)。 ```java interface Smart { default void connectWifi() { // 可不重写 System.out.println("Connected to Wi-Fi"); } } ``` ### 关键总结 | **维度** | 抽象 | 接口 | |------------------|---------------------------------------------|--------------------------| | 继承关系 | 单继承 | 多实现 | | 方法重写强制要求 | 仅抽象方法 | 所有抽象方法 | | 构造方法 | 支持(子需调用`super()`)[^2] | 不支持 | | 核心设计目标 | 代码复用 + 多态 | 行为抽象 + 解耦 | | 适用场景 | 同事物的基础模板(如"车辆"抽象) | 跨别的能力(如"可飞行") | > 抽象定义**是什么**,接口定义**能做什么**[^1][^3]。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值