多线程-2

线程间的通信

  • wait()/wait(long):释放当前对象持有的对象锁,并进入到阻塞态,直到被**notify()/notifyAll()**通知到或者时间到,来竞争对象锁
  • notify():选中通知一个**wait()**进入等待队列的线程来竞争对象锁;
  • notifyAll():通知所有等待的线程来竞争对锁;**

CAS:Compare And Swap (比较并交换):我们假设内存中的原数据V,旧的预期值A,需要修改的新值B。 1. 比较 A 与 V 是否相等。(比较) 2. 如果
比较相等,将 B 写入 V。(交换) 3. 返回操作是否成功。

-CAS的实现:
针对不同的操作系统,JVM 用到了不同的 CAS 实现原理,简单来讲:

  • java 的 CAS 利用的的是 unsafe 这个类提供的 CAS 操作;

  • unsafe 的 CAS 依赖了的是 jvm 针对不同的操作系统实现的 Atomic::cmpxchg;

  • Atomic::cmpxchg 的实现使用了汇编的 CAS 操作,并使用 cpu 硬件提供的 lock 机制保证其原子性。

简而言之,是因为硬件予以了支持,软件层面才能做到。

  • -CAS自旋尝试设置值操作:

自旋实现:

  1. 循环死等
  2. 可中断的方式----->interrupt
  3. 判断循环次数,到达阈值退出
  4. 判断循环总耗时,到达阈值退出

缺点:

  1. 前提(很快运行)满足不了,线程一直处于运行态,循环执行CAS性能消耗大
  2. 线程数量多,导致前提满足不了,或者CPU在很多线程间切换---->性能消耗大

在这里插入图片描述
在这里插入图片描述

乐观锁和悲观锁:设计上解决线程安全的一中思想

  • 乐观锁:乐观锁假设认为数据一般情况下不会产生并发冲突,所以在数据进行提交更新的时候,才会正式对数据是
    否产生并发冲突进行检测,如果发现并发冲突了,则让返回用户错误的信息,让用户决定如何去做。
  • 悲观锁:总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样
    别人想拿这个数据就会阻塞直到它拿到锁。

Callable创建线程:

import java.util.concurrent.*;

/**
 * @author liye
 * @create 2020-06-30-19:52
 * Callable创建线程
 * Future/FutureTask
 */
public class CallableTest {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        Callable<Integer> c= new Callable<Integer>() {
            @Override
            public Integer call() throws Exception {
                System.out.println("call");
                return 1233;
            }
        };
        FutureTask<Integer> task=new FutureTask<Integer>(c);
        //FutureTask实现了Runnable接口,然后传Callable参数到它的构造方法中
        new Thread(task).start();
        System.out.println("main");
        Integer r=task.get();//当前线程阻塞等待,直到线程执行完毕,获取到返回值
        System.out.println(r);
        //线程池使用Callable
        ExecutorService pool= Executors.newFixedThreadPool(4);
        Future future=pool.submit(c);
        System.out.println("main");
        System.out.println(future.get());

    }
}

**synchronized: **

  1. 实现原理:通过对象头加锁,monitor锁机制:编译为字节码时,生成monitorenter,monitorexit;
  2. 分类: 只能升级,不能降级

1.无锁:没有对资源进行锁定,所有的线程都能访问并修改同一个资源,但同时只有一个线程能修改成功,其他修改失败的线程会不断重试直到修改成功。
2.偏向锁:对象的代码一直被同一线程执行,不存在多个线程竞争,该线程在后续的执行中自动获取锁,降低获取锁带来的性能开销。偏向锁,指的就是偏向第一个加锁线程,该线程是不会主动释放偏向锁的,只有当其他线程尝试竞争偏向锁才会被释放。
偏向锁的撤销,需要在某个时间点上没有字节码正在执行时,先暂停拥有偏向锁的线程,然后判断锁对象是否处于被锁定状态。如果线程不处于活动状态,则将对象头设置成无锁状态,并撤销偏向锁;如果线程处于活动状态,升级为轻量级锁的状态。
3.轻量级锁:轻量级锁是指当锁是偏向锁的时候,被第二个线程 B 所访问,此时偏向锁就会升级为轻量级锁,线程 B会通过自旋的形式尝试获取锁,线程不会阻塞,从而提高性能。当前只有一个等待线程,则该线程将通过自旋进行等待。但是当自旋超过一定的次数时,轻量级锁便会升级为重量级锁;当一个线程已持有锁,另一个线程在自旋,而此时又有第三个线程来访时,轻量级锁也会升级为重量级锁。
4.重量级锁指当有一个线程获取锁之后,其余所有等待获取该锁的线程都会处于阻塞状态。

  1. 优化方案:

1.锁粗化:一个线程对一个对象锁反复获取和释放的操作,中间没有其他线程影响,可以合并为一个锁;
2.锁消除:临界区代码中,如果没有对象逃逸出当前线程,可以说本身是线程安全的操作,可以直接不使用锁;

死锁

  • 前提条件:同步的本质在于,一个线程等待另一个线程执行完毕之后才可以继续执行,但是如果现在相关的几个线程之间都在互相等待,那么就会造成死锁;
  • 产生的必要条件

1、互斥使用,即当资源被一个线程使用(占有)时,别的线程不能使用
2、不可抢占,资源请求者不能强制从资源占有者手中夺取资源,资源只能由资源占有者主动释放。
3、请求和保持,即当资源请求者在请求其他的资源的同时保持对原有资源的占有。
4、循环等待,即存在一个等待队列:P1占有P2的资源,P2占有P3的资源,P3占有P1的资源。这样就形成了一个等待环路。

  • 后果:线程阻塞等待,无法向下执行
  • 解决方案

① 资源一次性分配(破坏请求与保持条件)
② 可剥夺资源:在线程满足条件时,释放掉已占有的资源
③ 资源有序分配:系统为每类资源赋予一个编号,每个线程按照编号递 请求资源,释放则相反

  • 检测手段:使用JDK提供的监控工具,如jconsole,jstack查看线程状态;

lock体系:JDK提供的除了synchronized之外的加锁方式,定义了锁对象来进行锁操作;

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

/**
 * @author liye
 * @create 2020-06-30-19:52
 */
public class CallableTest {
    public static void main(String[] args) {
        Lock lock=new ReentrantLock();
        lock.lock();//设置当前线程的同步状态,并在队列中保存线程及线程的同步状态
        try {

        }finally {
            lock.unlock();//线程出队列
        }
    }
}
  • 实现原理:AQS队列式的同步器,当中保存了队列的头尾节点;

1.实现原理:双端队列保存线程及线程同步状态,并通过CAS提供设置同步状态的方法
2.AQS提供的模板方法:1.独占式获取与释放同步状态;2.共享式获取与释放同步状态;3.查询同步队列中线程等待情况;

  • LOCK锁的特点

1.提供公平锁与非公平锁:是否按照入队顺序设置线程同步状态,如果多个线程同时申请加锁操作时是否按照时间顺序操作
2.AQS提供的独占式共享式设置同步状态:1.独占式:只允许一个线程获取锁;2.共享式:允许一定数量的线程共享式获取锁
3.带Reentrant关键字的lock包下的api:可重入锁,代码如下:

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

/**
 * @author liye
 * @create 2020-06-30-19:52
 */
public class CallableTest {
    public static Lock lock=new ReentrantLock();

    public static void t1() {
        try {
            lock.lock();
            System.out.println("t1");
        } finally {
            lock.unlock();
        }
    }
    public static void t2() {
        try {
            lock.lock();
            t1();//t2的锁还没释放,就调用t1()方法,这就叫做重入锁
            System.out.println("t2");
        } finally {
            lock.unlock();
        }
    }
    public static void main(String[] args) {
        t2();
    }
}

ReentrantReadWriteLock提供的读写api

  • 使用场景:多个线程执行某个操作时只允许读读并发/并行操作
  • 特点:1.公平锁,2.重入锁,3.只允许降级(写—>读)
  • 优势:针对读读并发执行,提高效率
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * @author liye
 * @create 2020-06-30-19:52
 */
public class CallableTest {
    private static ReadWriteLock LOCK=new ReentrantReadWriteLock();
    private static Lock readLock=LOCK.readLock();
    private static Lock writeLock=LOCK.writeLock();

    public static void readFile() {
        try {
            readLock.lock();
            //io读文件
        } finally {
            readLock.unlock();
        }
    }

    public static void writeFile() {
        try {
            writeLock.lock();
            //IO写文件
        } finally {
            writeLock.unlock();
        }
    }

    public static void main(String[] args) {
        //20个线程读文件
        for(int i=0;i<20;i++) {

        }
        //20个线程写文件
        for(int i=0;i<20;i++) {

        }
    }
}

Condition

  • 作用:线程间通信
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @author liye
 * @create 2020-06-30-19:52
 */
public class CallableTest {
    private static volatile int SUM=0;
    private static Lock LOCK=new ReentrantLock();
    private static Condition CONDITION =LOCK.newCondition();
    public static void t1() {
        try {
            LOCK.lock();//相当于synchronized ()
            while (SUM>97) {
                CONDITION.await();//相当于Thread.wait()
            }
            SUM+=3;
            System.out.println("面包师傅生产了3个面包"+SUM);
            CONDITION.signalAll();//相当于notifyAll()/notify()
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            LOCK.unlock();
        }
    }
    public static void t2() {
        try {
            LOCK.lock();//相当于synchronized ()
            while (SUM<=0) {
                CONDITION.wait();//相当于Thread.wait()
            }
            SUM--;
            System.out.println("有客人买走一个面包"+SUM);
            CONDITION.signalAll();//相当于notifyAll()/notify()
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            LOCK.unlock();
        }
    }

    public static void main(String[] args) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    t1();
                }
            }
        }).start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    t2();
                }
            }
        }).start();
    }
}

AQS的应用

  • CountDownLatch

1.使用场景:在某个线程A某个地方阻塞等待,直到一组线程执行完毕之后,再执行A后续代码;
2.注意:只提供计数器减的操作

 @Test
    public void t3() throws Exception {
        CountDownLatch cdl=new CountDownLatch(20);
        for(int i=0;i<20;i++){
            Thread t=new Thread(()->{
                System.out.println(Thread.currentThread().getName());
                cdl.countDown();//计数器-1
            });
            t.start();
        }
        //等待所有子线程执行完毕
        cdl.await();//当前线程阻塞等待,直到计数器为零
        System.out.println(Thread.currentThread().getName());
    }
  • Semaphore
  • 使用场景:

1.:在某个线程A某个地方阻塞等待,直到一组线程执行完毕之后,再执行A后续代码;

 @Test
    public void t5() throws Exception {
        Semaphore s=new Semaphore(0);
        for(int i=0;i<20;i++){
            Thread t=new Thread(()->{
                System.out.println(Thread.currentThread().getName());
                s.release();//增加一个资源量
            });
            t.start();
        }
        //等到资源总量为给定数值,如果相等继续执行,否则阻塞等待
        s.acquire(20);
        System.out.println(Thread.currentThread().getName());
    }

2.多线程有限资源的访问

@Test
    public void t4() throws Exception {
        Semaphore s=new Semaphore(1000);
        for(;;){
            Thread t=new Thread(()->{
                //模拟每个线程处理Http请求
                try {
                    //获取一个资源量,总资源量超过1000,阻塞等待
                    s.acquire();
                    System.out.println(Thread.currentThread().getName());

                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    s.release();//释放资源
                }
            });
            t.start();
        }
    }

ThreadLocal

  • 使用场景:隔离线程间的变量,保证每个线程是使用自己的变量
import org.junit.Test;

/**
 * @author liye
 * @create 2020-06-29-14:36
 * ThreadLocal和线程绑定
 */
public class ThreadLocalTest {

    private static ThreadLocal<String> HOLDER=new ThreadLocal<>();


    @Test
    public void t1() {
        try {
//            HOLDER.get();//获取当前线程中,某个ThreadLocal对象的值
//            HOLDER.remove();//删除当前线程中,某个ThreadLocal对象的值
//            HOLDER.set("abc");//设置当前线程中,某个ThreadLocal对象的值
            HOLDER.set("abc");
            for(int i=0;i<20;i++) {
                final int j=i;
                new Thread(()->{
                    HOLDER.set(String.valueOf(j));
                    try {
                        if(j==5) {
                            Thread.sleep(500);
                            System.out.println(HOLDER.get());
                        }
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }finally {
                        HOLDER.remove();
                    }
                }).start();
            }
            while (Thread.activeCount()>1) {
                Thread.yield();
            }
            System.out.println(HOLDER.get());
        } finally {
            HOLDER.remove();
        }
    }
}

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
该资源内项目源码是个人的课程设计、毕业设计,代码都测试ok,都是运行成功后才上传资源,答辩评审平均分达到96分,放心下载使用! ## 项目备注 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用! 2、本项目适合计算机相关专业(如计科、人工智能、通信工程、自动化、电子信息等)的在校学生、老师或者企业员工下载学习,也适合小白学习进阶,当然也可作为毕设项目、课程设计、作业、项目初期立项演示等。 3、如果基础还行,也可在此代码基础上进行修改,以实现其他功能,也可用于毕设、课设、作业等。 下载后请首先打开README.md文件(如有),仅供学习参考, 切勿用于商业用途。 该资源内项目源码是个人的课程设计,代码都测试ok,都是运行成功后才上传资源,答辩评审平均分达到96分,放心下载使用! ## 项目备注 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用! 2、本项目适合计算机相关专业(如计科、人工智能、通信工程、自动化、电子信息等)的在校学生、老师或者企业员工下载学习,也适合小白学习进阶,当然也可作为毕设项目、课程设计、作业、项目初期立项演示等。 3、如果基础还行,也可在此代码基础上进行修改,以实现其他功能,也可用于毕设、课设、作业等。 下载后请首先打开README.md文件(如有),仅供学习参考, 切勿用于商业用途。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值