【构建并发程序】5-惰性值lazy与循环依赖问题

使用惰性值可以提高程序的性能

案例

 假如我们对一个变量使用了lazy 则这个变量只有再被使用时才会执行初始化。

package study.ch_three

//演示代码1:
//init::::::
//start:::
//end:::

object lazyinitval  {
  def init(): Int = {
    println("init::::::")
    0
  }
  val resource: Int = init()
  def main(args: Array[String]): Unit = {
    println("start:::")
    println("end:::")
  }
}
//演示代码2:
//start:::
//init::::::
//end:::
object lazyinitval2 {
  def init(): Int = {
    println("init::::::")
    0
  }
  lazy val resource: Int = init()
  def main(args: Array[String]): Unit = {
    println("start:::")
    resource
    println("end:::")
  }
}

惰性值在多线程中会遇到的问题

 由于惰性值在访问时才会进行初始话,可以缩短程序的执行事件進而提高效率;
问题:

  • 但是在多线程-线程池中,如果多个线程都需要访问惰性值,此时惰性值就会频繁的进行初始话,从而影响效率。对于我来说,我只想让惰性值被线程池中的某一个线程初始话一次就够了,没必要多个线程,每访问一次都要初始化一次。

  • 这个问题已经通过Scala(java同理)源码进行了解决(使用的是synchronized锁机制实现的),即一个线程池中的线程最多只会让一个线程初始化惰性值。

def execute (body: =>Unit) = ExecutionContext.global.execute{new Runnable {
  override def run(): Unit = body
}}
/**
 * *   但是在多线程-线程池中,如果多个线程都需要访问惰性值,此时惰性值就会频繁的进行初始话,从而影响效率。
 * 对于我来说,我只想让惰性值被线程池中的某一个线程初始话一次就够了,没必要多个线程每访问一次都要初始化一次。
 * 这个问题已经通过Scala源码进行了解决(使用的是this.synchronized锁机制实现的),即一个线程池中的线程最多只会让一个线程初始化惰性值。
 **/
object two_惰性值在多线程中的问题和解决3 extends App {

  lazy val obj = new AnyRef
  lazy val non = s"made by:${Thread.currentThread().getName}"
  execute {
    println(s"线程池中的线程 obj = $obj")
    println(s"线程池中的线程 non = $non")
  }
  println(s"main线程 obj = $obj")
  println(s"main线程 non = $non")

  Thread.sleep(2000)
}

避免惰性值之间出现循环依赖关系

惰性值之间的循环依赖关系:
1、 在单线程 (即顺序程序) 会出现堆栈溢出;
2、 多线程中会出现的死锁;

惰性值之间出现循环依赖关系(直接调用)

  • 1、在并发程序中:
    会出现死锁(因为lazy中有用到:this.synchronized锁机制) 当你的线程池中的线程调用惰性值A.x的时候,A对象会被进行监控; lazy的源码中是this.synchronized这里的this就是A对象,A对象被锁当你的main线程调用惰性值B.y的时候,B对象会被进行监控; 那么你想在A对象中使用B.y那就不可能了,因为此时B对象正在被main线程霸占着

  • 2、在顺序环境中(即将execute { A.x }变成A.x):会出现堆栈溢出

  • 3、在自己编写代码的时候尽量避免出现此类情况

object LazyValueDeadlock extends App {
	def execute (body: =>Unit) = ExecutionContext.global.execute{new Runnable {
	  override def run(): Unit = body
	}}

  object A {
    lazy val x: Int = B.y
  }

  object B {
    lazy val y: Int = A.x
  }

  execute(A.x)
  B.y
}

惰性值之间出现循环依赖关系(惰性值中使用线程操作)

 我的main线程会先初始话惰性值x,同时通过synchronized进行当前对象的监控,然后执行里面的thread线程; thread线程因为要调用惰性值x,因此也会初始话惰性值x,但此时惰性值已经被main线程进行监控了, 所以就造成了死锁,或者说是堵塞。

object LazyValsAndBlocking extends App {
	def thread(body: => Unit): Thread = {
	    val t = new Thread {
	      override def run(): Unit = body
	    }
	    t.start()
	    t
	  }
  lazy val x: Int = {
    val t = thread {
      println(s"Initializing $x")
    }
    t.join()
    1
  }
  x
}

惰性值之间出现循环依赖关系(synchronized中使用子线程)

 我的main线程中使用了this.synchronized监听了this当前对象 而在this.synchronized中继续使用子线程调用惰性值x进行初始话的化,需要调用this当前对象,但是当前对象已经被main线程占用了,所以就出现死锁或者说是堵塞。

object LazyValsAndMonitors extends App {
	def thread(body: => Unit): Thread = {
	    val t = new Thread {
	      override def run(): Unit = body
	    }
	    t.start()
	    t
	  }
  lazy val x = 1
  this.synchronized {
    val t = thread {
      x
    }
    t.join()
  }
}

(本文章虽然采用的代码为scala代码,但java代码与Scala代码可以互相转换,且本质上两者所阐述的东西都是一致的)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值