最近在学习OkHttp源码的时候,在ConnectionPool类中,频繁看到如下这句代码:

assert (Thread.holdsLock(this));

根据网上查资料,知道assert是断言的意思,后面我们也称之为断言。

在了解assert(断言)之前,我们需要知道“宏”的概念。宏是在C/C++中经常使用到的一种语法替换,通常C/C++中使用define来定义,如:

#define MAX_LEN 10 //其中MAX_LEN 叫做“宏名”

#define MAX(a, b) (a) > (b) ? (a) : (b) //其中MAX叫做“宏名”,a、b为宏函数的参数

上面两个表达式,都为宏定义。宏定义的宏变量、宏函数,在程序编译之前,不需要经过计算,直接进行简单的字符串替换,占用的是编译时间,上面例子中的MAX_LEN 宏名、MAX(a, b)宏函数,在编译之前,程序中凡是出现宏体(红名、宏函数)的地方,均被直接替换成字符串值。

比如,所有的MAX_LEN 都被替换成字符串10;

例如代码中有使用c = MAX(1, 3)这样的语句,则会被直接替换成 c = (1) > (3) ? (1) : (3)

因为宏是在编译前只做字符串直接替换,不需要给参数变量分配空间,所以它的参数是不会占用栈内存的。所以当函数体量很小时,使用宏可以提高程序的执行效率和系统开销。

断言

assert(断言),就属于宏,可以看做是异常处理的一种高级表达形式,类似于if()语句,表示为一些布尔表达式,程序员相信在程序中某个特定点该表达式值为真。一般用于对程序的调试,对执行结构的判断,而不是对业务流程的判断,如果满足断言的的条件,则执行程序,如果不满足则跑出错误。我们现在返回头来看我们的代码,在ConnectionPool类中找一段示例代码:

 @Nullable 
RealConnection get(Address address, StreamAllocation streamAllocation, Route route) {
    assert (Thread.holdsLock(this));
    for (RealConnection connection : connections) {
      if (connection.isEligible(address, route)) {
        streamAllocation.acquire(connection, true);
        return connection;
      }
    }
    return null;
  }

当assert (Thread.holdsLock(this));中的Thread.holdsLock(this)返回true时候,下面的代码才会继续执行,当Thread.holdsLock(this)返回false时,编译时候,就会抛出错误AssertionError(这是个错误,编译时错误),程序中断。这一切的前提,是需要我们提前开启断言,因为断言是用来调试程序的,默认情况下,开发工具是关闭断言调试的,assert是不会被执行的,在编译时候直接被忽略掉。

我的开发工具是Android Studio,网上找到的如何开启assert断言的资料,比较少,大部分的操作方法如下面截图所示:

Thread.holdsLock(Object)方法、assert断言、宏

 

Thread.holdsLock(Object)方法、assert断言、宏

先打开Run/Debug Configurations,然后设置设JUnit的VM Options的值为 -ea,如果想关闭assert断言,则设置JUnit的VM Options的值为 -da。

检测断言是否开启

可使用如下代码测试:

boolean isOk = false;

assert isOk = true;

System.out.println( "程序是否开启了断言:" + isOk);

如果程序开启了断言,那么在编译的时候,assert isOk = true代码就会被执行。打印结果为true。然而最终的结果为false,AndroidStudio如何开启断言,我也不清楚,就不去纠结如何开启了,估计是使用单元测试才可以吧,哪位大神清除的,请不吝赐教,嘿嘿。关于断言、宏的概念就介绍到这里。

holdsLock()方法

holdsLock(Object obj)是Thread的一个静态方法,源码贴出来

/**

* Returns <tt>true</tt> if and only if the current thread holds the

* monitor lock on the specified object.

*

* <p>This method is designed to allow a program to assert that

* the current thread already holds a specified lock:

* <pre>

* assert Thread.holdsLock(obj);

* </pre>

*

* @param obj the object on which to test lock ownership

* @throws NullPointerException if obj is <tt>null</tt>

* @return <tt>true</tt> if the current thread holds the monitor lock on

* the specified object.

* @since 1.4

*/

public static boolean holdsLock(Object obj) {

return currentThread().nativeHoldsLock(obj);

}

当且仅当线程在指定的对象上保持监视器锁时,才返回true,该方旨在使程序能够断言当前线程已经保持一个指定的锁:assert Thread.holdsLock(obj);

参数:obj 用于测试锁所属权的对象

返回:如果当前线程在指定的对象上保持监视器锁,则返回 true。 抛出:NullPointerException - 如果 obj 为 null。

根据文档可以看出,Thread.holdsLock建议结合assert(断言使用),测试线程是否持有一个指定所属权的锁对象。

我们写个小例子测试一下:

final Object lockObj = new Object();

new Thread(new Runnable() {

@Override

public void run() {

synchronized (lockObj) {

Log.i("Thread", "holdsLock:" + Thread.holdsLock(lockObj));

}

}

}).start();

Log.i("Main", "holdsLock:" + Thread.holdsLock(lockObj));

Log日志如下:

Thread.holdsLock(Object)方法、assert断言、宏

由于synchronized关键字的使用,使得子线程持有了锁lockObj,所以返回true,而主线程并没有持有锁,所以返回false。

文章开头,我的引入点assert (Thread.holdsLock(this));基本已经清楚了,这是工程师在利用assert(断言)来进行调试程序而写的,为了确保该代码所在的线程正持有所在类的对象作为锁。以上就是我想要分享的一些知识以及自己的一些疑惑,望各位批评指正。

相关文章: