首页 > 其他分享 >JUC及多线程,线程安全

JUC及多线程,线程安全

时间:2024-06-10 10:11:10浏览次数:16  
标签:JUC Thread System 线程 println new 多线程 out

JUC及多线程

返回到 Java开发知识汇总 目录 @程序员猴哥

1.什么是JUC

java.util.concurrent:核心并发工具类。

java.util.concurrent包含了许多线程安全,测试良好,高性能的并发模块。创建java.util.concurrent的目的就是要实现Collection框架对数据结构所执行的并发操作。

核心组件

Executor
ExecutorService
ScheduledExecutorService
Future
CountDownLatch
CyclicBarrier
Semaphore
ThreadFactory
BlockingQueue
DelayQueue
Locks
Phaser
传统程序员的4大利器:泛型,枚举,反射,注解
新程序员的4大利器:lambda表达式(str)->{return 1024},链式编程Array.asList(u1,u2),函数式接口FunctionInterface,stream流式编程list.stream().filter()

2.线程和进程

线程:程序语句快,是一个单独的资源类

进程:程序语句块的集合,通常一个应该程序至少包含一个进程.

3.Lock锁(重点)

同一时刻只允许有一个线程访问代码块的机制,是为了保证线程安全而生(存储数据的集合数据安全)。

创建锁的3种方式:继承Thread类,实现接口runable,callable

syschronized:队列,锁,通一时间只有一个队列消费
区别
1、synchronized是java关键字,而Lock是java中的一个接口
2、synchronized会自动释放锁,而Lock必须手动释放锁
3、synchronized是不可中断的,Lock可以中断也可以不中断
4、通过Lock可以知道线程有没有拿到锁,而synchronized不能
5、synchronized能锁住方法和代码块,而Lock只能锁住代码块
6、Lock可以使用读锁提高多线程读效率
7、synchronized是非公平锁,ReentranLock可以控制是否公平锁
1 lock():获取锁,如果锁被暂用则一直等待 
2 unlock():释放锁 
3 tryLock(): 注意返回类型是boolean,如果获取锁的时候锁被占用就返回false,否则返回true
4 tryLock(long time, TimeUnit unit):比起tryLock()就是给了一个时间期限,保证等待参数时间 
5 lockInterruptibly():用该锁的获得方式,如果线程在获取锁的阶段进入了等待,那么可以中断此线程,先去做别的事
任何一个新的技术是对已有技术的优化和补充,lock condition 是

4.生产者和消费者问题

要判断是锁对象还是锁的类,静态类是同一个对象

使用if会存在虚假唤醒,需使用while

Synchronized(wait,notify),Lock(ReentrantLock,newCondition,lock,unlock,await,signalAll) 可以精准的控制线程唤醒:

5. 8锁现象 ,关于锁的8个问题

1.两个线程使用的是同一个对象(new Phone())。两个线程是一把锁!先调用的先执行!

2.两个线程使用的是2个对象(new Phone())。两个线程使用的是各自的锁!执行顺序有cpu策略决定。

3.两个线程使用的是2个类,两个线程使用的是相同的对象,同一把锁!执行顺序有cpu策略决定。

6.集合类不安全

多线程操作同一个集合时产生脏数据问题,需要添加锁或使用线程安全的集合。

LIst,set不安全

      //Map<String,String> map=new HashMap<>();是线程不安全的
        //List<String> list=new ArrayList<>();是线程不安全的
        
        //List<String> list=new Vector<>();  //vector是线程安全的
        //List<String> list= Collections.synchronizedList(new ArrayList<>()); //synchronizedList是线程安全的
        //List<String> list=new CopyOnWriteArrayList<>();
        //Set<String> list=new CopyOnWriteArraySet<>();
        //Map<String,String> list=new ConcurrentHashMap<>();

CopyOnWriteArrayList是ArrayList的线程安全版本,内部也是通过数组实现,每次对数组的修改都完全拷贝一份新的数组来修改,修改完了再替换掉老数组,这样保证了只阻塞写操作,不阻塞读操作,实现读写分离。

7.Callable

创建多线程的一个接口,可以有返回值和能抛出异常

public class Test1 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        // Thread(Runnable)
        // Thread(RunnableFuture)
        // Thread(FutureTask)

        MyThread myThread = new MyThread();
        FutureTask task = new FutureTask(myThread); // 适配类

        // 会打印几次 end
        new Thread(task,"A").start(); // 执行线程
        new Thread(task,"B").start(); // 执行线程。细节1:结果缓存!效率提高N倍
        System.out.println(task.get());// 获取返回值, get()
        
    }
}


class MyThread implements Callable<Integer> {

    @Override
    public Integer call() throws Exception {
        System.out.println("end");
        TimeUnit.SECONDS.sleep(3);
        return 1024;
    }

}

8.常用的辅助类(必会)

1.CountDownLatch:减法计数器.等待所有的执行完成

CountDownLatch countDownLatch = new CountDownLatch(5);//减法计数器
for (int i = 0; i < 5; i++) {
    new Thread(()->{
        System.out.println(Thread.currentThread().getName()+"go out");
        countDownLatch.countDown(); //减1
    },"a").start();
}

countDownLatch.await();  //等待所有的执行完成
System.out.println("close door");//最后执行

2.CyclicBarrier加法计数器:等待直到7个线程执行完成

CyclicBarrier cyclicBarrier = new CyclicBarrier(7,()->{
    System.out.println("召唤神龙成功");
});

for (int i = 0; i < 7; i++) {
    final int tem=i;
    new Thread(()->{
        System.out.println(Thread.currentThread().getName()+"shouji" +tem+"ge longzhu");
        try {
            cyclicBarrier.await();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } catch (BrokenBarrierException e) {
            throw new RuntimeException(e);
        }
    }).start();
}

3.Semaphore 同一时刻只允许3个线程执行,完成后另外3个线程再继续执行

// 线程数量:停车位有3个,开始停车3俩,等待1秒后释放,另外3个停车
Semaphore semaphore = new Semaphore(3);
for (int i = 0; i < 10; i++) {
    new Thread(()->{
        try {
            semaphore.acquire();//抢到线程 若已满则等待
            System.out.println("抢到线程");
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }finally {
            semaphore.release(); //释放
        }
    },String.valueOf(i)).start();
}

9.读写锁

ReadWriteLock:先批量一个个写入,然后再多线程读:writeLock(),readLock()

独占锁:写锁 一个一个写 单线程

共享锁:读锁,多个线程一起读

private volatile Map<String,Object> map=new HashMap<>();
//ReadWriteLock:先批量一个个写入,然后再随机读
private ReadWriteLock readWriteLock= new ReentrantReadWriteLock();
public void put(String key,Object value){
    readWriteLock.writeLock().lock();
    try{
        System.out.println(Thread.currentThread().getName()+"write"+key);
        map.put(key,value);
        System.out.println(Thread.currentThread().getName()+"write ok");
    }catch (Exception e){
        e.printStackTrace();
    }finally {
        readWriteLock.writeLock().unlock();
    }
}

public void get(String key){
    readWriteLock.readLock().lock();
    try{
        System.out.println(Thread.currentThread().getName()+"get"+key);
        Object o= map.get(key);
        System.out.println(Thread.currentThread().getName()+"get ok");
    }catch (Exception e){
        e.printStackTrace();
    }finally {
        readWriteLock.readLock().unlock();
    }
}

执行结果:
1write1
1write ok
2write2
2write ok
0write0
0write ok
4write4
4write ok
3write3
3write ok
0get0
2get2
3get3
1get1
4get4
4get ok
0get ok
1get ok
2get ok
3get ok

10.阻塞队列

BlockingQueue

队列:FIFO 先进先出

写入队列元素,写入满了,需要阻塞

取队列元素,若元素是空的,则需阻塞

Colleciton:
1.List:
2.Set:
3.Queue:
3.1 blockingQueue:阻塞队列
3.2 AbstractQueue:非阻塞队列
3.3 Deque:双端队列

ArrayBlockingQueue arrayBlockingQueue=new ArrayBlockingQueue(5);
arrayBlockingQueue.add("a");
arrayBlockingQueue.remove(); //没有元素,跑异常

arrayBlockingQueue.put("a");
arrayBlockingQueue.take();  //没有一直等到死

arrayBlockingQueue.offer("a");
arrayBlockingQueue.offer("b",2, TimeUnit.SECONDS);
arrayBlockingQueue.poll();
arrayBlockingQueue.poll(2,TimeUnit.SECONDS);  //没有元素是等待2秒

使用场景:多线程并发处理,线程池

image-20240607174215475

同步队列:SynchronousQueue,添加一个,取出1个

BlockingQueue<String> blockingQueue=new SynchronousQueue<>();

new Thread(()->{
    System.out.println("Thread.currentThread().getName() = " + Thread.currentThread().getName());
    try {
        blockingQueue.put("1");
        System.out.println(Thread.currentThread().getName()+"=>put 1");
        blockingQueue.put("2");
        System.out.println(Thread.currentThread().getName()+"=>put 2");
        blockingQueue.put("3");
        System.out.println(Thread.currentThread().getName()+"=>put 3");
    } catch (InterruptedException e) {
        throw new RuntimeException(e);
    }
},"t1").start();

new Thread(()->{
    try {
        TimeUnit.SECONDS.sleep(2);
        System.out.println(Thread.currentThread().getName()+"=>"+blockingQueue.take());
        TimeUnit.SECONDS.sleep(2);
        System.out.println(Thread.currentThread().getName()+"=>"+blockingQueue.take());
        TimeUnit.SECONDS.sleep(2);
        System.out.println(Thread.currentThread().getName()+"=>"+blockingQueue.take());
    } catch (InterruptedException e) {
        throw new RuntimeException(e);
    }
},"t2").start();

11. 线程池(重点)

 // 线程池 总数可以管理 Executors原生三大方法
 ExecutorService threadpool1 = Executors.newFixedThreadPool(10); // 固定大小
// ExecutorService threadpool2 = Executors.newCachedThreadPool(); //可以弹性伸缩的线程池,遇强则强
 //ExecutorService threadpool3 = Executors.newSingleThreadExecutor(); // 只有一个


 try {
     // 10个线程,会显示几个线程~
     for (int i = 1; i <= 100; i++) {
         // 线程池,执行线程
         threadpool1.execute(()->{
             System.out.println(Thread.currentThread().getName()+" running...");
         });
     }
 } catch (Exception e) {
     e.printStackTrace();
 } finally {
     // 线程池关闭
     threadpool1.shutdown();
 }

12. 四大函数式接口(必需掌握)

只有一个方法的接口称为函数式接口

13.Stream流式计算

可以将集合转为stream后利用stream过滤,转换,求和,求最大值,排序,limit等方式操作集合复制过滤,复制排序的方法。

14. ForkJoin

  • Java 1 支持thread,synchronized。
  • Java 5 引入了 thread pools, blocking queues, concurrent collections,locks, condition queues。
  • Java 7 加入了fork-join库。有空闲时帮助消费
  • Java 8 加入了 parallel streams。

15.异步回调

有返回值的异步回调:ComletableFuture com=ComletableFuture.suppLyAsync(()->{return 1024;});

无返回值的异步回调

16. JMM

Java内存模型,是一种概念

加锁解锁是同一把锁

工作线程,主内存

17. Volatile

1.保证可见性

2.不保证原子性:不可分割,同时成功同时失败

3.禁止指令重排

volatile 读取的时候去主内存中读取在最新值!
     private  volatile static int num = 0;

    public static void main(String[] args) throws InterruptedException { // Main线程

        new Thread(()->{ // 线程A 一秒后会停止!  0
            while (num==0){

            }
        }).start();

        TimeUnit.SECONDS.sleep(1);

        num = 1;
        System.out.println(num);

    }

18. 彻底玩转单例模式

19.深入理解CAS

20. 原子引用

各种锁的理解:公平锁、非公平锁、可重入锁、自旋锁、死锁

标签:JUC,Thread,System,线程,println,new,多线程,out
From: https://www.cnblogs.com/chenshaojun2008/p/18240423

相关文章

  • 【QT5】<总览五> QT多线程、TCP/UDP
    文章目录前言一、QThread多线程二、QT中的TCP编程1.TCP简介2.服务端程序编写3.客户端程序编写4.服务端与客户端测试三、QT中的UDP编程1.UDP简介2.UDP单播与广播程序前言承接【QT5】<总览四>QT常见绘图、图表及动画。若存在版权问题,请联系作者删除!一、QThre......
  • android主线程与子线程
    创建子线程创建子线程在android中穿件子线程的方案很简单创建子线程的几种方法///////第一种///////classThreadoneextendsThread{@Overridepublicvoidrun(){}//重写run方法,这个方法就是子线程一旦启用就会执行的方法}newThreadone().start()//启动子线程/......
  • python 字典是不是线程安全的
    Python字典(dict)对象本身不是线程安全的。在多线程环境下,对同一个字典对象的读写操作需要额外的同步机制来确保线程安全性。如果需要在多线程环境下使用线程安全的字典,可以使用collections.Counter对象,它是线程安全的,或者使用threading.local,它提供了线程局部存储的功能。另外......
  • 线程池的实现代码分析
    [toc]线程池线程池代码分析thread_pool.c#include"thread_pool.h"voidhandler(void*arg){ printf("[%u]isended.\n", (unsigned)pthread_self()); //打印自己的进程号 pthread_mutex_unlock((pthread_mutex_t*)arg); //解锁}//线程要执行的任......
  • python 多任务之多线程
    多线程线程是程序执行的最小单位,实际上进程只负责分配资源,而利用这些资源执行程序的是线程,也就是说进程是线程的容器,一个进程中最少有一个线程来负责执行程序,它可以与同属一个进程的其它线程共享进程所拥有的全部资源 为什么要选择线程,而不选择进程进程:就像同时和两个人......
  • Qt 子线程调用connect/QMetaObject::invokeMethod 不调用槽函数问题
    在使用invokeMethod进行跨线程调用的时候,发现invokeMethod在某些情况下不能正常调用.经过查各种资料发现invokeMethod底层的调用逻辑是通过Qt事件循环处理,所以子线程需要显示的调用QEventLoop::exec()或者QCoreApplication::processEvents()执行信号槽处理.首先有一个QDemoObje......
  • 跟着GPT学习Java线程中断机制
    Java中的线程中断是一个复杂但非常重要的概念,它允许一个线程告知另一个线程希望它停止正在做的事情。这个机制是协作式的,意味着被请求中断的线程需要自己检查中断状态,并且决定如何响应中断请求。下面我将系统地讲解Java中的线程中断知识点。 1.中断标志每个线程都有一个内部......
  • 定时器更新界面,线程报错
    项目场景:在javafx框架下使用线程更新UI的时候,出现无法正常更新UI。问题代码如下:packageclock;importjava.util.Calendar;importjava.util.GregorianCalendar;importjava.util.Timer;importjava.util.TimerTask;importjavafx.application.Application;importj......
  • 创建线程池工具类
    线程池具有以下属性:核心线程数:CORE_POOL_SIZE最大线程数:MAX_POOL_SIZE线程保持活跃时间:KEEP_ALIVE_SECONDS时间单位:TimeUnit.MILLISECONDS任务队列:LinkedBlockingQueue,容量为QUEUE_CAPACITY线程工厂:为每个新线程设置名称前缀"lwc-pool-"和一个自增的编号拒绝策略:Thread......
  • 利用线程池给客户端传文件
    主函数#include<func.h>#include"threadPool.h"#include"server.h"#defineEVENTSNUM100intpipefd[2];voidsigHandle(intsigno){printf("%dsignal\n",signo);intexitNum=1;write(pipefd[1],&exitN......