做了快6年的java项目了,仅用过一次多线程,而且都是架构组封装好的,仅需要调用即可,现在想起来,真是惭愧....近期在项目中又遇到一个需要用多线程处理的问题,因此仔细研究了一下...
------------------------------------------------------------------------------------------------------------------------------------
首先,了解什么是锁,举个简单的例子,你出门的时候会把门锁上,这叫给门这个对象上锁,然后回到家过后肯定会打开锁推门进屋,这叫给门这个对象解锁。在给对象上锁后,若不解锁,则其他人就无法使用这个对象,(假若你带一个朋友回家,假若你不开锁,朋友就无法推开门,推开门就是你朋友在使用你们家门这个对象),在这儿假若你和你朋友是二个线程,你朋友在等你开门的过程中,就是在线程等待,你是正在执行的线程,若你发现钥匙丢了开不了门,就会造成你朋友等待,造成线程执行超时(本来平时开门就两分钟时间),假若你一直都解决不了开锁的问题,你朋友就会一直等待,从而造成死锁...
在java里面的锁,有两类锁,类锁和对象锁。
一、首先我们来说说类锁
实现类锁有两种方式:在方法前加static synchronized或在方法体内对部分或所有代码加synchronized(XXX.class)。
注意:我们要知道,一个类只有一个类锁,当某个线程A为了执行该类的某个已被标识为需要获得类锁才能执行的方法时,若该类的类锁正在被其它线程占用,则线程A会一直等待,直到被其它线程占用的类锁被释放。
对于类锁,方法的执行顺序,主要有四点:
第一,当几个线程同时访问某个类的不同方法时,若执行这些方法都需要获得该类的类锁时,则这几个线程串行执行。举个例子,有个ClassLock类,里面有a()和b()两个方法,执行这两个方法都需要获得该类的类锁(因为方法被标记了static synchronized),此时有两个线程,分别访问a()和b()方法,则这两个线程串行执行,代码示例如下:
第二,当几个线程同时访问某个类的相同方法时,若执行这个方法需要获得该类的类锁时,则这几个线程串行执行,代码示例如下:
第三、当几个线程同时访问某个类的不同方法时,若该类中仅其中一个方法在执行时需要获得类锁,则这几个线程并行执行,代码示例如下:
第四,当几个线程同时访问某个类的不同方法时,若执行该类的这些方法都不需要获得该类的类锁,则几个线程并行执行,很简单,我这儿就不举例了。
上面例子中我们还可以通过synchronized (ClassLock.class) 这种方式获得类锁,以下这两种方式效果相同
synchronized(XXX.class)方式相对灵活一些,它可以针对方法体内的某些代码而不是所有代码(作用范围可以根据需要调整),若作用范围不是整个方法体,当多个线程同时访问该方法时,不需要获得类锁的代码同步执行,需要获得类锁的代码串行执行,例
关于类锁我们就讲完了,我们总结一下,其实最重要的一点: 一个类只有一个类锁,当某个线程A为了执行该类的某个已被标识为需要获得类锁才能执行的方法时,若该类的类锁正在被其它线程占用,则线程A会一直等待,直到被其它线程占用的类锁被释放。
第二,我们来说下对象锁
首先我们要知道:一个对象只有一把锁,如果一个对象被某个标识为synchronized的方法或synchronized(XXX){//}的代码块引用了,而这个方法此时又正在被A线程执行,那么这个对象此时就被锁住了,要等到A线程执行完synchronized作用范围的代码,这个对象的锁才会被释放,才能被其它线程访问。
锁住对象的方式有两种,在方法前上synchronizd或在方法体内标识synchronizd(对象),在方法前加synchronizd,那这把锁的作用范围是整个方法体,而在方法内部用synchronizd(对象)标识,则作用范围要灵活得多,可以是整个方法体,也可以是部分语句块。在锁的作用范围内的所有对象,都将被锁住,只到当锁被释放后,才能被另外一个线程访问。
我们来举个例子,ObjectLockC类的main方法中有线程一、线程二、线程三,三个线程,在类中有a()方法,调用时需要传入d对象,a()方法中直接会调用d对象的打印方法执行打印操作,d对象打印时需要获得对象d的锁,线程二、线程三调用ObjectLockC类A()方法,代码如下:
首先,我问一个问题,在上例中,这三个线程是并行执行还是串行执行?在执行线程一时有哪些对象被锁住了?
...知道答案了吗,答案如下:
一、首先线程一、线程二、线程三并行执行,因为线程一、线程三在执行a()方法第一行代码时还没进入到synchronized范围内,因此d对象和ca对象不会被锁,因此三个线程首先并行执行,其次是线程一执行,线程一执行进入a()方法的synchronized的作用范围后,会锁定ca对象,因此线程一、线程三串行执行。
二、在执行线程一时,首先会锁住ca对象,因为a()方法是ca对象的,而a()方法内部又被synchronized标识了,因此当执行到a()方法中synchronized作用范围内的代码时,就会锁住ca对象,为什么要锁住d呢,因d在方法a()中synchronized的作用范围内。
这个就是对象的锁。
上面介绍完了两种锁,我们通过一个例子还看下他们两者的区别,代码如下:
上面的代码中进行了两种测试,第一个测试,两个不同的实例通过两个线程分别调用类锁的方法,执行结果为两个实例串行执行,第二个测试,两个不同实例通过两个线程分别调用对象锁的方法,执行结果为两个实例并行执行,为什么会这样呢?因为第一个测试,DifLock类的类锁只有一个,线程1获得了,那么线程2就需要等待线程1释放类锁,第二个测试,实例1和实例2分别为不同的对象,也就是说对象锁是不一样的,因此线程2不需要等待线程1释放锁。
上面就讲完了,若有问题,请留言。