线程的同步机制

2 线程的同步机制

2.1. 线程安全与非线程安全

1)线程安全与非线程安全
非线程安全是当多个线程访问同一个对象汇总的成员变量时产生的,产生的后果就是脏读,就是指读到的数据是被更改过的。而线程安全就是对获取成员变量的值经过同步处理,不会再出现脏读的现象。
2)线程不安全的产生条件
a.存在多个线程
b.多个线程共享同一个资源
c.对共享资源进行非原子性操作
3)线程安全的特征
1.局部变量是线程安全的,不存在线程安全问题,这是由于局部变量时私有的特征决定的。
样例程序解释:

public class LocalVariable {
    public static void main(String[] args) {
        LocalVaribleService localVaribleService = new LocalVaribleService();
        Thread threadA = new LocalVaribleThreadA(localVaribleService);
        threadA.start();
        Thread threadB = new LocalVaribleThreadB(localVaribleService);
        threadB.start();
    }
}

class LocalVaribleService{
    public void add(String name){
        int num = 0;          // 作为add方法内的局部变量
        if("A".equals(name)){
            num = 1;
            System.out.println("A set over");
            try {
                Thread.sleep(1000);
            }catch(InterruptedException e){
                e.printStackTrace();
            }
        }else if("B".equals(name)) {
            num = 2;
            System.out.println("B set over");
            try {
                Thread.sleep(1000);
            }catch(InterruptedException e){
                e.printStackTrace();
            }
        }
        System.out.println("名称为" + name + "的num值为" + num);
    }
}


class LocalVaribleThreadA extends Thread{
    private LocalVaribleService localVaribleService;

    public LocalVaribleThreadA(LocalVaribleService localVaribleService){
        this.localVaribleService = localVaribleService;
    }

    @Override
    public void run(){
        this.localVaribleService.add("A");
    }
}


class LocalVaribleThreadB extends Thread{
    private LocalVaribleService localVaribleService;

    public LocalVaribleThreadB(LocalVaribleService localVaribleService){
        this.localVaribleService = localVaribleService;
    }

    @Override
    public void run(){
        this.localVaribleService.add("B");
    }
}

2.成员变量不是线程安全的,如果有两个线程同时操作业务对象中的成员变量,可能会产生非线程安全问题。解决方法:在方法前使用关键字synchronized进行修饰。
样例程序解释:

public class MemberVariable {
    public static void main(String[] args) {
        MemberVaribleService memberVaribleService = new MemberVaribleService();
        Thread threadA = new MemberVaribleThreadA(memberVaribleService);
        threadA.start();
        Thread threadB = new MemberVaribleThreadB(memberVaribleService);
        threadB.start();
    }
}

class MemberVaribleService{
    private int num = 0;          // 作为成员变量
    public void add(String name){
        if("A".equals(name)){
            num = 1;
            System.out.println("A set over");
            try {
                Thread.sleep(1000);
            }catch(InterruptedException e){
                e.printStackTrace();
            }
        }else if("B".equals(name)) {
            num = 2;
            System.out.println("B set over");
            try {
                Thread.sleep(1000);
            }catch(InterruptedException e){
                e.printStackTrace();
            }
        }
        System.out.println("名称为" + name + "的num值为" + num);
    }
}


class MemberVaribleThreadA extends Thread{
    private MemberVaribleService memberVaribleService;

    public MemberVaribleThreadA(MemberVaribleService memberVaribleService){
        this.memberVaribleService = memberVaribleService;
    }

    @Override
    public void run(){
        this.memberVaribleService.add("A");
    }
}


class MemberVaribleThreadB extends Thread{
    private MemberVaribleService memberVaribleService;

    public MemberVaribleThreadB(MemberVaribleService memberVaribleService){
        this.memberVaribleService = memberVaribleService;
    }

    @Override
    public void run(){
        this.memberVaribleService.add("B");
    }
}

2.2. 内置锁与显示锁

1)锁的分类
多线程编程同步时有可能会用到锁,可以分为内置锁(sychronized)和显示锁(例如ReentrantLock)
2)内置锁的对象锁
1、sychronized 作用于实例方法时,锁对象是this,也就是所在类的实例对象
2、sychronized 作用于静态方法时,锁对象是Class对象
3、sychronized 作用于代码块时,锁对象是sychronized(object)中的object
3)内置锁的特性
a.内置锁的互斥。内置锁是一个互斥锁,不仅读写互斥并且读读也互斥,最多只有一个线程能够获得该锁,当线程A尝试去获得线程B持有的内置锁时,线程A必须等待或者阻塞,直到线程B释放这个锁。
b.内置锁是可重入锁。如果已经获取了一个锁对象,在还没释放时又要执行该锁对象的另一个代码块或方法,则不需要再次给这个对象加锁就可以直接执行。
c.内置锁自动释放:在多线程中,当多个线程访问同一对象时,当一个线程先运行某synchronized关键字修饰的方法出现异常时,该线程持有的锁会自动释放,如果该异常没有被捕获或抛出,则该线程停止运行,但是其他线程会继续运行,因停止线程获得的对象锁被自动释放,所以其他线程会获得对象锁。
d.内置锁是非公平锁。线程在竞争synchronized锁时并不遵循先到等待队列就先获得锁,如果一个线程来请求锁,刚好该锁被释放了,那么这个线程可能会跳过在等待队列中的其它线程直接获得该锁。
e. synchronized关键字不能继承。父类方法中加了synchronized,在子类中覆盖了这个方法,在子类中的这个方法默认情况下并不是同步的,需要重新加锁。
4)内置锁与显示锁的区别
a.锁的释放。内置锁只要运行到同步代码块之外就会自动释放锁;而显示锁必须调用unlock方法才能释放锁。
b.公平性。内置锁不可以选择公平策略,只能是不公平锁; 显示锁可以指定公平策略,默认为不公平锁。
c.可中断申请。 内置锁不可中断申请,在申请锁时被其它线程持有,那么当前线程后挂起,挂起期间不可中断;显示锁提供可中断申请,例如lock.lockInterruptibly(),在申请锁的过程中如果当前线程被中断, 将抛出InterruptedException异常。
d.可尝试申请、可定时早请。 内置锁不提供这种特性;显示锁提供尝试型申请方法,例如Lock.tryLock方法。
e.是否可以精确唤醒特定线程。内置锁的notify或notifyAll方法唤醒在其上等待的线程,但无法指定特定线程;显示锁可以通过Condition对象(由显示锁派生出来),调用Condition.singal或Condition.singalAll方法可以唤醒在该Condition对象上等待的线程,以此来唤醒指定线程。
总结:内置锁够解决大部分需要的场景,当需要额外的灵活时才考虑显示锁,比如公平、可中断、可尝试、可定时、可唤醒特定线程等场景。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值