一心多用多线程-线程安全问题

因为我们知道java是平台无关性语言,所以java为了忽略各个平台带来的不同的内存差异,定义了自己的JMM(java内存模型)
即对象产生是放在java堆中,线程使用的是java栈,每一次需要用到对象中的变量时,就要从java堆中抓取该变量的副本,然后进行修改使用后,再把使用后的变量放回java堆中,也就是所谓的read-load-use过程。正因为这样的过程不是原子性的,那么当多线程进行操作时,就会产生线程问题,下面给出一个不安全的例子。

定义一个接口,方便对不安全类的改进

public interface Count {
    void add();
    int getCount();
}

下面定义一个不安全类

public class NotSecureCount implements Count{
    private int count = 0 ;
    public void add(){
        count++;
    }
    public int getCount(){
        return this.count;
    }
}

打开五个线程,每个线程分别调用一次add()方法,我们期望得到的结果是5,然后事实并不是这样

public class MyThread extends Thread {
    private Count count;
    public MyThread(Count count){
        this.count=count;
    }
    @Override
    public void run() {
        this.count.add();
    }
    public static void main(String[] args){
        Count count = new NotSecureCount();
        for(int i=0;i<5;i++){
            Thread thread = new MyThread(count);
            thread.start();

        }
        Thread.sleep(5000);//等五个线程做完事情
        System.out.println("五个线程作用后的结果:"+count.getCount());
    }
}

结果是:

五个线程作用后的结果:4

正因为可视性问题,原子性问题(count++不是原子性操作,count=count+1),所以出现了作用效果不合我们意愿。那么下面我们可以通过几种方法对该非安全类进行改进

1.隐式锁,又称线程同步synchronized
那我们对我们的Count实现类做一次修改,如下:

public class SynchronizedCount implements Count{
    private int count = 0;
//  private byte[] lock = new byte[1];
    @Override
    public synchronized void add() {//在方法上面添加隐式锁
        count++;
        /*synchronized(lock){//或者添加同步代码块,这样的话可以减少里面,
                                        //当一个对象持有该锁时,那么其余被该锁锁定的代码块也不能进入
            count++;
        }*/
    }
    @Override
    public  int getCount() {
        return count;
    }
}

java关键字synchronized提供了隐式锁的功能,可以让线程有序的使用代码块,但是这样不适高并发的情况下,因为这样相当于是串行的,后面的线程必须等前面的线程释放锁之后才能进入代码块,那么有了隐式锁,那显锁呢?

2.显示锁Lock和ReentrantLock,ReentrantReadWriteLock
Lock:这一个锁的接口,主要定义了锁的获取与释放等相关信息。

ReentantLock:可重入锁,是隐式锁的显式使用,区别在于该锁比隐式锁支持的吞吐量比较多,显示锁需要自己手动释放锁,是Lock接口的实现类。

ReentrantReadWriteLock:可重入读写锁,是可重入锁的改进版本,细化了读与写两个方面,他支持的功能是这样的:
加读锁时:读-读 可以进行并发处理,读-写 互斥,要等读锁释放才能进行读
加写锁时:写-写 互斥,写-读 互斥

接下来看修改后的安全类
关于可重入锁

public class ReentrantCount implements Count{
    private int count = 0;
    private ReentrantLock reentrantLock = new ReentrantLock();//使用同一个锁
    @Override
    public void add() {
        reentrantLock.lock();//加锁
        count++;
        reentrantLock.unlock();//释放锁,如果代码快会抛出异常的话就要把释放锁放在finally里面
    }
    @Override
    public int getCount() {
        return this.count;
    }
}

关于读写锁,为了更好的展示读写锁的功能所在,我对count实现类进行一次改进,写锁功能同上不必累述,测试读锁并发时的情况

public class ReadWriteCount implements Count{
    private int count = 0;
    private  ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();
    private Lock readLock = reentrantReadWriteLock.readLock();
    private Lock writeLock = reentrantReadWriteLock.writeLock();
    @Override
    public void add() {
        writeLock.lock();
        count++;
        writeLock.unlock();
    }
    @Override
    public int getCount() {
        readLock.lock();//加锁
        System.out.println("即将对count进行获取");
        System.out.println("count:"+count);
        System.out.println("获取结束");
        readLock.unlock();//解锁
        return count;
    }
}

使用五个线程并发

public class MyThread extends Thread {
    private Count count;
    public MyThread(Count count,String name){
        super(name);
        this.count=count;
    }
    @Override
    public void run() {
        this.count.getCount();
    }
    public static void main(String[] args) throws InterruptedException{
        Count count = new ReadWriteCount();
        for(int i=0;i<5;i++){
            Thread thread = new MyThread(count,i+"");
            thread.start();
        }
    }
}

结果:

即将对count进行获取
即将对count进行获取
即将对count进行获取
count:0
获取结束
即将对count进行获取
即将对count进行获取
count:0
获取结束
count:0
获取结束
count:0
获取结束
count:0
获取结束

可以看到加读锁时,都是读锁的时候可以容纳并发的状态,所以又提高了并发量,不得不佩服java线程的这种锁思想

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值