首页 > 其他分享 >多线程-进阶2

多线程-进阶2

时间:2024-08-04 16:57:23浏览次数:14  
标签:加锁 进阶 Thread util import new 多线程 public

 博主主页: 码农派大星.

    数据结构专栏:Java数据结构

 数据库专栏:MySQL数据库

JavaEE专栏:JavaEE

关注博主带你了解更多数据结构知识

1.CAS

1.1CAS全称:Compare and swap

比较内存和cpu寄存器中的内容,如果发现相同,就进行交换(交换的是内存和另一个寄存器的内容)

一个内存的数据 和 两个寄存器中的数据进行操作(寄存器1和寄存器2)

此处的"交换"实际上就是赋值

比较内存和寄存器1中的值,是否相等,如果不等,就无事发生,如果相等,就交换内存和寄存器2的值(此处只关心内存交换后的内容,不关心寄存器2交换后的内容)

因此CAS就能编写多线程代码,"无锁化编程"

CAS具体使用场景

1)基于CAS实现原子类 都是线程安全

import java.util.concurrent.atomic.AtomicInteger;
public class Main5 {
private static AtomicInteger count = new AtomicInteger(0);
public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(()->{
            for (int i = 0; i <50000 ; i++) {
             count.getAndIncrement();//count++
//           count.incrementAndGet();//++count
//           count.getAndDecrement();//count--
//           count.decrementAndGet();//--count
     //      count.getAndAdd(10);//count+= 10
            }
        });
        Thread t2 = new Thread(()->{
            for (int i = 0; i <50000 ; i++) {
                count.getAndIncrement();//count++
              /*  count.incrementAndGet();//++count
                count.getAndDecrement();//count--
                count.decrementAndGet();//--count*/
            }
        });
        t1.start();
        t2.start();
        t1.join();
        t1.join();
        System.out.println(count.get());
    }
}

2.Callable接口

Callable 接口也是创建线程的一种方式。相当于把线性封装了一个返回值。

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

public class Main {

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

                }
                return sum;
            }
        };
        FutureTask<Integer> futureTask = new FutureTask<>(callable);
        Thread t = new Thread(futureTask);
        t.start();
        System.out.println(futureTask.get());


    }
}

3.创建线程的方式:

1.直接继承Thead类

2.使用Runnable

3.使用Callable

4.使用lambda

5.使用线程池

4.ReentrantLock

4.1synchroniazed与ReentrantLock区别

1)大多是情况下使用synchronized,属于关键字(底层是通过JVM的c++代码实现的)

ReentrantLock则是标准库提供的类,通过Java代码实现的

2)synchronized通过代码块控制加锁解锁,ReentrantLock通过调用lock unlock方式来完成 unlock可能会遗漏

3)ReentrantLock 提供了tryLock这样的加锁风格

前面介绍的加锁,都是发现锁被别人占用了,就阻塞等待

tryLock在加锁失败的时候,不会阻塞,而是返回,通过返回值来反馈是加锁成功还是失败

4)1Reentrantlock提供了公平锁的实现

默认是非公平的,可以在构造方法中,传入参数,设定成公平的

5)Reentrantlock还提供了功能更强的功能"等待通知机制"

基于Condition类,能力要比wait notify更强一些.

import java.util.concurrent.locks.ReentrantLock;

public class Main2 {
    public static int count = 0;
    public static void main(String[] args) throws InterruptedException {
        ReentrantLock locker = new ReentrantLock();
        Thread t1 = new Thread(()->{
            for (int i = 0; i < 50000; i++) {
                locker.lock();
                count++;
                locker.unlock();
            }
        });
        Thread t2 = new Thread(()->{
            for (int i = 0; i <50000 ; i++) {
                locker.lock();
                count++;
                locker.unlock();
            }
        });
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        System.out.println(count);
    }



}

5.信号量Semaphore

import java.util.concurrent.Semaphore;

public class Main3 {
    public static void main(String[] args) throws InterruptedException {
        Semaphore semaphore =new Semaphore(3);//可硬资源数,计数器初始值

        semaphore.acquire();
        System.out.println("申请一个资源");
        semaphore.acquire();
        System.out.println("申请一个资源");
        semaphore.acquire();
        System.out.println("申请一个资源");
        semaphore.release();
        System.out.println("释放一个资源");
        semaphore.acquire();
        System.out.println("申请一个资源");


    }


}

6.CountDownLatch

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

public class Main4 {
    public static void main(String[] args) throws InterruptedException {
        ExecutorService executorService = Executors.newFixedThreadPool(5);
        //构造方法的数字,就是拆分出来的任务个数
        CountDownLatch countDownLatch =new CountDownLatch(20);

        for(int i = 0 ; i < 20;i++){
            int id = i;
            executorService.submit(()->{
                System.out.println("下载任务"+id+"开始执行");
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                System.out.println("下载任务"+id+"结束任务");
                //完毕!!
                countDownLatch.countDown();
            });
        }

            //当countDownLatch收到20个"完成",所有的任务就完成了
 //await= allwait
            countDownLatch.await();

    }



}

借助CountDownLatch就能衡量出当前任务是否整体执行结束

7.线程安全的集合类

解决方案:

1)自己加锁

2)如果要使用ArraryList/LinkedList这样的结构

标准库中,提供了一个带锁的List

还可以使用CopyOnWrite集合类

写时拷贝的缺点:

1)无法应对,多个线程同时修改的情况

2)如果涉及到数据量很大,拷贝起来非常慢 

3)想多线程环境下使用队列

BlockingQueue

4)多线程环境下使用哈希表

Hashtable虽然是可选项

ConcurrentHashMap[推荐]

此处这个数据结构,相比于HashMa和Hashtable 来说,改进力度非常大的

1)优化了锁的粒度 

Hashtable的加锁,就是直接给put get 的方法加上synchronized,就是给this加锁

整个哈希表对象就是一把锁,任何一个针对这个哈希表的操作,都会触发锁竞争

ConcurrentHashMap则是给每个哈希表中的"链表" 进行加锁.(不是一把锁,而是多把锁)

上述设定方式,是可以保证线程安全的!!

其次可大大降低锁冲突的概率,只有同时进行两次修改,恰好在修改在同一链表元素上的时候,才会

触发锁竞争

2)ConcurrentHashMap引入了CAS原子操作.针对像修改了size这样的操作,直接借助CAS完成,才不会加锁

3)针对读操作,做了特殊处理 .上述的加锁,只是针对写操作,加锁

对于读操作,通过volatile以及一些精巧的代码实现,确保读操作,不会读到"修改一半的数据"

4)针对hash表的扩容,进行了特殊的优化

普通hash表的扩容,需要创建新的hash表,把元素搬过去

这一列操作,很有可能就在一次put中就完成了就会使这次put开销非常大,耗时非常长

ConcurrentHashMap进行了"化整为零" 不会在一次操作中,进行搬运所有数据而是一次搬运一部分,伺候每次操作,都会触发,一部分key的搬运,最终把所有的key都搬运完成

当新旧同时存在的时候

1)插入操作,直接插入到新空间中

2)查询/修改/删除/都需要同时查询旧的空间和新空间

标签:加锁,进阶,Thread,util,import,new,多线程,public
From: https://blog.csdn.net/jj666mhhh/article/details/140770019

相关文章

  • 数组案例练习进阶版---查找数组中的元素
    今天,我们来做一个进阶版的练习,输入一个数字,来判断他在数组中是否存在:这样的话,首先我们就需要有一个能帮助我们输入的工具,那么在Java中它长成什么样子呢?首先我们必须在主方法的第一行写上这样一串代码:Scannerinput=newScanner(System.in); 这样我们就创建了一个输入......
  • JavaEE 第1节 认识多线程
    本节目标(全是重点,都必须掌握)1、了解什么是线程、多线程、进程以及他们之间的关系2、了解多线程的优势以及各种特性3、用Java掌握多种创建线程的方法一、线程、多线程、进程1、概念1.基本概念这三个名词的概念可以用一个餐馆的厨房和厨师来进行超级形象的比喻。想象一下......
  • 算法进阶指南第一题 a^b
    【模板】快速幂题目描述给你三个整数a,b,pa,b,p......
  • 【Redis 进阶】哨兵 Sentinel(重点理解流程和原理)
    Redis的主从复制模式下,一旦主节点由于故障不能提供服务,需要人工进行主从切换,同时大量的客户端需要被通知切换到新的主节点上,对于上了一定规模的应用来说,这种方案是无法接受的,于是Redis从2.8开始提供了RedisSentinel(哨兵)加个来解决这个问题。一、基本概念由于对Red......
  • MySQL进阶(查询、备份与恢复)
    一、多表联合查询在关系型数据库中,表与表之间是有联系的,所以在实际应用中,经常使用多表查询。多表查询就是同时查询两个或两个以上的表。在MySQL中,多表查询主要有交叉连接、内连接、外连接、分组查询与子查询等5种。1、交叉连接(CROSSJOIN)笛卡尔积交叉连接(CROSSJO......
  • Java通过redis实线多线程多用户操作时添加锁
    背景由于项目中多出涉及同步数据,同步过程就是从设备上查询数据,将数据库中该设备数据删除,将新数据导入到数据库;多次同步数据或多用户操作,会导致数据库出现重复数据,例如,两个线程同时删除设备数据,同时导入数据,就会出现双倍数据;还有线程1正在导入数据,中途线程2将线程1导入数据之前删......
  • Java使用多线程池给List赋值导致List存在空的处理
    错误示例:publicList<String>test()throwsNuMaxCloudCommonException{ExecutorServiceexecutorService=Executors.newFixedThreadPool(3);List<String>list=newArrayList<>();for(inti=0;i<3;i++){......
  • MySQL数据分析进阶(八)存储过程
    ※食用指南:文章内容为‘CodeWithMosh’SQL进阶教程系列学习笔记,笔记整理比较粗糙,主要目的自存为主,记录完整的学习过程。(图片超级多,慎看!)【中字】SQL进阶教程|史上最易懂SQL教程!10小时零基础成长SQL大师!!https://www.bilibili.com/video/BV1UE41147KC/?spm_id_from=333.1007.0.......
  • 神经网络训练(二):基于残差连接的图片分类网络(进阶篇②)
    目录日常·唠嗑3基于ResNet18的优化3.1初步构思3.1.1数据预处理3.1.2批量大小3.1.3参数初始化3.1.4optimizer3.1.5学习速率3.2hyper-parameter测试3.2.1批量大小日常·唠嗑       昨天写完了神经网络训练(二):基于残差连接的图片分类......
  • 【QT】Qt 多线程
    多线程qt多线程1.Qt多线程概述2.QThread常用API3.使用线程4.线程安全(1)互斥锁(2)条件变量(3)信号量qt多线程1.Qt多线程概述在Qt中,多线程的处理⼀般是通过QThread类来实现。QThread代表⼀个在应用程序中可以独立控制的线程,也可以和进程中的其他线程共享......