并发编程---原子类

对多线程访问同一个变量,我们需要加锁,而锁是比较消耗性能的,JDk1.5之后, 新增的原子操作类提供了一种用法简单、性能高效、线程安全地更新一个变量的方式, 这些类同样位于JUC包下的atomic包下,发展到JDk1.8,该包下共有17个类, 囊括了原子更新基本类型、原子更新数组、原子更新属性、原子更新引用。

原子类,不需要加同步资源锁


/**
 * 原子类
 */

public class Demo1 {

    private static AtomicInteger sum = new AtomicInteger(0);

    public static void inCreament(){
        sum.incrementAndGet(); // 自增
    }

    public static void main(String[] args){
        for(int i =0;i<10;i++){
            new Thread(
                    ()->{
                for(int j = 0;j<10;j++){
                    inCreament();
                System.out.println(sum);
                try {
                    Thread.sleep(200L);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            }).start();
        }
    }
}

         原子地更新某个类里的某个字段时,就需要使用原子更新字段类,Atomic包提供了以下4个类进行原子字段更新AtomicIntegerFieldUpdater、AtomicLongFieldUpdater、AtomicStampedReference、AtomicReferenceFieldUpdater

/**
 * AtomicLongFieldUpdaterDemo
 */
public class AtomicLongFieldUpdaterDemo {

    public static void main(String[] args) {
        AtomicLongFieldUpdater<Student> longFieldUpdater = AtomicLongFieldUpdater.newUpdater(Student.class, "id");

        Student xdclass = new Student(1L, "xdclass");
        longFieldUpdater.compareAndSet(xdclass, 1L, 100L);
        System.out.println("id="+xdclass.getId());

        AtomicReferenceFieldUpdater<Student, String> referenceFieldUpdater = AtomicReferenceFieldUpdater.newUpdater(Student.class, String.class, "name");
        referenceFieldUpdater.compareAndSet(xdclass, "xdclass", "wiggin");
        System.out.println("name="+xdclass.getName());
    }
}

class Student{
    volatile long id;
    volatile String name;

    public Student(Long id, String name) {
        this.id = id;
        this.name = name;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

容器:包括同步容器以及并发容器

注意,此处所指的不是dock容器,而是jdk里面的容器,包括同步容器和并发容器
 

同步容器
Vector、HashTable -- JDK提供的同步容器类 Collections.synchronizedXXX 本质是对相应的容器进行包装。

同步容器类的缺点:
        在单独使用里面的方法的时候,可以保证线程安全,但是,复合操作需要额外加锁来保证线程安全 使用Iterator迭代容器或使用使用for-each遍历容器,在迭代过程中修改容器会抛出
ConcurrentModificationException异常。想要避免出现ConcurrentModificationException,就必须在迭代过程持有容器的锁。但是若容器较大,则迭代的时间也会较长。那么需要访问该容器的其他线程将会长时间等待。从而会极大降低性能。 若不希望在迭代期间对容器加锁,可以使用"克隆"容器的方式。使用线程封闭,由于其他线程不会对容器进行修改,可以避免ConcurrentModificationException。但是在创建副本的时候,存在较大性能开销。 toString,hashCode,equalse,containsAll,removeAll,retainAll等方法都会隐式的Iterate,也即可能抛出ConcurrentModificationException。

public class VectorDemo {

    public static void main(String[] args) {
        Vector<String> stringVector = new Vector<>();
        for(int i =0;i<100;i++){
            stringVector.add("demo"+i);
        }
        
        // 获取strignvector里面的元素必须使用迭代器
// 多线程的时候需要对迭代器加锁
        Iterator<String> iterator = stringVector.iterator();
        while(iterator.hasNext()){
           String next =  iterator.next();
           if(next.equals("demo2")){
               iterator.remove();
           }
        }
    }
}

  并发容器CopyOnWrite、Concurrent、BlockingQueue 根据具体场景进行设计,尽量避免使用锁,提高容器的并发访问性。 ConcurrentBlockingQueue:基于queue实现的FIFO的队列。队列为空,取操作会被阻塞ConcurrentLinkedQueue,队列为空,取得时候就直接返回空。

LinkedBlockingQueue的使用:

在并发编程中,LinkedBlockingQueue使用的非常频繁。因其可以作为生产者消费者的中间商
add 实际上调用的是offer,区别是在队列满的时候,add会报异常
offer 对列如果满了,直接入队失败
put("111"); 在队列满的时候,会进入阻塞的状态


remove(); 直接调用poll,唯一的区别即使remove会抛出异常,而poll在队列为空的时候直接返回null
poll(); 在队列为空的时候直接返回null
take(); 在队列为空的时候,会进入等待的状态

public class Demo1 {

    public static void main(String[] args) throws InterruptedException {

        LinkedBlockingDeque<String> string = new LinkedBlockingDeque<>();

        // 往队列里面存放元素   put 和take使用比较多

        string.add("a");
        string.offer("b");
        string.put("c");
        
        // 从队列中取元素

        String remove = string.remove();
        String poll = string.poll();
        String take = string.take();

    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zero _s

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值