多线程的一些知识点,其中涉及到了一个不可忽视的问题,那就是多个线程在同时运行的时候,极易造成数据的错乱或丢失,这就是多线程的不安全问题,Java中为了解决这个问题,提供了三种主要的解决办法。
本博客就是为了记录下这个知识点,达到加深印象和方便回看的目的。
下面是我简单的总结。
方法一.同步代码块
简单理解就是,被同步代码块括住的内容,在多线程执行时会进行排队操作。(通过传一个锁的对象,初始为开放状态,当一个线程执行该任务时,会先检查这个锁对象,若未上锁,则线程执行任务,在一个线程执行任务时锁对象会上锁(通过底层的原理对锁对象进行标记),其他线程检查锁对象时发现是上锁状态,就会排队等待执行,期间不断监察锁对象,一旦解锁,继续抢占执行权)。
(1)格式:Synchronized(锁对象){
##中间是任务的具体代码##
}
注:锁对象可以是任意对象,且多线程执行时检查的锁必须是同一把锁,可以将锁对象设置成一个任务类中的一个私有属性,这样在只有一个任务的情况下就只有一把锁。
方法二.同步方法
创建任务类时会重写run方法来定义任务,run方法内部就是任务本身的具体代码,同步方法就是将这些任务代码额外抽成另一个方法,用Synchronized修饰成同步方法,代表这是需要排队执行的任务,然后在run方法内部直接调用这个方法即可。
同步方法同样是利用锁标记来进行上锁解锁实现排队,这里需要注意:
(1).如果抽成的方法是静态方法,锁对象就是任务类本身,即 任务类.class 。
(2).如果不是静态方法,锁对象就是任务类创建的对象,一个任务就是一把锁。
注: 【如果同时创建多个任务对象去多线程执行,就具有多个锁,是无意义的】
方法三.显式锁
之前的同步代码块和同步方法均为隐式锁,也就是你只需要传入一个锁对象,上锁解锁由底层执行。
接下来所说的显式锁,意思是自己定义锁对象,自己执行上锁和解锁操作。
需要用到一个类:Lock
注,自行定义锁对象依然是在任务类内部,绝对要保证只有一把锁。
(1).定义的方法是:
Lock L = new ReentrantLock();
这就代表利用Lock类创建了一个锁对象。
(2).然后在任务类重写的run方法内部任务的开头,执行上锁操作(L.lock),代表的是如果一个线程进来了,要先执行上锁,这样其他线程就不可以使用了。
(3).在任务最后,执行解锁操作(L.unlock),重新开放任务的执行权。
显式锁有趣的一点是,它具有公平锁和不公平锁的概念。
(1).公平锁:在上锁的情况下,下一个排队的是谁,谁就是下一个执行任务的线程,不会存在所有线程抢夺谁抢到是谁的情况。
(2).不公平锁:谁抢到就是谁的(简单粗暴)。
至于对公平锁与不公平锁的设置,只需在创建锁对象时,括号内部传入一个Boolean值即可,True代表是公平锁,False代表是不公平锁。
例如:
Lock L = new ReentrantLock(True);
这就是一个公平锁。
对于多线程同时执行时的不安全问题,可通过上述三种方法解决,时刻要记住只能拥有一把锁的原则。