Synchronized(m){
<p style="margin-left:0pt;">Synchronized(m){</p>
<p style="margin-left:0pt;"> </p>
<p style="margin-left:0pt;">}</p>
<p style="margin-left:0pt;">}</p>
</td>
</tr></tbody></table><h3><a name="t2"></a><strong><strong><strong>ReentrantLock的特性</strong></strong></strong></h3>
·这是一种重入锁的实现。它有一个很大的特点就是必须的手动开启锁和释放锁。尤其是这个释放锁,不能忘记否则程序则会一直阻塞。与Synchronized不同的是Synchronized在遇到异常的时候就会释放锁但是ReentrantLock在异常下是不会释放锁的,因此经常在finally中进行锁的释放。
·locked = lock.tryLock(5, TimeUnit.SECONDS);尝试去获得锁,如果5秒还是没有获得到那么就会向下是执行。
·可以被打断的锁,如果一段代码被lock.lockInterruptibly(); 这个锁锁住,那么他能够被
t2.interrupt();这样的语句去手动打断。
·公平锁与不公平锁,通常来说Synchronized的锁是一种不公平的锁,而ReentrantLock可以实现公平锁。那什么是不公平呢?就是由于随机化的原因有的线程会由于运气不好久久得不到执行。公平就是使用了一种时间上的调度算法来使得每个线程的都能够得到公平的执行。
线程通信的底层
线程通信通常有两种,一种是读取共享的一段内存,还有一种就是线程之间互相通信。Java的线程通讯采用的是读取共享的一段内存。
Volatile关键字
这是一个案例,在上述代码不开volatile的时候new出来的那个线程把running从主内存中读取出来读到自己的缓冲区中并且以此为标签来执行while()中的代码块。而且一直执行下去在繁忙的情况下不会去读取主内存中的值,即使main线程对这个值做了修改。所以我们会看到while()中的代码一直被执行。
当volatile开启的时候,这两个线程之间也就变成了可见的了。具体原因就是在主内存的running的值被修改之后这时有一个线程会通知new出来的线程但缓冲区说你的running的值已经过期了,所以缓冲区的running的值会变成false随之while的执行结束。
值得一提的是在线程空闲的时候有可能会去主内存中读取值。
拓展:Volatile与Synchronized的联系与区别
Volatile只保证可见性,就是线程之间的变量是互相可见的,但是不能够保证原子性。
Synchronized,同时保证了原子性和可见性,因为Synchronized会将程序串行执行,当上一个程序执行完毕之后会将值写入到主内存中下一个线程来到时肯定会读取到这段内容。
这种感觉类似于数据库中的事务隔离级别。
AtomicXXX类型
因为++操作(当然也包含其他的系列操作)不是一个原子操作,所以在需要保证原子的++时可以通过上锁来解决问题,也可以通过以下的案例来解决:
import java.util.ArrayList; import java.util.concurrent.atomic.AtomicInteger; AtomicInteger count = new AtomicInteger(0); for (int i = 0; i < 10000; i++) public static void main(String[] args) { List<Thread> threads = new ArrayList<Thread>(); for (int i = 0; i < 10; i++) { threads.add(new Thread(t::m, "thread-" + i)); threads.forEach((o) -> o.start()); } catch (InterruptedException e) { System.out.println(t.count);
一道淘宝面试题的演化(Volatile与门闩机制)
实现一个容器,提供两个方法,add,size。写两个线程,线程1添加10个元素到容器中,线程2实现监控元素的个数,当个数到5个时,线程2给出提示并结束。
方案一:
正常的思维,单纯的判断容器的大小是无效的,因为线程之间是不可见的
public class MyContainer1 { List lists = new ArrayList(); public void add(Object o) { public static void main(String[] args) { MyContainer1 c = new MyContainer1(); for(int i=0; i<10; i++) { System.out.println("add " + i); TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { System.out.println("t2 结束");
方案二:
在方案一的基础上加上volatile关键字,成功运行。因为两个线程之间是彼此可见的。
volatile List lists = new ArrayList();
|
方案三:
由于方案二的while的循环一直在监视所以十分的浪费CPU。因此我们将机制改为了wait()与notify()。也就是当t1检测到到达size到达5的时候叫醒正在沉睡的t2。但是t2、t1使用的是通一把锁而notify不会释放锁(当然它与notify起作用的前提是获得锁)。所以我们的最终方案是当t1叫醒t2的同时自己要wait()这样才保证了t2能够拿到锁然后t2执行完之后也要唤醒t1(由于这时候t2已经执行完毕了所以锁自然释放)。代码如下:
public class MyContainer4 { volatile List lists = new ArrayList(); public void add(Object o) { public static void main(String[] args) { MyContainer4 c = new MyContainer4(); final Object lock = new Object(); System.out.println("t2启动"); } catch (InterruptedException e) { System.out.println("t2 结束"); TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e1) { System.out.println("t1启动"); for(int i=0; i<10; i++) { System.out.println("add " + i); } catch (InterruptedException e) { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) {
终极方案:
使用门闩机制
使用Latch(门闩)替代wait notify来进行通知
好处是通信方式简单,同时也可以指定等待时间
使用await和countdown方法替代wait和notify
CountDownLatch不涉及锁定,当count的值为零时当前线程继续运行
当不涉及同步,只是涉及线程通信的时候,用synchronized + wait/notify就显得太重了
这时应该考虑countdownlatch/cyclicbarrier/semaphore
@author mashibing
首先new一个门闩CountDownLatch latch = new CountDownLatch(1);里面有一个参数,同时他有一个方法就是latch.await();,它会插在代码之间阻拦代码的执行。同时latch.countDown();方法会减少门闩的数量当门闩的数量减少为0的时候这时门会自动打开这时候latch.await();会向下执行。整个过程不涉及锁的机制,高效得实现了线程之间的通信。
public class MyContainer5 { volatile List lists = new ArrayList(); public void add(Object o) { public static void main(String[] args) { MyContainer5 c = new MyContainer5(); CountDownLatch latch = new CountDownLatch(1); System.out.println("t2启动"); } catch (InterruptedException e) { System.out.println("t2 结束"); TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e1) { System.out.println("t1启动"); for (int i = 0; i < 10; i++) { System.out.println("add " + i); TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) {
最后感谢马老师,一个专心做教育的老师。
原文地址:https://blog.csdn.net/qq_34993631/article/details/82425052 |