学了Runnable之后随手写的一个买车票的例子
package com.process;
class MyRunnable implements Runnable{
    private int ticket=10;
    private final String name;
    public MyRunnable(String name) {
        this.name = name;
    }
    @Override
    public void run()
    {
        while(ticket>0)
        {
            System.out.println(name + "买了第" + ticket-- + "张票");
        }
    }
}
public class TestSleep 
{
    public static void main(String[] args) 
    {
        MyRunnable r1=new MyRunnable("小明");
        MyRunnable r2=new MyRunnable("老师");
        Thread t1=new Thread(r1);
        Thread t2=new Thread(r2);
        t1.start();
        t2.start();
    }
}
运行之后的结果
老师买了第10张票
老师买了第9张票
老师买了第8张票
老师买了第7张票
老师买了第6张票
老师买了第5张票
老师买了第4张票
老师买了第3张票
老师买了第2张票
老师买了第1张票
小明买了第10张票
小明买了第9张票
小明买了第8张票
小明买了第7张票
小明买了第6张票
小明买了第5张票
小明买了第4张票
小明买了第3张票
小明买了第2张票
小明买了第1张票
发现有点问题,我需要他们共享10张票,而不是分别各自买10张票(这样没有实际意义)。
突然我想到了用static关键字的用法
private static int ticket = 10; // 添加 static
- 作用:使变量属于类而非实例
- 
特点:
- 所有实例共享同一个变量
- 内存中只存在一份拷贝
- 可以通过类名直接访问(MyRunnable.ticket)
 
- 当前代码问题:
  MyRunnable r1 = new MyRunnable("小明");
  MyRunnable r2 = new MyRunnable("老师");
每个实例有自己的 ticket 副本(各卖10张票),这不是真正的共享资源
修改方案:实现票源共享
  
  
  方案A:使用 static 共享票源(不推荐)
class MyRunnable implements Runnable {
    private static int ticket = 10; // 静态共享
    private final String name;
    // ... 其他不变 ...
}
效果:
- 所有线程共享同一个 ticket
- 输出可能:
  小明买了第10张票
  老师买了第9张票
  小明买了第8张票
  ...
问题:
- 需要同步机制(如 synchronized)避免线程安全问题
- 全局状态难以管理
- 不符合面向对象封装原则
方案B:共享实例(推荐)
public class TestSleep {
    public static void main(String[] args) {
        MyRunnable shared = new MyRunnable("售票点");
        Thread t1 = new Thread(shared, "小明");
        Thread t2 = new Thread(shared, "老师");
        t1.start();
        t2.start();
    }
}
修改点:
- 创建 单个 MyRunnable实例
- 通过 Thread的第二个参数设置线程名称
- 修改 run()方法:
   public void run() {
       while (ticket > 0) {
           // 获取当前线程名称
           String buyer = Thread.currentThread().getName();
           System.out.println(buyer + "买了第" + ticket-- + "张票");
       }
   }
最佳实践方案(完整代码)
class MyRunnable implements Runnable {
    private int ticket = 10; // 实例变量(多个线程共享同一实例时自然共享)
    @Override
    public void run() {
        while (true) {
            synchronized (this) { // 添加同步锁
                if (ticket <= 0) break;
                String buyer = Thread.currentThread().getName();
                System.out.println(buyer + "买了第" + ticket-- + "张票");
            }
            try {
                Thread.sleep(100); // 模拟处理时间
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
public class TestSleep {
    public static void main(String[] args) {
        MyRunnable shared = new MyRunnable();
        new Thread(shared, "小明").start();
        new Thread(shared, "老师").start();
    }
}
关键改进:
- 使用 单个Runnable实例 实现资源自然共享
- 通过 synchronized保证线程安全
- 使用 Thread.currentThread().getName()获取线程名称
- 添加 Thread.sleep()模拟真实场景
- 移除非必要的 name字段(用线程名替代)
输出示例:
小明买了第10张票
老师买了第9张票
小明买了第8张票
老师买了第7张票
...
小明买了第1张票
总结理解
- 
final:用于创建"常量"(不可变变量),保证安全性和代码意图清晰- 例:private final String ID = "A100";
 
- 例:
- 
static:用于创建类级别共享资源- 例:public static final Logger LOG = Logger.getGlobal();
- 常与 final组合定义全局常量:public static final double PI = 3.14159;
 
- 例:
- 
多线程资源共享原则: - 优先使用 实例共享 而非 static变量
- 共享资源必须添加 同步机制
- 
final变量天然线程安全,无需同步
 
- 优先使用 实例共享 而非 
  
  
  附:static vs final 对比表
| 特性 | static | final | 
|---|---|---|
| 作用对象 | 类级别 | 变量/方法/类级别 | 
| 内存 | 类加载时初始化,仅一份 | 与普通变量相同 | 
| 修改性 | 可修改(需同步) | 初始化后不可修改 | 
| 共享性 | 所有实例共享 | 每个实例独立 | 
| 典型用途 | 全局配置、常量池、工具方法 | 不可变参数、常量定义 | 
| 线程安全 | 需要额外同步 | 本身提供安全发布 | 
 

 
    
Top comments (0)