Java线程第二弹--资源冲突问题

接着上一篇,梳理java线程资源冲突的问题,也是我们编程中遇到的,在什么时候考虑资源冲突,该如何解决呢?

当我们处理数据,会有多个线程或多出访问修改数据是,就需要考虑资源共享的问题 。(Brian同步规则:如果我们写的一个变量,他可能接下来将被另一个线程读取,或者正在读取一个上一次已经被另一个线程写过的变量,必须使用同步。

解决资源冲突的问题就是采用序列化访问共享资源的方案
共享资源:一般以对象的形式存储在内存片段,但也可以是文件,输入/输出端口,要控制对共享资源的方法,一般是把资源封装在一个对象里,然后对其使用Synchronized等关键字标记。
当任务执行时,他将检查锁是否可用,然后获取锁,执行代码,释放锁
主要方法:

1,Synchronized关键字:
1),每个对象都自动含有单一的锁:当在对象上调用任意的synchronized方法时,此对象就会被加上锁,此时该对象的其他Synchronized方法必须等到前一个方法释放锁,才能调用
2)对象里的所有Synchronized方法共享同一个锁,防止对个任务同时被编码为内存对象
3)使用Synchronized时,域必须是private的,否者没有作用
4)在一个synchronized方法的内部调用synchronized方法,锁会递加,释放锁递减,直到标志为零时,完全释放锁。
5)在一个对象内,有多个方法对某个数据进行访问和修改,必须都加锁,否者其他方法将忽略锁,可以在任何情况下对该数据进行读写。
2.Lock关键字
使用:必须被显示的创建,锁定和释放。一般在开始加锁,try语句里进行相关的操作,finally里释放锁。且return语句必须在try里,确保unlock过早发生,造成数据的暴露。
代码:

package resouceHandler;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * Created by acer on 2016/2/28.
 */
public class LockEventGenetor extends IntGenetor {
    private int countValue = 0;
    private Lock lock = new ReentrantLock();
    @Override
    public int next() {
        lock.lock();
        try{
            ++countValue;
            Thread.yield();
            ++countValue;
            return countValue;
        } finally {
            lock.unlock();
        }

    }
}

区别:lock可以解决synchronized关键字的所有问题,但lock更加灵活,可以自己控制什么时候加锁和释放锁。但代码比较多,使用比较复杂。同时当在同步的过程中抛出异常,synchronized无法释放锁,无法进行处理工作,但lock可以释放,后续的处理。
Synchronized使用简单,出错的机会小。
一般在特殊问题才会使用lock。例如:synchronized无法获取锁,或获取一段时间后要放弃锁。
3,volatile关键字
确保了应用中的可视性:如果一个域声明为volatile,对它的读写操作,其他读操作都会看到,即使使用了本地缓存。因为被声明volatile的域,会立即写入到主存中。后面的文章会介绍线程的内存模型。
使用规则:
1)如果一个域完全被synchronized方法或synchronized语句块锁定,无须使用volatile。
2)如果一个域的值依赖与他的上一个值或受制于其他域,volatile无法工作。例如Range的lowe和upper
3)线程或任务的任何读写操作对该任务都是可视的,都在工作内存里,如果只须在任务内部可视,不须使用volatile关键字
4)当类中只有一个可变的域时,使用volatile。
如果一个域需要被多个任务同时访问,或至少一个任务有写的操作,就要考虑使用volatile关键字

4,Synchronized的另一种用法:同步块
比同步方法性能更高。我们只是需要锁定某一个对象,或某一块而不是整个方法,此时可以使用同步块
1)自身同步
2)其他对象上同步

5,线程本地存储
根除共享资源上产生冲突的第二种方式是根除对变量的共享,相同的变量对不同的线程都创建不同的存储。即如果有五个线程都对x进行读写操作,那么线程本地存储就要生产五个用于x的存储块。
ThreadLocal通常当做静态域的存储,创建ThreadLocal时,只能通过get和set方法得到域,其中get方法返回与其线程相关的对象的副本,set方法会将参数插入到为其线程存储的对象中,并返回存储中原有的对象
代码:
6原子类:原子类为底层实现,因此只在特殊的情况下才使用,即便使用了也要确保不存在其他问题,通常依赖synchronized更安全些
常见的原子类:AutomInteger AutomLog AutomReference

下面具体演示实例的简单代码:

package resouceHandler;

/**
 * Created by acer on 2016/2/28.
 */
public abstract class IntGenetor {

    private volatile boolean cancel = false;

    public abstract int next();
    public void cancel () {
        cancel = true;
    }

    public boolean isCancel() {
        return  cancel;
    }
}
package resouceHandler;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**整数检测器的具体是西安
 * Created by acer on 2016/2/28.
 */
public class EventGenetor extends IntGenetor {
    private int currentValut = 0;
    private int count = 5;
    //演示冲突问题方法
//    @Override
//    public int next() {
//
        如果不存在资源冲突,检测器就不会打印相关语句,一直都是偶数
//            ++currentValut;
//            ++currentValut;
//            return currentValut;
//
//    }


//    使用锁
    @Override
    public synchronized int next() {

//        如果不存在资源冲突,检测器就不会打印相关语句,一直都是偶数
        ++currentValut;
        ++currentValut;
        return currentValut;

    }
}
package resouceHandler;

import Util.PrintUtil;

/**
 * Created by acer on 2016/2/28.
 */
public class EventChecker implements Runnable {

    private IntGenetor intGenetor;
    private final  int id;

    public EventChecker(IntGenetor intGenetor, int id) {
        this.intGenetor = intGenetor;
        this.id = id;
    }

    @Override
    public void run() {
        while (!intGenetor.isCancel()) {
            int value = intGenetor.next();
            //如果不存在资源冲突,检测器就不会打印相关语句,一直都是偶数
            if (value % 2 != 0) {
                PrintUtil.printb(value + " not even");
                intGenetor.cancel();
            }
        }
    }
}
package resouceHandler;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * Created by acer on 2016/2/28.
 */
public class TestResouce {

    public static void main(String args[]) {
        test(new LockEventGenetor());
    }

    public static void test(IntGenetor genetor,int count) {
        ExecutorService serv = Executors.newCachedThreadPool();
        for (int i=0; i< count; i++) {
            serv.execute(new EventChecker(genetor,i));
        }
        serv.shutdown();
    }

    public static void test(IntGenetor genetor) {
        test(genetor,2);
    }
}

写完第三篇以后,我会把代码的github地址贴出,大家感兴趣可以看一下,环境大家code完善。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值