java中啥是友好变量和友好方法
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java中啥是友好变量和友好方法相关的知识,希望对你有一定的参考价值。
不用private、public、protected修饰符的成员变量和方法被称为友好变量和友好方法。 参考技术A 没有,Java中访问类的私有成员变量都使用public的getter方法访问;或者使用内部类访问类的成员变量与方法;或者将成员变量或方法用protected修饰,再使用继承的方式由子类继承,达到访问的目的。 参考技术B 所谓友好即 不被public、protected、private关键修饰的变量和方法区别在于 比public低一级 用于只能在同一包下访问的变量本回答被提问者采纳 参考技术C 不被public、protected、private修饰的变量和方法。 参考技术D 就是用friend关键字修饰的变量或者方法。
不过基本不用,常用的都是public和private。
Java 中啥是快速、等待通知或忙等待?
【中文标题】Java 中啥是快速、等待通知或忙等待?【英文标题】:What is fast, wait notify or busy wait in Java?Java 中什么是快速、等待通知或忙等待? 【发布时间】:2014-07-25 05:43:12 【问题描述】:我知道使用忙等待不是一个好的编程习惯,最好尽可能使用同步对象(等待通知)。但我想知道是否准备好牺牲 cpu 周期,那么忙等待会更快还是等待通知?
我假设等待通知将涉及对同步对象的内在锁定,并且信号可能来自内核以唤醒线程,这使得这种方法比忙碌等待慢得多,在这种方法中,人们可以连续检查一个条件直到满意为止。一旦满足此条件(例如布尔值 == true),线程可能会退出忙等待。据我了解,我觉得忙等待应该更快。
如果我的论点有错误,如果其他人能分享他们的想法并纠正我,我将不胜感激。
【问题讨论】:
我可能错了,但是如果强制CPU循环,会不会比允许CPU空闲时慢? 我无法相信在现代系统中,你不是在写“金属”,而是在操作系统上运行并通过驱动程序,谁知道有多少操作系统代码等待 IO,那忙等待会比阻塞快得多。但是,嘿,如果有疑问,请尝试两种方法并衡量结果! 写一个实验真的比写一个问题要花更长的时间吗?我怀疑你会发现一个快速的实验让你毫无疑问地确定正确的路线。 使用wait()/notify()
将是有利的,因为一旦您notify()
,(其中一个)等待线程就会收到通知并开始执行。即,调用notify()
的线程将不会继续。如果您忙于等待,即使第二个线程设置了第一个线程正在等待的布尔标志,第二个线程仍会执行,直到其时间片完成然后第一个线程启动。 @其他人,如果我错了,请纠正我..
@TheLostMind notify()
不会导致调用线程挂起,也不会导致等待线程立即唤醒。相反,被通知线程仍然必须等待通知线程首先离开同步块。关于您的时间片问题:在多核系统中,线程可能真的并行运行!所以你的说法并不完全正确。
【参考方案1】:
实验表明,如果您忙于等待,那么您会比等待并通知(无论如何在我的硬件上)更快地看到标志。 (详情如下。)差异是非常非常非常非常小,因此这仅适用于非常罕见的应用程序。例如,股票交易应用程序,如果公司追求他们可以获得的任何优势(争相将他们的服务器放置在尽可能靠近交易所的地方,以便从交易所获得其网络馈送的微秒级改进等)可能会认为这种差异是值得的。我也可以想象一些科学应用。
在绝大多数应用程序中,差异实际上根本没有差异。
但是发生在 CPU 上的当然是其中一个核心硬钉:
就影响机箱上的其他进程和数据中心的功耗而言,这很糟糕。
所以:非常不情愿地使用,仅在真正重要的情况下使用。
数据(非常小的样本,但代码如下):
忙等待:10631 12350 15278 等待并通知:87299 120964 107204 达美:76668 108614 91926时间以 nano秒为单位。十亿分之一秒。上面的平均增量为 92403ns(0.092402667 毫秒,0.000092403 秒)。
BusyWait.java
:
public class BusyWait
private static class Shared
public long setAt;
public long seenAt;
public volatile boolean flag = false;
public static void main(String[] args)
final Shared shared = new Shared();
Thread notifier = new Thread(new Runnable()
public void run()
System.out.println("Running");
try
Thread.sleep(500);
System.out.println("Setting flag");
shared.setAt = System.nanoTime();
shared.flag = true;
catch (Exception e)
);
notifier.start();
while (!shared.flag)
shared.seenAt = System.nanoTime();
System.out.println("Delay between set and seen: " + (shared.seenAt - shared.setAt));
WaitAndNotify.java
:
public class WaitAndNotify
private static class Shared
public long setAt;
public long seenAt;
public boolean flag = false;
public static void main(String[] args)
(new WaitAndNotify()).test();
private void test()
final Shared shared = new Shared();
final WaitAndNotify instance = this;
Thread notifier = new Thread(new Runnable()
public void run()
System.out.println("Running");
try
Thread.sleep(500);
System.out.println("Setting flag");
shared.setAt = System.nanoTime();
shared.flag = true;
synchronized (instance)
instance.notify();
catch (Exception e)
);
notifier.start();
while (!shared.flag)
try
synchronized (this)
wait();
catch (InterruptedException ie)
shared.seenAt = System.nanoTime();
System.out.println("Delay between set and seen: " + (shared.seenAt - shared.setAt));
【讨论】:
while 循环通常不应该在同步块中,而不是相反吗? @isnot2bad:这取决于你在做什么。通常的规则是尽可能少地同步。在这种情况下,这意味着在wait
和 notify
调用附近。当然,理论上,我们从来没有真正循环(因为这需要我们的升旗代码之外的其他东西执行notify
),我们只是进入while
的主体一次,然后从不重复,所以对于这个测试代码来说,无论哪种方式都是一样的。
@T.J.Crowder 你是对的。另一件事是您正在修改同步块之外的共享标志。 (我知道它适用于您的情况,因为以下同步块确保 happens-before 关系,但仍然......)。顺便提一句。我稍微修改了您的代码并在循环中调用它以进行 JVM 预热,从而大大加快了繁忙等待示例的速度。
如果预期的等待时间长于线程的剩余时间片,那么旋转的任何优势都会完全消失。如果线程被换出,则首选等待/通知。
@JimMischel:是的,我可以发誓 OP 会说假设有足够的内核,但我现在看不到。我刚刚在我的四核机器上做了一个 20 秒的测试,该机器有三个虚拟机和运行在它上面的各种其他东西(Linux)。数字与我上面的半秒测试一致。但是我对时间片等的了解很少,并且其他核心没有远程加载,所以我猜线程被允许保留核心。【参考方案2】:
一个准备好在繁忙的等待中牺牲 CPU 周期,因为它更快。 忙等待是实时低延迟应用程序的示例。
有一个名为lmax disruptor 的框架是为伦敦证券交易所构建的,其中一个锁定策略是忙等待,这就是他们使用它的方式。
为了超快,最好在通知您的锁时浪费 cpu 周期来节省时间。
你对所有其他的东西都是正确的,如果你用谷歌搜索一下disruptor 并阅读他们的论文,你会得到更多的澄清。关于高性能和低延迟有太多话要说。
一个不错的博客是Mechanical Sympathy。
【讨论】:
【参考方案3】:视情况而定。有几种情况:
场景 A
如果在'busy-wait'中等待的是硬件操作(例如,从硬盘读取一个扇区到内存):
1) 硬件将执行操作。
2) 驱动程序将启动中断。
3) 操作将暂停实际进程(您的忙等待进程),保存任何 CPU 寄存器的实际值,中断将在其处理中覆盖。
4) 中断将被处理,修改任何指示数据可用的标志(在磁盘读取的情况下)。
5) 任何被覆盖的寄存器都将被恢复。
6) 您的流程将继续其流程。就在下一次迭代中,它将调用循环的 conthe 条件。例如,如果忙等待是:
while( !fileReady() )
...
fileReady() 方法将是一种在内部检查是否设置了具体标志(在 4 中修改的标志)的方法。 7)所以就在下一次迭代中,循环将进入并执行操作。
请记住,如果有另一个进程正在运行(操作系统进程、其他程序),它们会将您的进程置于进程尾部。此外,操作系统可以决定,鉴于您的进程已经使用了他可以使用的所有 CPU 周期(它花费了它的时间片),它的优先级将低于其他进入睡眠状态(而不是使用忙等待方法)的进程。需要等待某个条件。
结论。如果没有其他外部进程在同一个内核/CPU 中运行(非常不可能),则速度会更快。
场景 B
另一方面,如果busy-method正在等待另一个进程结束(或将任何变量设置为某个值,busy-wait会变慢。
1)busy-method 将在 CPU 中运行。由于其他进程没有运行,条件不能改变,所以busy-method会一直运行,直到CPU决定把CPU时间给其他进程。
2) 另一个进程将运行。如果这个过程花费的时间没有达到busy-wait过程需要的条件,则执行1),否则继续执行3)
3) 另一个(不是忙等待)进程仍会运行一段时间,直到 cpu 决定新的进程更改。
4)busy-method 会再次运行,但是现在条件已经满足,所以操作已经完成了。
结论:它比较慢,我们也拖慢了整个过程。
场景 C
如果我们有与 B 相同的场景,但有多个内核(每个内核中有一个进程)怎么办?
首先,请记住,即使您的 CPU 具有多个内核,也可能不允许您的程序使用多个内核。也许有一些操作系统或其他程序在使用它们。
其次,它不值这个价。请记住,进程必须进行通信以允许忙等待发现条件满足。这通常可以通过“最终”变量来完成,因此每次评估条件时都需要在同步块中输入(在进入循环之前不能锁定并且不能解锁它,因为在这种情况下另一个进程将无法更改变量。所以你需要这样的东西:
boolean exit = false;
while( exit==false )
synchronize(var)
if(var = CONDITIONS_MEET)
exit=true;
//operations...
¡¡ 但是等待通知会做一些类似的事情,并且更有效(在语言级别),不会浪费 CPU 周期,并使用良好的规则!!
结论:您正在使您的生活复杂化,而这不太可能会更快(非常非常不可能)。
最终结论:只有在非常非常简单的场景中,知道操作系统的具体细节和程序运行的环境,才可以考虑busy-wait方法.
我希望这将回答您的问题。如果有不清楚的地方,请不要犹豫。
【讨论】:
【参考方案4】:忙碌等待比正常等待通知更快。
但是为什么要等呢?因为一个生产者或其他线程会做一些工作,然后设置一个条件(或通知),这样你就可以真正摆脱忙/等待循环。 现在假设如果您的 Producer 正在执行一些繁重的任务,那么您实际上会通过忙等待来消耗它的 CPU 周期(主要是在单处理器系统中),这反过来可能会使您的系统整体变慢。
所以现在应该使用忙等待。 正如克劳迪奥所说,它主要用于低延迟系统。 但仍不能盲目使用。制作人时使用忙等待 正在以稳定的速度生产。 如果您的生产者以可变速率生产项目(通常由泊松分布证明),那么您可能应该使用等待通知。
通常情况下,高吞吐量和低延迟系统的最佳权衡是使用 忙等待一段时间,然后转到 wait()。 如果您的系统需要超低延迟,那么您可以进行许多优化 其中之一可能是忙碌等待。 但不应该是每个线程都在忙等待。确保只有一些消费者 可能在 N/2 左右 消费者正在忙着等待 N 是你的内核数 系统。浪费 CPU 周期可能会影响系统的整体性能和响应能力。 供您参考:即使是 Normal ReentrantLock 及其变体也应用这些策略。 IE 即 当一个线程调用 lock.lock() 时,它会尝试两次获取锁,然后再进入队列并等待锁被释放。对于低延迟系统,您甚至可以为特定场景定义自己的锁,在这些场景中它们会在进入队列之前尝试超过 10 次(它们将是所谓的自旋锁的变体)
【讨论】:
以上是关于java中啥是友好变量和友好方法的主要内容,如果未能解决你的问题,请参考以下文章