RError.com

RError.com Logo RError.com Logo

RError.com Navigation

  • 主页

Mobile menu

Close
  • 主页
  • 系统&网络
    • 热门问题
    • 最新问题
    • 标签
  • Ubuntu
    • 热门问题
    • 最新问题
    • 标签
  • 帮助
主页 / 问题 / 754797
Accepted
Vaagn Akopyan
Vaagn Akopyan
Asked:2020-12-07 01:36:29 +0000 UTC2020-12-07 01:36:29 +0000 UTC 2020-12-07 01:36:29 +0000 UTC

java中的多线程,为什么结果的输出顺序不一样?

  • 772

假设有这样的代码。它的结果是:[Synchronization] [in Java] [useful]。如果对象Caller在没有单独线程的情况下运行(即没有extends Thread和没有方法start()),那么结果将采用不同的顺序:[同步] [有用] [在 Java 中]。为什么会这样?请给出详细的答复。

class CallMe {
    void call(String msg) {
        System.out.print("[" + msg );
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("]");
    }
}
class Caller extends Thread {
    String msg;
    CallMe target;

    Caller(CallMe target, String msg) {
        this.target = target;
        this.msg = msg;
        start();
    }

    public void run() {
        synchronized (target) {
            target.call(msg);
        }
    }
}
public class Main {
    public static void main(String[] args) {
        CallMe callMe = new CallMe();
        new Caller(callMe, "Синхронизация");
        new Caller(callMe, "в Java");
        new Caller(callMe, " полезная");
    }
}
java
  • 1 1 个回答
  • 10 Views

1 个回答

  • Voted
  1. Best Answer
    user236980
    2020-12-07T03:04:20Z2020-12-07T03:04:20Z

    在方法中设置 200 毫秒的延迟run(),并在方法中main()添加 100 毫秒的循环 - 您可以获得更多选择。

    因为无法预测哪个线程将synchronized首先进入块,即使您按顺序运行它们也是如此。

    类用于同步线程

    • 信号
    • 倒计时锁存器
    • 循环障碍
    • 锁

    他们有不同的目标和方法。这取决于具体的任务。


    信号量- 通常用于限制使用资源时的线程数。使用计数器限制访问,如果其值大于零,则允许访问线程,并且计数器值递减。如果计数器为零,则当前线程被阻塞,直到另一个线程释放资源。为了获得访问权限,该方法用于acquire()释放 - release()。

    public class SemaphoreDemo {
        public static void main(String[] args) {
            Semaphore smp = new Semaphore(2);
            for (int i = 0; i < 5; i++) {
                final int w = i;
                new Thread(() -> {
                    try {
                        System.out.println("Поток" + w + " перед семафором");
                        smp.acquire();
                        System.out.println("Поток" + w + " получил доступ к ресурсу");
                        Thread.sleep(500);
                    } catch (InterruptedException e) {
                            e.printStackTrace();
                    } finally {
                        System.out.println("Поток" + w + " освободил ресурс");
                        smp.release();
                    }
                }).start();
            }
        }
    }
    

    工作成果:

    Поток 0 перед семафором
    Поток 0 получил доступ к ресурсу
    Поток 2 перед семафором
    Поток 2 получил доступ к ресурсу
    Поток 1 перед семафором
    Поток 4 перед семафором
    Поток 3 перед семафором
    Поток 2 освободил ресурс
    Поток 1 получил доступ к ресурсу
    Поток 0 освободил ресурс
    Поток 4 получил доступ к ресурсу
    Поток 4 освободил ресурс
    Поток 1 освободил ресурс
    Поток 3 получил доступ к ресурсу
    Поток 3 освободил ресурс
    

    同时,只有两个线程可以捕获信号量(使用acquire())方法,其余线程排队,直到其中一个线程使用release().


    CountDownLatch - 允许线程等待,直到在其他线程中执行的一定数量的操作完成后,线程使用await(). 所需操作的数量在创建对象时设置,之后在调用方法时减少countDown()。一旦计数器达到 0,等待线程就被解除阻塞。

    public class SimpleCDL {
        public static void main(String[] args) {
            // задаем кол-во потоков
            final int THREADS_COUNT = 6;
            // задаем значение счетчика
            final CountDownLatch cdl = new CountDownLatch(THREADS_COUNT);
            System.out.println("Начинаем");
            for (int i = 0; i < THREADS_COUNT; i++) {
                final int w = i;
                new Thread(() -> {
                    try {
                        // считаем что выполнение задачи занимает ~1 сек
                        Thread.sleep(500 + (int)(500 * Math.random()));
                        // как только задача выполнена, уменьшаем счетчик
                        cdl.countDown();
                        System.out.println("Поток #" + w + " - готов");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }).start();
            }
            try {
                // ждем пока счетчик не сбросится в ноль, пока это не
                // произойдет, будем стоять на этой строке
                cdl.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            // как только все потоки выполнили свои задачи - пишем сообщение
            System.out.println("Работа завершена");
        }
    }
    

    工作成果:

    Начинаем
    Поток #1 - готов
    Поток #0 - готов
    Поток #3 - готов
    Поток #2 - готов
    Поток #4 - готов
    Поток #5 - готов
    Работа завершена
    

    主线程创建 6 个线程并等待每个线程完成准备工作。


    CyclicBarrier - 用于在某一点同步给定数量的线程。当方法被调用时,await()线程被阻塞。一旦指定数量的线程被阻塞,锁就会同时从它们中释放。

    public class BarrierExample {
        public static void main(String[] args) {
            CyclicBarrier cb = new CyclicBarrier(3);
            for (int i = 0; i < 3; i++) {
                final int w = i;
                new Thread(() -> {
                    try {
                        System.out.println("Поток " + w + " готовится");
                        Thread.sleep(100 + (int) (3000 * Math.random()));
                        System.out.println("Поток " + w + " готов");
                        cb.await();
                        System.out.println("Поток " + w + " запустился");
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }).start();
            }
        }
    }
    

    工作成果:

    Поток 0 готовится
    Поток 1 готовится
    Поток 2 готовится
    Поток 2 готов
    Поток 0 готов
    Поток 1 готов
    Поток 1 запустился
    Поток 2 запустился
    Поток 0 запустился
    

    尽管有些线程较早完成准备,有些较晚,但它们同时启动,因为锁是同时释放的。


    锁定- 界面。它是一种高级线程同步机制,提供了比同步块更大的灵活性。由于Lock这是一个接口,要使用它,您必须创建其实现之一的对象。

    Lock lock = new ReentrantLock();
    lock.lock();
    lock.unlock();
    

    首先,创建一个类型的对象Lock,然后对该对象调用一个方法lock()并将其捕获。另一个线程尝试调用同一对象上的方法lock()将导致该线程阻塞,直到持有该类型对象的线程Lock使用unlock(). 方法调用后,unlock()类型对象被释放,其他线程可以抓取,和同步块Lock的主要区别是:Lock

    • 同步块不保证保持线程访问临界区的顺序;

    • 没有办法通过超时退出同步块;

    • 同步块必须完全包含在一种方法中,而它们Lock可以在一种方法中捕获并在另一种方法中释放。

    • 14

相关问题

Sidebar

Stats

  • 问题 10021
  • Answers 30001
  • 最佳答案 8000
  • 用户 6900
  • 常问
  • 回答
  • Marko Smith

    Python 3.6 - 安装 MySQL (Windows)

    • 1 个回答
  • Marko Smith

    C++ 编写程序“计算单个岛屿”。填充一个二维数组 12x12 0 和 1

    • 2 个回答
  • Marko Smith

    返回指针的函数

    • 1 个回答
  • Marko Smith

    我使用 django 管理面板添加图像,但它没有显示

    • 1 个回答
  • Marko Smith

    这些条目是什么意思,它们的完整等效项是什么样的

    • 2 个回答
  • Marko Smith

    浏览器仍然缓存文件数据

    • 1 个回答
  • Marko Smith

    在 Excel VBA 中激活工作表的问题

    • 3 个回答
  • Marko Smith

    为什么内置类型中包含复数而小数不包含?

    • 2 个回答
  • Marko Smith

    获得唯一途径

    • 3 个回答
  • Marko Smith

    告诉我一个像幻灯片一样创建滚动的库

    • 1 个回答
  • Martin Hope
    Air 究竟是什么标识了网站访问者? 2020-11-03 15:49:20 +0000 UTC
  • Martin Hope
    Алексей Шиманский 如何以及通过什么方式来查找 Javascript 代码中的错误? 2020-08-03 00:21:37 +0000 UTC
  • Martin Hope
    Qwertiy 号码显示 9223372036854775807 2020-07-11 18:16:49 +0000 UTC
  • Martin Hope
    user216109 如何为黑客设下陷阱,或充分击退攻击? 2020-05-10 02:22:52 +0000 UTC
  • Martin Hope
    Qwertiy 并变成3个无穷大 2020-11-06 07:15:57 +0000 UTC
  • Martin Hope
    koks_rs 什么是样板代码? 2020-10-27 15:43:19 +0000 UTC
  • Martin Hope
    user207618 Codegolf——组合选择算法的实现 2020-10-23 18:46:29 +0000 UTC
  • Martin Hope
    Sirop4ik 向 git 提交发布的正确方法是什么? 2020-10-05 00:02:00 +0000 UTC
  • Martin Hope
    faoxis 为什么在这么多示例中函数都称为 foo? 2020-08-15 04:42:49 +0000 UTC
  • Martin Hope
    Pavel Mayorov 如何从事件或回调函数中返回值?或者至少等他们完成。 2020-08-11 16:49:28 +0000 UTC

热门标签

javascript python java php c# c++ html android jquery mysql

Explore

  • 主页
  • 问题
    • 热门问题
    • 最新问题
  • 标签
  • 帮助

Footer

RError.com

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

帮助

© 2023 RError.com All Rights Reserve   沪ICP备12040472号-5