多线程的问题,我也不知道怎么描述

蒋一清 2023-08-19 10:56:17

定义三个窗口的线程,然后卖共同的100张票,为了不让一直一个窗口买票,使用同步代码块

public class ticket extends Thread {
    //定义起始票
    static int ticket = 0;
 

    //加上一个静态关键字,就会使这个类所有的对象共享这个数据,
    @Override
    public  void run() {
        while (true) {
            //要保证锁对象是唯一的
            synchronized (ticket.class) {
                if (ticket < 100) {
                    ticket++;
                    System.out.println(getName() + "在买第" + ticket + "票");
                } else {
                    break;
                }
            }
            /*try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }*/
        }
    }
}

 

测试类: 

public class test {
    public static void main(String[] args) {
        //创建线程对象
        ticket t1=new ticket();
        ticket t2=new ticket();
        ticket t3=new ticket();


        t1.setName("窗口1");
        t2.setName("窗口2");
        t3.setName("窗口3");
        t2.start();
        t1.start();
        t3.start();
    }
}

刚开始我调换start方法的调用顺序,发现一直是一个窗口在卖票,好家伙嘛!不累嘛,我调换到第一个的窗口二,合着谁第一个调用start方法谁先卖,而且是一直卖到票卖买完!真离谱啊,

        一开始我想着机缘巧合,就多运行了几次,很多次下来确实有一次两次的例外,然后我就搞不明白为什么,明明将同步代码块写在循环的里面,为什么还会出现这种一条路走到黑的情况!

运行截图:

 

真的就很离谱!而且我在前面,实现改变线程的优先级的时候,两个线程就是很规律的一个一个交替,你一个我一个,我明明设置一个优先级为10,一个优先级为1,虽然说有优先级只能说只能提高抢占CPU的概率,但是这种一个一个相互交替也太离谱了!

然后我就想这让线程执行完同步代码块后睡一会,看看其他线程怎么说,会不会抢cpu然后执行同步代码块,哎有意思,然后他们三个窗口就开始都买票了!

 

因为我刚开始学什么都不懂,就特别好奇这种现象和什么有关,有大佬的话希望可以解释一下! 

...全文
201 3 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
3 条回复
切换为时间正序
请发表友善的回复…
发表回复
命运之光 修行初阶 2023-08-21
  • 打赏
  • 举报
回复 1

你遇到的现象涉及到多线程并发执行的问题,涉及到线程调度、竞争条件以及操作系统的调度算法等概念。我会逐步解释你的疑问。

首先,你的代码中的同步块使用了类对象 ticket.class 作为锁。这意味着所有线程都在竞争同一个锁,即类对象 ticket.class。只有一个线程能够获得锁,执行同步块中的代码,而其他线程会在锁被释放之前等待。这确保了在同一时刻只有一个线程能够买票。

然而,你在测试类中创建了三个线程对象 t1、t2 和 t3,然后依次调用了它们的 start() 方法。线程的启动顺序是不确定的,取决于操作系统的线程调度算法。这就是为什么你会看到有时只有一个窗口在卖票的情况,因为可能第一个启动的线程率先获得了锁,而其他线程则在等待。

当你调换了线程的启动顺序,有时候会看到不同的结果,这是因为启动顺序的不同可能导致某个线程率先获得了锁。但仍然会存在不确定性。

关于线程的优先级,你提到的情况也是正常的。虽然你设置了线程的优先级,但线程调度的具体实现在不同的操作系统和Java虚拟机中可能会有所不同。优先级只是一个指示,不一定会严格按照优先级的顺序执行。

关于让线程执行完同步代码块后睡一会儿的情况,这实际上会让其他线程有机会竞争获得锁并执行同步代码块。在你的实验中,这是有效的一种方式来演示多个窗口一起卖票。

总的来说,多线程编程涉及到许多复杂的问题,包括竞争条件、线程调度、锁机制等。为了更好地控制线程执行的顺序和并发情况,你可以尝试使用更高级的并发工具,如 java.util.concurrent 包中提供的类,来实现更精细的线程协作和同步。

命运之光 修行初阶 2023-08-21
  • 举报
回复 1
@命运之光 当时我们学校老师讲到多线程就跳过去了,所以也不是很懂,网上的答案可以参考一下。
mr.zhu0020 2023-08-19
  • 打赏
  • 举报
回复 1

在你的代码中,虽然将同步代码块写在了循环内部,但是问题出在了你创建了多个独立的ticket对象。每个对象都有自己的锁,因此每个线程在执行同步代码块时,只会锁住自己对象的锁,而不会锁住其他对象的锁。
因此,虽然你在run方法中使用了同步代码块来确保每次只有一个线程进入临界区,但是由于每个线程都在独立的对象上执行,所以锁住的是不同的对象锁,导致多个线程可以同时进入临界区。
要解决这个问题,你需要将票数变量定义为静态变量,这样所有的ticket对象都可以共享同一个变量。同时,在同步代码块中,使用一个共同的锁对象来确保只有一个线程可以进入临界区。
还有一种简单的方式把你的同步锁中的ticket.class对象直接换成一个字符串,字符串因为在堆得常量池里面储存,所以引用的对象地址是同一个地址,这样就保证了你的锁对象唯一,不会 你new 一下就出现一个新的锁对象,要么你就给你的锁对象加个static保证他静态共享也行!理解了吗?

51,929

社区成员

发帖
与我相关
我的任务
社区描述
CodeCrafters联盟是一个致力于汇聚热衷编程、技术和创新的专业人士的社区。我们的使命是为开发人员、工程师和技术爱好者提供一个共享知识、交流经验以及不断学习成长的平台。
学习java算法 个人社区 陕西省·西安市
社区管理员
  • 命运之光
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

1、这是命运之光(个人社区),欢迎您的加入;

2、发表帖子的时候请注意用语文明;

3、欢迎大家在遵守上一条的前提下积极发帖,多多交流;

4、点赞、评论都会使自己的积分上升的,欢迎大家积极参与;

5、如果有什么关于社区的疑问可以私信版主:命运之光

试试用AI创作助手写篇文章吧