创作赢红包喂,鬼仔,你还在瞒着我偷偷使用强制相等
Posted 前端修罗场
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了创作赢红包喂,鬼仔,你还在瞒着我偷偷使用强制相等相关的知识,希望对你有一定的参考价值。
我们都知道javascript有==
(强制相等)和===
(严格相等)运算符进行比较。但你可能不知道它们两个究竟有什么不同,并且更重要的是,在 js 引擎中使用它们的时候发生了什么?
前面我们提到 ==
是强制比较。强制意味着 VM 试图将进行比较的双方强制为相同的类型,然后查看它们是否相等。以下我们列举了一些自动被强制相等的例子:
"1" == 1 // true
1 == "1" // true
true == 1 // true
1 == true // true
[1] == 1 // true
1 == [1] // true
你要知道,强制是对称的,如果
a == b
为真,那么b == a
也为真。另一方面,只有当两个操作数完全相同时===
才为真(除了Number.NaN
)。因此,上面的例子都真实的情况下都是假真
(即,在===
的情况下是false
的)。
为什么强制相等有这样的问题,这要归咎与强制相等的规则。
强制相等的规则
实际的规则很复杂(这也是不使用==
的原因)。但是为了显示规则有多么复杂,我通过使用===
实现了==
,带大家看看强制相等的规则到底多复杂:
function doubleEqual(a, b)
if (typeof a === typeof b) return a === b;
if (wantsCoercion(a) && isCoercable(b))
b = b.valueOf();
else if (wantsCoercion(b) && isCoercable(a))
const temp = a.valueOf();
a = b;
b = temp;
if (a === b) return true;
switch (typeof a)
case "string":
if (b === true) return a === "1" || a === 1;
if (b === false) return a === "0" || a === 0 || a == "";
if (a === "" && b === 0) return true;
return a === String(b);
case "boolean":
if (a === true) return b === 1 || String(b) === "1";
else return b === false || String(b) === "0" || String(b) === "";
case "number":
if (a === 0 && b === false) return true;
if (a === 1 && b === true) return true;
return a === Number(String(b));
case "undefined":
return b === undefined || b === null;
case "object":
if (a === null) return b === null || b === undefined;
default:
return false;
function wantsCoercion(value)
const type = typeof value;
return type === "string" || type === "number" || type === "boolean";
function isCoercable(value)
return value !== null && typeof value == "object";
这是不是太复杂了,我甚至不确定这是正确的! 也许有你知道更简单的算法。
但有趣的是,你会发现在上面的算法中,如果其中一个操作数是对象,VM 将调用. valueof()
来允许对象将自身强制转换为基本类型。
强制转换的成本
上面的实现很复杂。那么==
比===
要多浪费多少性能呢? 看看下面这张图,我用基准测试做了一个对比:
其中,图表中越高表示越快(即,每秒操作次数越多)。
首先我们来讨论数字数组。当 VM 注意到数组是纯整数时,它将它们存储在一个称为PACKED_SMI_ELEMENTS
的特殊数组中。在这种情况下,VM 知道将 ==
处理为 ===
是安全的,性能是相同的。这解释了为什么在数字的情况下,==
和 ===
之间没有区别。但是,一旦数组中包含了数字以外的内容,==
的情况就变得很糟糕了。
对于字符串,==
比 ===
的性能下降了 50%,看起来挺糟的是吧。
字符串在VM中是特殊的,但一旦我们涉及到对象,我们就慢了 4 倍。看看 mix
这栏,现在速度减慢了 4 倍!
但还有更糟的。对象可以定义 valueOf
,这样在转换的时候可以将自己强制转换为原语。虽然在对象上定位属性可以通过内联缓存,内联缓存让属性读取变得快速,但在超大容量读取的情况下可能会经历 60 倍的减速,这可能会使情况更糟。如图中最坏情况(objectsMega
)场景所示,==
比===
慢15 倍!
有其他使用 ==
的理由吗
现在,===
非常快! 因此,即使是使用 ===
的15倍减速,在大多数应用程序中也不会有太大区别。尽管如此,我还是很难想出为什么要使用 ==
而不是 ===
的任何理由。强制规则很复杂,而且它存在一个性能瓶颈,所以在使用 ==
之前请三思。
创作赢红包Java多线程:synchronized锁方法块
synchronized同步代码块 用关键字synchronized声明方法在某些情况下是有弊端的,比如A线程调用同步方法执行一个较长时间的任务,那么B线程必须等待比较长的时间。这种情况下可以尝试使用synchronized同步语句块来解决问题。看一下例子: public class ThreadDomain18
public void doLongTimeTask() throws Exception
for (int i = 0; i < 100; i++)
System.out.println(
"nosynchronized threadName = " + Thread.currentThread().getName() + ", i = " + (i + 1));
System.out.println();
synchronized (this)
for (int i = 0; i < 100; i++)
System.out.println(
"synchronized threadName = " + Thread.currentThread().getName() + ", i = " + (i + 1));
public class MyThread18 extends Thread
private ThreadDomain18 td;
public MyThread18(ThreadDomain18 td)
this.td = td;
public void run()
try
td.doLongTimeTask();
catch (Exception e)
e.printStackTrace();
public static void main(String[] args)
ThreadDomain18 td = new ThreadDomain18();
MyThread18 mt0 = new MyThread18(td);
MyThread18 mt1 = new MyThread18(td);
mt0.start();
mt1.start();
运行结果,分两部分来看:
synchronized threadName = Thread-1, i = 1 synchronized threadName = Thread-1, i = 2 nosynchronized threadName = Thread-0, i = 95 synchronized threadName = Thread-1, i = 3 nosynchronized threadName = Thread-0, i = 96 synchronized threadName = Thread-1, i = 4 nosynchronized threadName = Thread-0, i = 97 synchronized threadName = Thread-1, i = 5 nosynchronized threadName = Thread-0, i = 98 synchronized threadName = Thread-1, i = 6 nosynchronized threadName = Thread-0, i = 99 synchronized threadName = Thread-1, i = 7 nosynchronized threadName = Thread-0, i = 100 ... synchronized threadName = Thread-1, i = 98 synchronized threadName = Thread-1, i = 99 synchronized threadName = Thread-1, i = 100 synchronized threadName = Thread-0, i = 1 synchronized threadName = Thread-0, i = 2 synchronized threadName = Thread-0, i = 3 ...这个实验可以得出以下两个结论: 1、 当A线程访问对象的synchronized代码块的时候,B线程依然可以访问对象方法中其余非synchronized块的部分,第一部分的执行结果证明了这一点 2、 当A线程进入对象的synchronized代码块的时候,B线程如果要访问这段synchronized块,那么访问将会被阻塞,第二部分的执行结果证明了这一点 所以,从执行效率的角度考虑,有时候我们未必要把整个方法都加上synchronized,而是可以采取synchronized块的方式,对会引起线程安全问题的那一部分代码进行synchronized就可以了。 两个synchronized块之间具有互斥性 如果线程1访问了一个对象A方法的synchronized块,那么线程B对同一对象B方法的synchronized块的访问将被阻塞,写个例子来证明一下:
public class ThreadDomain19
public void serviceMethodA()
synchronized (this)
try
System.out.println("A begin time = " + System.currentTimeMillis());
Thread.sleep(2000);
System.out.println("A end time = " + System.currentTimeMillis());
catch (InterruptedException e)
e.printStackTrace();
public void serviceMethodB()
synchronized (this)
System.out.println("B begin time = " + System.currentTimeMillis());
System.out.println("B end time = " + System.currentTimeMillis());
写两个线程分别调用这两个方法:
public class MyThread19_0 extends Thread
private ThreadDomain19 td;
public MyThread19_0(ThreadDomain19 td)
this.td = td;
public void run()
td.serviceMethodA();
public class MyThread19_1 extends Thread
private ThreadDomain19 td;
public MyThread19_1(ThreadDomain19 td)
this.td = td;
public void run()
td.serviceMethodB();
写个main函数:
public static void main(String[] args)
ThreadDomain19 td = new ThreadDomain19();
MyThread19_0 mt0 = new MyThread19_0(td);
MyThread19_1 mt1 = new MyThread19_1(td);
mt0.start();
mt1.start();
看一下运行结果:
A begin time = 1443843271982 A end time = 1443843273983 B begin time = 1443843273983 B end time = 1443843273983看到对于serviceMethodB()方法synchronized块的访问必须等到对于serviceMethodA()方法synchronized块的访问结束之后。那其实这个例子,我们也可以得出一个结论: synchronized块获得的是一个对象锁,换句话说,synchronized块锁定的是整个对象 。 synchronized块和synchronized方法 既然上面得到了一个结论 synchronized块获得的是对象锁 ,那么如果线程1访问了一个对象方法A的synchronized块,线程2对于同一对象同步方法B的访问应该是会被阻塞的,因为线程2访问同一对象的同步方法B的时候将会尝试去获取这个对象的对象锁,但这个锁却在线程1这里。写一个例子证明一下这个结论:
public class ThreadDomain20
public synchronized void otherMethod()
System.out.println("----------run--otherMethod");
public void doLongTask()
synchronized (this)
for (int i = 0; i < 1000; i++)
System.out.println(
"synchronized threadName = " + Thread.currentThread().getName() + ", i = " + (i + 1));
try
Thread.sleep(5);
catch (InterruptedException e)
e.printStackTrace();
写两个线程分别调用这两个方法:
public class MyThread20_0 extends Thread
private ThreadDomain20 td;
public MyThread20_0(ThreadDomain20 td)
this.td = td;
public void run()
td.doLongTask();
public class MyThread20_1 extends Thread
private ThreadDomain20 td;
public MyThread20_1(ThreadDomain20 td)
this.td = td;
public void run()
td.otherMethod();
写个main函数调用一下,这里"mt0.start()"后sleep(100)以下是为了确保mt0线程先启动:
public static void main(String[] args) throws Exception
ThreadDomain20 td = new ThreadDomain20();
MyThread20_0 mt0 = new MyThread20_0(td);
MyThread20_1 mt1 = new MyThread20_1(td);
mt0.start();
Thread.sleep(100);
mt1.start();
看一下运行结果:
... synchronized threadName = Thread-0, i = 995 synchronized threadName = Thread-0, i = 996 synchronized threadName = Thread-0, i = 997 synchronized threadName = Thread-0, i = 998 synchronized threadName = Thread-0, i = 999 synchronized threadName = Thread-0, i = 1000 ----------run--otherMethod证明了我们的结论。为了进一步完善这个结论,把"otherMethod()"方法的synchronized去掉再看一下运行结果:
... synchronized threadName = Thread-0, i = 16 synchronized threadName = Thread-0, i = 17 synchronized threadName = Thread-0, i = 18 synchronized threadName = Thread-0, i = 19 synchronized threadName = Thread-0, i = 20 ----------run--otherMethod synchronized threadName = Thread-0, i = 21 synchronized threadName = Thread-0, i = 22 synchronized threadName = Thread-0, i = 23 ..."otherMethod()"方法和"doLongTask()"方法中的synchronized块异步执行了 将任意对象作为对象监视器 总结一下前面的内容: 1、synchronized同步方法 (1)对其他synchronized同步方法或synchronized(this)同步代码块呈阻塞状态 (2)同一时间只有一个线程可以执行synchronized同步方法中的代码 2、synchronized同步代码块 (1)对其他synchronized同步方法或synchronized(this)同步代码块呈阻塞状态 (2)同一时间只有一个线程可以执行synchronized(this)同步代码块中的代码 前面都使用synchronized(this)的格式来同步代码块,其实 Java还支持对"任意对象"作为对象监视器来实现同步的功能 。这个"任意对象"大多数是 实例变量 及 方法的参数 ,使用格式为synchronized(非this对象)。看一下将任意对象作为对象监视器的使用例子:
public class ThreadDomain21
private String userNameParam;
private String passwordParam;
private String anyString = new String();
public void setUserNamePassword(String userName, String password)
try
synchronized (anyString)
System.out.println("线程名称为:" + Thread.currentThread().getName() + "在 " + System.currentTimeMillis() + " 进入同步代码块");
userNameParam = userName;
Thread.sleep(3000);
passwordParam = password;
System.out.println("线程名称为:" + Thread.currentThread().getName() + "在 " + System.currentTimeMillis() + " 离开同步代码块");
catch (InterruptedException e)
e.printStackTrace();
写两个线程分别调用一下:
public class MyThread21_0 extends Thread
private ThreadDomain21 td;
public MyThread21_0(ThreadDomain21 td)
this.td = td;
public void run()
td.setUserNamePassword("A", "AA");
public class MyThread21_1 extends Thread
private ThreadDomain21 td;
public MyThread21_1(ThreadDomain21 td)
this.td = td;
public void run()
td.setUserNamePassword("B", "B");
写一个main函数调用一下:
public static void main(String[] args)
ThreadDomain21 td = new ThreadDomain21();
MyThread21_0 mt0 = new MyThread21_0(td);
MyThread21_1 mt1 = new MyThread21_1(td);
mt0.start();
mt1.start();
看一下运行结果:
线程名称为:Thread-0在 1443855101706 进入同步代码块 线程名称为:Thread-0在 1443855104708 离开同步代码块 线程名称为:Thread-1在 1443855104708 进入同步代码块 线程名称为:Thread-1在 1443855107708 离开同步代码块这个例子证明了: 多个线程持有"对象监视器"为同一个对象的前提下,同一时间只能有一个线程可以执行synchronized(非this对象x)代码块中的代码 。 锁非this对象具有一定的优点:如果在一个类中有很多synchronized方法,这时虽然能实现同步,但会受到阻塞,从而影响效率。但如果同步代码块锁的是非this对象,则synchronized(非this对象x)代码块中的程序与同步方法是异步的,不与其他锁this同步方法争抢this锁,大大提高了运行效率。 注意一下"private String anyString = new String();"这句话,现在它是一个全局对象,因此监视的是同一个对象。如果移到try里面,那么对象的监视器就不是同一个了,调用的时候自然是异步调用,可以自己试一下。 最后提一点,synchronized(非this对象x),这个对象如果是实例变量的话,指的是对象的引用, 只要对象的引用不变,即使改变了对象的属性,运行结果依然是同步的 。 细化synchronized(非this对象x)的三个结论 synchronized(非this对象x)格式的写法是将x对象本身作为对象监视器,有三个结论得出: 1、当多个线程同时执行synchronized(x)同步代码块时呈同步效果 2、当其他线程执行x对象中的synchronized同步方法时呈同步效果 3、当其他线程执行x对象方法中的synchronized(this)代码块时也呈同步效果 第一点很明显,第二点和第三点意思类似,无非一个是同步方法,一个是同步代码块罢了,举个例子验证一下第二点:
public class MyObject
public synchronized void speedPrintString()
System.out.println("speedPrintString__getLock time = " + System.currentTimeMillis() + ", run ThreadName = " + Thread.currentThread().getName());
System.out.println("----------");
System.out.println("speedPrintString__releaseLock time = " + System.currentTimeMillis()+ ", run ThreadName = " + Thread.currentThread().getName());
ThreadDomain24中持有MyObject的引用:
public class ThreadDomain24
public void testMethod1(MyObject mo)
try
synchronized (mo)
System.out.println("testMethod1__getLock time = " + System.currentTimeMillis() + ", run ThreadName = " + Thread.currentThread().getName());
Thread.sleep(5000);
System.out.println("testMethod1__releaseLock time = " + System.currentTimeMillis() + ", run ThreadName = " + Thread.currentThread().getName());
catch (InterruptedException e)
e.printStackTrace();
写两个线程分别调用"
speedPrintString()
"方法和"
testMethod1(MyObject mo)
"方法:
public class MyThread24_0 extends Thread
private ThreadDomain24 td;
private MyObject mo;
public MyThread24_0(ThreadDomain24 td, MyObject mo)
this.td = td;
this.mo = mo;
public void run()
td.testMethod1(mo);
public class MyThread24_1 extends Thread
private MyObject mo;
public MyThread24_1(MyObject mo)
this.mo = mo;
public void run()
mo.speedPrintString();
写一个main函数启动这两个线程:
public static void main(String[] args)
ThreadDomain24 td = new ThreadDomain24();
MyObject mo = new MyObject();
MyThread24_0 mt0 = new MyThread24_0(td, mo);
MyThread24_1 mt1 = new MyThread24_1(mo);
mt0.start();
mt1.start();
看一下运行结果:
testMethod1__getLock time = 1443855939811, run ThreadName = Thread-0 testMethod1__releaseLock time = 1443855944812, run ThreadName = Thread-0 speedPrintString__getLock time = 1443855944812, run ThreadName = Thread-1 ---------- speedPrintString__releaseLock time = 1443855944812, run ThreadName = Thread-1看到"speedPrintString()"方法必须等待"testMethod1(MyObject mo)"方法执行完毕才可以执行,没有办法异步执行,证明了第二点的结论。第三点的验证方法类似,就不写代码证明了。
以上是关于创作赢红包喂,鬼仔,你还在瞒着我偷偷使用强制相等的主要内容,如果未能解决你的问题,请参考以下文章