一、互斥锁的特性
1、互斥性(原子性)
只有一个线程持有对象锁。
2、可见性
对共享变量的修改对之后的线程可见。
二、锁的分类
1、对象锁
2、类锁
三、获取的方法
1、synchronized(){
}//同步代码快
2、public synchronized void method(){
}//同步方法
四、两种方法的区别
1、一个线程调用同步方法或这同步代码块另一个不调用,两者互不影响。
public class SynchronizedTest {
public synchronized void methodA(){
try{
for (int i=0;i<5;i++){
System.out.println("methodA-"+i);
Thread.sleep(1000);
}
}catch (InterruptedException e){
e.printStackTrace();
}
}
/**
* 同步代码快
*/
public void methodA(){
synchronized (this){
try{
for (int i=0;i<5;i++){
System.out.println("methodA-"+i);
Thread.sleep(1000);
}
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
public void methodB(){
try{
for (int i=0;i<5;i++){
System.out.println("methodB-"+i);
Thread.sleep(1000);
}
}catch (InterruptedException e){
e.printStackTrace();
}
}
public static void main(String[] args) {
SynchronizedTest test=new SynchronizedTest();
Thread thread1=new Thread(new Runnable() {
@Override
public void run() {
test.methodA();
}
});
thread1.start();
Thread thread2=new Thread(new Runnable() {
@Override
public void run() {
test.methodB();
}
});
thread2.start();
}
}
结果
methodA-0
methodB-0
methodA-1
methodB-1
methodB-2
methodA-2
methodB-3
methodA-3
methodB-4
methodA-4
2、synchronization与synchronization(this)都是在this对象上上锁,所以会堵塞,如果synchronization(所属类不同)则不会堵塞。
public class SynchronizedTest {
public synchronized void methodA2(){
try{
for (int i=0;i<5;i++){
System.out.println("methodA2-"+i);
Thread.sleep(1000);
}
}catch (InterruptedException e){
e.printStackTrace();
}
}
/**
* 同步代码快
*/
public void methodA(){
synchronized (this){
try{
for (int i=0;i<5;i++){
System.out.println("methodA-"+i);
Thread.sleep(1000);
}
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
// public void methodB(){
// try{
// for (int i=0;i<5;i++){
// System.out.println("methodB-"+i);
// Thread.sleep(1000);
// }
// }catch (InterruptedException e){
// e.printStackTrace();
// }
//
// }
public void methodB(){
Object obj=new Object();
synchronized (obj){
try{
for (int i=0;i<5;i++){
System.out.println("methodB-"+i);
Thread.sleep(1000);
}
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
public static void main(String[] args) {
SynchronizedTest test=new SynchronizedTest();
Thread thread1=new Thread(new Runnable() {
@Override
public void run() {
test.methodA();
}
});
thread1.start();
Thread thread2=new Thread(new Runnable() {
@Override
public void run() {
test.methodB();
}
});
thread2.start();
}
}
结果
methodB-0
methodA-0
methodA-1
methodB-1
methodA-2
methodB-2
methodA-3
methodB-3
methodA-4
methodB-4
A2被堵塞
同步代码块与同步方法的区别
同步代码块会在开头插入monitorenter在结尾插入monitorexit指令,用于获取monitor。
同步方法是通过JVM检查ACC_SYNCHRONIZED获取。
五、synchronized的四种状态
1、无锁
2、偏向锁
markword记录着锁的Id当线程再次获取锁的时候只需比较ThreadID即可。
缺点:不适用于锁竞争的情况
3、轻量级锁
对象头中的lock record用于存储markword的拷贝
拷贝成功后markword的指针指向lockrecord
markword标志位职位00
4、重量级锁
总结
ReentrantLock再入锁
一、性质
1、位于java.util.concurrent.locks中
2、基于AQS实现
3、比synchronization更细粒度
4、可重入的公平锁
5、lock()后一定要释放锁
public class ReentrantLockDeom implements Runnable{
// private static ReentrantLock lock=new ReentrantLock(false);非公平锁
private static ReentrantLock lock=new ReentrantLock(true);
@Override
public void run() {
while (true){
try {
lock.lock();
System.out.println(Thread.currentThread().getName()+"got lock");
Thread.sleep(1000);
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();//一定要关锁
}
}
}
public static void main(String[] args) {
ReentrantLockDeom rtld=new ReentrantLockDeom();
Thread thread1=new Thread(rtld);
Thread thread2=new Thread(rtld);
thread1.start();
thread2.start();
}
}
结果
非公平锁
Thread-0got lock
Thread-0got lock
Thread-0got lock
Thread-0got lock
Thread-0got lock
Thread-0got lock
Thread-0got lock
线程分配不公平
公平锁
Thread-1got lock
Thread-0got lock
Thread-1got lock
Thread-0got lock
Thread-1got lock
线程分配公平
二、ReentrantLock公平性设置
1、参数为ture是倾向于把锁赋予等待时间最久的线程
2、公平锁获取锁的顺序为lock方法的顺序
三、ReentrantLick将锁对象化
1、判断是否有线程,或者某个特定的线程在排队等待获取锁。
2、带超时的获取锁的尝试
public class ReenyrantLockTest {
static Lock lock1 = new ReentrantLock();
static Lock lock2 = new ReentrantLock();
public static void main(String[] args) {
Thread thread = new Thread(new ThreadDemo(lock1, lock2));//该线程先获取锁1,再获取锁2
Thread thread1 = new Thread(new ThreadDemo(lock2, lock1));//该线程先获取锁2,再获取锁1
thread.start();
thread1.start();
}
static class ThreadDemo implements Runnable {
Lock firstLock;
Lock secondLock;
public ThreadDemo(Lock firstLock, Lock secondLock) {
this.firstLock = firstLock;
this.secondLock = secondLock;
}
@Override
public void run() {
try {
while (!lock1.tryLock()) {
TimeUnit.MILLISECONDS.sleep(10);
}
while (!lock2.tryLock()) {
lock1.unlock();
TimeUnit.MILLISECONDS.sleep(10);
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
firstLock.unlock();
secondLock.unlock();
System.out.println(Thread.currentThread().getName() + "正常结束!");
}
}
}
}
ReentrantLock还给我们提供了获取锁限时等待的方法tryLock(),可以选择传入时间参数,表示等待指定的时间,无参则表示立即返回锁申请的结果:true表示获取锁成功,false表示获取锁失败。我们可以使用该方法配合失败重试机制来更好的解决死锁问题
3、感知有没有成功获取锁。
4、可以将wait/notify/notifyAll对象化
四、synchronized和ReentrantLock的区别
1、synchronized是关键字,ReentrantLock是类
2、Reentrabtlock可以让等待锁的线程中断,避免死锁。
3、Reentrabtlock可以获取锁的信息知道有没有成功的获取锁。
4、Reentrabtlock可以灵活的实现多路通知
sync操作在markword中,lock调用Unsafe类的park()方法。
5、发生异常的时候synchronization会释放锁避免死锁,Reentrabtlock要释放。
五、什么是java内存模型jmm
是一个抽象的概念,一组规则或规范,通过这组规范定义了程序中各个变量的访问规则。
六、jmm
七、happens-before八大原则
1、程序次序原则
2、传递原则
3、锁定原则
4、对象终结原则
5、线程启动原则
6、线程中断原则
7、线程终结
8、volitile原则
八、volitile详解
1、volitile是jvm提供的轻量级同步机制,所有线程可见,并且禁止指令重排序优化。
2、volitile变量为何立即可见
因为当写一个volition变量是,jmm会把工作内存中的共享变量刷到主内存中,当读到volition变量是线程对应的工作内存无效。
九、volitile与synchronization的区别
1、volitile的本质是告诉JVM当前变量在工作内存中是不确定的,需要从主内存中读取。
synchronization是锁定当前变量,只有当前线程可以访问该变量,其他线程只能等到该线程完成为止。
2、volitile只能运用于变量级别,synchronization可以用于变量,方法区,类级别。
3、volitile只能保持修改可见性,不能保持原子性。
synchronization可见性,原子性
4、volitile不会造成线程的阻塞,synchronized可能会造成线程的阻塞。
5、volatile标记的变量不会被编译器优化,synchronized可以优化。
十、实现线程安全的单例写法
/**
* 单例的双重检测实现
*/
public class Singleton {
private static Singleton instance;
private Singleton(){ }
public static Singleton getInstance(){
//第一次检测
if (instance==null){
//同步
synchronized (Singleton.class){
if (instance==null){
//多线程环境下可能会出现问题的地方
instance=new Singleton();
}
}
}
return instance;
}
}