多线程8

void 取款(){

int oldBalance =balance
if(!CAS(balance,oldBalance,oldBalance-500)){

}

}

在这个线程中如果变成了这样

void 取款(){

int oldBalance =balance

void 取款(){

int oldBalance =balance
if(!CAS(balance,oldBalance,oldBalance-500)){

}有人转账发生了500->1000。

}
if(!CAS(balance,oldBalance,oldBalance-500)){

}

}

这两个东西不相等,就会产生bug,上述的过程就属于ABA问题的典型bug场景是非常高极端的情况

如何避免ABA问题呢??

核心思路是,使用账户余额判定,本身就不太科学.账户余额本身就属于"能加也能减”,就容易出现ABA问题,

引入"版本号”,约定版本号,只能加,不能减~~每次操作一次余额,版本号都要+1通过CAS判定版本号,就可以进一步的来完成上述的操作~~

一个版本号走一步

JUC中的常见类

Callable接口

public class Test {

    private  static int sum=0;
    public static void main(String[] args) throws InterruptedException {
        Thread t=new Thread(new Runnable() {
           @Override
           public void run() {
               int result=0;
               for (int i = 0; i <=1000 ; i++) {
                   result+=i;
               }
               sum=result;

           }
        });

        t.start();
        t.join();
        System.out.println(sum);
    }
}

    Thread t=new Thread(callable)

Callable不能直接填写到Thread构造方法中

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

public class Test1 {
    public static void main(String[] args) throws InterruptedException, ExecutionException {
        Callable<Integer> callable=new Callable<Integer>() {
            @Override
            public Integer call() throws Exception {
                int result=0;
                for (int i = 0; i <10000 ; i++) {
                    result+=i;
                }

                return result;
            }
        };
        //这是FutureTask的包装类
        FutureTask<Integer>futureTask=new FutureTask<>(callable);
        Thread thread=new Thread(futureTask);
        thread.start();
        thread.join();
        System.out.println(futureTask.get());
    }
}

FutureTask就像中间上来赚取差价。靠他们中间。

线程创建的方式:

1)继承Thread

2)使用Runnable

3)使用lambda

4)使用线程池/ThreadFactory

5)使用Callable

ReentrantLock

import java.util.concurrent.locks.ReentrantLock;

public class Test2 {
    public static void main(String[] args) {
        ReentrantLock lock = new ReentrantLock();
        try {
            lock.lock();
        }finally {
            lock.unlock();
        }

    }
}

1.ReentrantLock提供了公平锁的实现synchronized只是非公平锁。

  ReentrantLock lock = new ReentrantLock(true);

2.ReentrantLock提供tryLock操作.

给加锁提供了更多的课操作空间尝试加锁,如果锁已经被获取到了,直接返回失败,而不会继续等待。

Synchronized都是遇到锁竞争,就阻塞等待.(死等)

tryLock除了直接返回这种形态之外还有一个版本,可以指定等待超时时间

3.synchronized是搭配waitnotify等待通知机制

ReentrantLock是搭配Condition类完成等待通知Condition要比waitnotify更强一点(多个线程wait,notify是唤醒随机的一个.Condition指定线程唤醒)

信号量Semaphore

信号量就是一个计数器,描述了可用资源的个数

围绕信号量有两个基本操作

1.P操作.计数器-1.申请资源

2.V操作.计数器+1.释放资源

通过Semaphore完成

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;

public class Test2 {
  
        private final static Semaphore semaphore = new Semaphore(1);

        public static void main(String[] args){
            ExecutorService pools = Executors.newCachedThreadPool();

            for (int i=0 ; i < 10;i++){
                final int index = i;
                Runnable run = new Runnable() {
                    @Override
                    public void run() {
                        try {
                            semaphore.acquire();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        } finally {
                            //使用完成释放锁
                            semaphore.release();
                            System.out.println("锁释放");
                        }
                    }
                };
                pools.execute(run);
            }
            pools.shutdown();
        }

}

CountDownLatch

多线程下载.

把一个大的文件,拆分成多个部分.比如20个部分

每个线程都独立和人家服务器建立连接,分20个连接进行下载

等所有线程下载完毕之后,再结果进行合并.

CountDownLatch

import java.util.concurrent.CountDownLatch;

public class Test3 {
    public static void main(String[] args) throws InterruptedException {
        CountDownLatch latch = new CountDownLatch(10);
        latch.countDown();
        latch.await();//await会阻塞等待,一直到countDown调用的次数,和构造方法指定的次数一致的时候,,await才会返回
    
    }
}

Vector自带了synchronized加锁不能保证线程一定安全

Stack继承自Vector,也自带了synchronized不加锁也不能确定线程一定不安全

Hashtable也是自带synchronized一定要具体代码具体分析~~

 Vector<String>vector=new Vector<>();
    void  func(){
        if(vector.size()>0){
            System.out.println(vector.get(0));
        }
    }

比如t1纟线程执行func到if之后t2线程把巴vector清空了回到t1继续执行,get(0)就出问题了,

  Collections.synchronizedList(new ArrayList)

给ArrayList这些集合类,套一层壳

壳上是给关键方法都加了synchronized就可以使ArrayList达到类似于Vector效果。

CopyOnWriteArrayList

写时拷贝

修改的同时,很多线程在读呢如果直接修改,不加任何处理意味着有的线程可能会读到200,3这种“中间情况。

写完之后,用新的数组的引用,代替旧的数组的引用(3|用赋值操作,是原子的)上述过程,没有任何加锁和阻塞等待,也能确保读线程不会读出“错误的数据旧的空间就可以释放了。

多线程使用队列

直接使用BlockingQueue即可.

HashMap是线程不安全的Hashtable是带锁的,是否就线程更安全呢??但是这个并不推荐使用

Hashtable加锁就是简单粗暴给每个方法加了synchronized.就相当于是针对this加锁.只要针对Hashtable上的元素进行操作,就都会涉及到锁冲突, 

 ConcurrentHashMap做出了优化

1.使用“锁桶”的方式,来代替“一把全局锁”,有效降低锁冲突的概率

 另一方面,看起来锁对象多了,实际上也不会产生更多的额外开销Java中每个对象都可以作为锁对象就只需要把synchronized加到链表

一个hash表,上面的hash桶的个数是非常多~~大部分的操作,都没有锁冲突了,(synchronized,如果不产生锁冲突,就是个偏向锁)

2.像hash表的size,即使你插入的元素是不同的链表上的元素,也会涉及到多线程修改同一个变量

3.针对扩容操作做了特殊优化

如果发现负载因子太大了,就需要扩容扩容是一个比较重量比较低效的操作。

化整为零.ConcurrentHashMap会在扩容的时候,搞两份空间.

一份是之前扩容之前的空间一份是扩容之后的空间接下来每次进行hash表的基本操作,都会把一部分数据从I旧空间搬运到新空间

搬的过程中:1.插入=>插入到新的上面2.删除=>新的日的都要删除3.查找=>新的日的都要查找

 java8之前,ConcurrentHashMap基于分段锁的方式实现的,

引l入若干个锁对象,每个锁对象管理若干个hash桶相比于Hashtable是进化,但是不如现在直接锁桶代码写起来更复杂

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值