2)线程同步机制简介及锁的使用(同步代码块,同步方法,同步静态方法)及死锁

目录

1)线程同步机制简介

2)锁的概念

2.1)锁的实现方式

2.2)锁的作用

2.3)锁相关的概念

3)内部锁:synchronized关键字

3.1)synchronized同步代码块

3.2)同步方法

3.3)同步静态方法

4)死锁


1)线程同步机制简介

       协调多个线程之间数据访问的机制,java平台提供的线程同步机制包括:锁,volatile关键字,final关键字,static关键字,以及相关的API,如Object.wait()/Object.notify()等

2)锁的概念

线程安全的前提是多个线程并发访问共享数据,只要把多个线程在访问共享数据时的并发访问改成串行访问,就可以解决线程安全问题。JVM 把锁分为内部锁和显示锁两种. 内部锁通过 synchronized 关键字实现; 显示锁通过 java.concurrent.locks.Lock 接口的实现类 实现的

 

2.1)锁的实现方式

也可以说有N把锁,但是钥匙只有一把,一次只能打开一把锁,不能同时打开 

2.2)锁的作用

锁可以实现对共享数据的安全访问,保障线程的原子性、可见性与有序性。

原子性:一个锁只能被一个线程持有,这就保证临界区的代码一次只能被一个线程执行。既具备了原子性

可见性:在java平台中,锁的获得隐含着刷新处理器缓存的动作,锁的释放隐含着冲刷处理器缓存的操作,这就保存数据是最新的

有序性:写线程在临界区所执行的在读线程所执行的临界区看来像是完全按照源码顺序执行

注意:

这些线程在访问共享数据时必须使用同一个锁(如都是对数据A的操作,但不是同一个锁,也会导致线程安全问题)

即使用是读取共享数据的线程也需要使用同步锁(如果不使用锁,当某个锁读取数据时,可能有其它线程正在修改数据,就会造成脏读

2.3)锁相关的概念

1)可重入性:一个线程持有该锁的时候能再次(多次)申请该锁,相当于已经有该锁的钥匙了,下次再次遇到这个锁,还是能打开

在methodA中获取a锁后,b中能再次获取A锁

void methodA(){ 
    申请 a 锁 
    methodB(); 
    释放 a 锁 
}
void methodB(){ 
    申请 a 锁 
    .... 
    释放 a 锁
 }

2)锁的争用与调试

java平台中内部锁属于非公平锁,显示Lock锁既支持公平锁也支持非公平锁(非公平锁就是如果 A线程释放了a锁,再次申请a锁会比其它线程申请成功的概率大)

3)锁的粒度

一个锁可以保护的共享数据的数量大小称为锁的粒度,锁的粒度过粗会导致线程在申请锁时会时行不必要的等待,粒度过细会增加锁调试的开销

内部锁:synchronized关键字

内部锁是一种排他锁 , 可以保障原子性, 可见性与有序性
 
修饰代码块

 

3)内部锁:synchronized关键字

这种内部锁是一种排他锁 , 可以保障原 子性, 可见性与有序性 .。
上边概念写了,钥匙只有一把,谁先拿到算谁的,而锁的类型有很多,学习synchronized只要搞清楚 锁的类型(使用的那把锁) 就可以了,

3.1)synchronized同步代码块

虽然两个线程执行的方法不一样, 但对象锁是一样的this,所以结果先执行完mm()方法后,线程1才能重新获得对象锁执行s()方法
Test03 test03 = new Test03();
        Thread r = new Thread(new Runnable() {
            @Override
            public void run() {
                test03.mm();
            }
        });
        r.setName("0");
        r.start();
        Thread r1 = new Thread(new Runnable() {
            @Override
            public void run() {
                test03.s();
            }
        });
        r1.setName("1");
        r1.start();
    }
    public void mm(){
        synchronized (this){
            for (int i = 0; i < 100; i++) {
                System.out.println(Thread.currentThread().getName()+i);
            }
        }

    }
    public void s(){
        synchronized (this){
            for (int i = 0; i < 100; i++) {
                System.out.println(Thread.currentThread().getName()+i);
            }
        }

    }

3.2)同步方法

同步方法默认锁是this,与synchronized(this){}代码块是一个锁

ps:虽然执行的方法不同,但是同步锁都是this(test03),所以先执行完mm()后在执行ss1()

  Test04 test03 = new Test04();
        Thread r = new Thread(new Runnable() {
            @Override
            public void run() {
                test03.mm();
            }
        });
        r.setName("0");
        r.start();
        Thread r1 = new Thread(new Runnable() {
            @Override
            public void run() {
                test03.ss1();
            }
        });
        r1.setName("1");
        r1.start();
    }
    public synchronized void ss1(){
        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName()+i);
        }
    }
    public void mm(){
        synchronized (this){
            for (int i = 0; i < 100; i++) {
                System.out.println(Thread.currentThread().getName()+i);
            }
        }

    }

3.3)同步静态方法

静态方法默认的锁是调用它类的类对象,也称为类锁

bb1()与s()锁对象都是调用它们的类锁Test04.class

 Test04 test03 = new Test04();
        Thread r = new Thread(new Runnable() {
            @Override
            public void run() {
                test03.bb1();
            }
        });
        r.setName("0");
        r.start();
        Thread r1 = new Thread(new Runnable() {
            @Override
            public void run() {
                test03.s();
            }
        });
        r1.setName("1");
        r1.start();
    }
    public synchronized static void bb1(){
        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName()+i);
        }
    }
    public void s(){
        synchronized (Test04.class){
            for (int i = 0; i < 100; i++) {
                System.out.println(Thread.currentThread().getName()+i);
            }
        }
    }

4)死锁

解决死锁可以采用获取锁顺序一致的方法解决(既线程a与线程b都是先获取key1锁,在获取key2锁)

下边就是一个典型的死锁:

a线程获取Key1锁

b线程获取Key2锁

a线程获取key2锁,由于key2在b线程,等待b线程释放锁

b线程获取key1锁,由于key1在a线程,等待a线程释放锁

a线程获取key2锁,由于key2在b线程,等待b线程释放锁

b线程获取key1锁,由于key1在a线程,等待a线程释放锁

.........

public class Test05 {
    public static void main(String[] args) {
        Ru ru = new Ru();
        ru.setName("a");
        ru.start();


        Ru ru1 = new Ru();
        ru1.setName("b");
        ru1.start();
    }
    static class Ru extends Thread{
        private static final Integer key1 = new Integer(1);
        private static final Integer key2 = new Integer(1);

        @Override
        public void run() {
            if(this.getName().equals("a")){
                synchronized (key1){
                    System.out.println("线程a获的了key1锁,还要获取key2锁");
                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    synchronized (key2){
                        System.out.println("线程a获的了key2锁");

                    }
                }
            }
            if(this.getName().equals("b")){
                synchronized (key2){
                    System.out.println("线程b获的了key2锁,还要获取key1锁");
                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    synchronized (key1){
                        System.out.println("线程b获的了key1锁");

                    }
                }
            }
        }
    }

}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值