Java并发编程基础02-线程同步基础
概念
(1)竞态条件产生的原因:当一段程序中存在多个线程同时访问同一个共享资源时,竞态条件随之产生
(2)可见性问题:
当一个线程尝试修改一个共享变量的值时,由于该线程对这个共享变量的修改可能仅在当前工作线程内部的缓存中生效,因此其他工作线程无法获知当前工作线程对这一共享变量值的修改
(3)临界区:
临界区是一个用于访问共享资源的代码块,并且在同一时刻只能由一个线程执行。
(4)Java语言提供的两种基本同步机制:
- 关键词synchronized
- Lock接口及其实现类
synchronized同步
(1)在Java语言中,所有synchronized关键词(无论是作用在方法还是在代码块上的)都需要通过一个指向对象的引用来予以绑定,通过该绑定对象,一次只允许一个线程访问由同一个对象守护的方法或者代码块。
(2)当你在方法中使用synchronized关键字时,对象引用是隐式的。【绑定的是this对象】
(3)当程序由synchronized修饰的静态方法调用时,此时方法所关联的对象是类对象而并非该类的实例对象。【静态方法绑定类对象Class对象】
(4)用synchronized关键词保护代码块时,必须使用一个对象引用作为参数传入。【通常使用this对象作为参数传入】
在同步代码块中使用唤醒和等待
(1)程序可以在线程里调用wait()方法。如果此时程序在线程外调wait()方法,则JVM会抛出一
个IllegalMonitorStateException异常。【线程只能自己wait自己,一般代码中的wait()方法其实是this.wait()】
(2)通过在同一个对象绑定的synchronized代码块中调用notify()或notifyAll()方法来唤醒通过wait()方法沉睡的线程。
(3)notify()和notifyAll()的区别,他们的运行机制是什么?如果有两个消费者被wait(),当调用notify方法时,是不是会和其他非wait线程重新争用synchronized代码块?
- 区别:使用notifyall可以唤醒所有处于wait状态的线程,使其重新进入锁的争夺队列中,而notify只能唤醒一个
- 运行机制:当有线程调用了对象的 notifyAll()方法(唤醒所有 wait 线程)或 notify()方法(只随机唤醒一个 wait 线程),被唤醒的的线程便会进入该对象的锁池中,锁池中的线程会去竞争该对象锁。也就是说,调用了notify后只要一个线程会由等待池进入锁池,而notifyAll会将该对象等待池内的所有线程移动到锁池中,等待锁竞争。
在同步代码块中使用锁机制
(1)Lock机制的优势:
- 构建同步代码更加灵活。关键词synchronized仅能作用在一段结构化代码中。
- 通过Lock接口可以获得其他由synchronized关键词所不能提供的功能特性,比如tryLock()方法,不必挂起。
- 通过ReadWriteLock接口可以实现读写锁分离,即允许同时执行多个读锁而单个写锁与其他锁互斥。
- Lock接口在性能表现上比synchronized关键词更加优越。
(2)公平锁策略与非公平锁策略:
在ReentrantLock类构造函数的参数中包含一个名为fair的boolean变量,若值为false(也是默认设置)时则当前锁策略为非公平锁策略。
- 非公平锁策略:如果有多个线程同时请求同一个锁,那么该锁将随机分配给其中的一个线程
- 公平锁策略:如果当前有多个线程在等待同一把锁,则等待时间最长的线程将获得锁
(3)千万不要忘记在finally代码块中增加一个unlock()方法。【如果不释放,不会自动释放,造成死锁】
(4)可以给tryLock()方法传递时间值和TimeUnit对象来指定线程的最长等待时间
(5)ReentrantLock类允许线程递归调用。也就是说,如果一个线程已经获得了锁的控制权并递归地获取锁,则该线程依然可以获得该锁的控制权。
用读写锁保护同步代码块
(1)ReadWriteLock接口及其实现类ReentrantReadWriteLock是锁机制提供的一项重要特性,这些类涵盖了两种锁,即读锁和写锁。【多个线程可以同时读,此时屏蔽写锁。写的时候,屏蔽一切写和读】
在一个锁中使用多个条件
(1)在使用锁的过程中可能会涉及多个由Condition接口定义的条件机制。使用这些条件机制可以判断在当前条件下是否能获得锁的使用权。
(2)必须在线程中调用了同一个Lock对象的lock()和unlock()方法之间使用该对象产生的条件变量。
(3)Condition接口为开发者提供了把线程挂起并且唤醒的机制。如何唤醒?是不是和wait()机制相同?
- 唤醒用signal或者是signalAll方法
- 机制和wait()和notify()相同,await()进入condition相应对象的等待池,而signal进入condition相应对象的锁池
(4)也可以在读写锁ReadLock和WriteLock上使用条件。