JUC——并发编程—第二部分

news2024/11/27 4:36:10

集合类不安全

list不安全


//报错 java.util.ConcurrentModificationException
public class ListTest  {
    public static void main(String[] args) {
        List<String>  list= new CopyOnWriteArrayList<>();
        //并发下Arrayist边读边写会不安全的
        /**
         * 解决方案:
         * 1.List<String>  list= new Vector<>();
         * 2.List<String>  list= Collections.synchronizedList(new ArrayList<>());
         * 3.List<String>  list= new CopyOnWriteArrayList<>();
         */
        for(int i=0;i<50;i++){
            new Thread(()->{
                list.add(UUID.randomUUID().toString().substring(0,5));
                System.out.println(list);
            },String.valueOf(i)).start();
        }
    }
}

上面这个多个线程边读边写时会出现如下报错

java.util.ConcurrentModificationException

在CopyOnWriteArrayList的底层用一个这样的数据实现

volatile内存,模型中规定保证线程间的可见性,但不保证原子性

CopyOnWrite使用的是lock锁,Vertor使用的是synchronized,有sync都会很慢。

list的解决方案有使用vector这个安全类和工具类和 juc.

工具类是将其变成synchronized,但很慢,juc是用写入时复制。

Set不安全

set和List是一个同级的关系,都是Collection的子类。

所以set在边读边写时也会有java.util.ConcurrentModificationException报错。

但是set没有vector,只有工具类和juc的解决方案。

public class SetList {
    public static void main(String[] args) {
        Set<String> set = new HashSet<>();
//        Set<String> set = Collections.synchronizedSet(new HashSet<>());
//        Set<String> set = new CopyOnWriteArraySet<>();
        for(int i=0;i<50;i++){
            new Thread(()->{
                set.add(UUID.randomUUID().toString().substring(0,5));
                System.out.println(set);
            },String.valueOf(i)).start();
        }
    }
}

hashset底层就是hashmap。

hashset的add方法就是hashmap的put方法。

map不安全

这个也有" java.util.ConcurrentModificationException报错

 这里的解决方案是juc下的ConcurrentHashMap。

public class MapList {
    public static void main(String[] args) {
        //加载因子,初始化容量 0.75和16
//        Map<String,String> map=new HashMap<>();
        Map<String,String> map=new ConcurrentHashMap<>();

        for(int i=0;i<50;i++){
            new Thread(()->{

                map.put(Thread.currentThread().getName(),UUID.randomUUID().toString().substring(0,5));
                System.out.println(map);
            },String.valueOf(i)).start();
        }
    }
}

走进Callable

callable接口就类似于runnable接口,然而runnable接口不会返回结果也不会抛出异常,callable就可以。一个是call()方法,一个是run()方法.

callable接口需要提供一个泛型,泛型的参数等于方法的返回值。 

如何用new Thread().start接收callable接口并开启线程

Thread()只能接收runnable参数,不认识callable()参数,所以callable要通过runnable去做一个桥梁,在runnable里面有如下的一些实现。

其中FutureTask这个实现类与Callable有联系,如下所示,有一个构造参数就是Callable<V>

这里用到的应该就是适配器模式,这里的futuretask就是一个适配器。

1.两个同样的callable实现类开启的线程内的输出结果会被缓存。

2.结果可能会等待,会阻塞。


public class CallavkeTest {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
      //   new Thread(new Runnable()).start();  //传统方式
//         new Thread(new FutureTask<V>()).start();
//         new Thread(new FutureTask<V>(Callable)).start();
        mythread mythread=new mythread();   //callable接口的实现类
        FutureTask futureTask = new FutureTask(mythread);        //callable接口的适配类
        new Thread(futureTask,"A").start();
        new Thread(futureTask,"B").start();  //只会输出一个call,结果被缓存了,达到提高效率的目的
        String str=(String)futureTask.get(); //获取callable的返回结果,get方法会等待结果,可能产生阻塞,要将其放在最后
        //或者通过异步通信来处理!
        System.out.println(str);
    }
}

class mythread implements Callable<String> {
    @Override
    public String call() throws Exception {
        System.out.println("call方法被调用");
        //耗时操作
        return "1230";
    }
}

//class mythread implements Runnable{
//
//    @Override
//    public void run() {
//
//    }
//}

 

常用的辅助类

CountDownLatch

原理: 

countDownLatch.countDown(); //-1

countDownLatch.await(); //等待计数器归零再向下执行

//计数器
public class CountDownLatchDemo {
    public static void main(String[] args) throws InterruptedException {
        //总数是6
        CountDownLatch countDownLatch = new CountDownLatch(6);
        for(int i=0;i<6;i++){
            new Thread(()->{
                System.out.println(Thread.currentThread().getName()+"go out");
                countDownLatch.countDown(); //-1

            },String.valueOf(i)).start();
        }
        countDownLatch.await(); //等待计数器归零再向下执行
        System.out.println("Close door");
//        countDownLatch.countDown(); //-1
    }
}

CyclicBarrier

加法计数器

 有两种构造参数,一个是传个计数,一个是传个计数完之后要执行的线程。


public class CyclicBarrierDemo {
    public static void main(String[] args) {

        CyclicBarrier cyclicBarrier=new CyclicBarrier(7,()->{
            System.out.println("g盖亚!!!");
        });

        for(int i=0;i<7;i++){

            final int temp=i;
            //lamda表达式能操作到i吗?
            new Thread(()->{
                System.out.println(Thread.currentThread().getName()+"收集"+temp+"个"); //间接获得
                try {
                    cyclicBarrier.await();  //等待
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                } catch (BrokenBarrierException e) {
                    throw new RuntimeException(e);
                }
            }).start();
        }
    }
}

如果计数为8但是线程只有7个的话,就会永远卡死在一个地方。 

Semaphore(信号量)

这个 的实现也有两种参数

public class SemaphoreDemo {
    public static void main(String[] args) {
        //线程数量:停车位
        Semaphore semaphore = new Semaphore(3);
        for(int  i=1;i<=6;i++){
            new Thread(()->{
                //acquire()得到
                try {
                    semaphore.acquire();
                    System.out.println(Thread.currentThread().getName()+"抢到车位");
                    TimeUnit.SECONDS.sleep(2);
                    System.out.println(Thread.currentThread().getName()+"离开车位 ");
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }finally {
                    //realease() 释放
                    semaphore.release();
                }

            },String.valueOf(i)).start();

        }
    }
}

一开始只有三个进去了,后面三个都出去了,后三个才能进来,这里的主要应用场景就是限流。 

原理:

semaphore.acquire();  //获取,假设已经的满了,就等待到资源被释放为止。

semaphore.release();  //释放,将当前信号量释放+1,然后唤醒等待线程。

作用:多个共享资源的互斥使用。并发限流,控制最大线程数。

读写锁

readwritelock只有一个实现类,可重入的读写锁. 读的时候可以多个线程同时读,但是写的时候只能一个线程在写。

如下所示的一个自定义缓存类读写操作

/**
 * readwritelock
 */
public class readwritelockdemo {
    public static void main(String[] args) {
        MyCache  myCache=new MyCache();
        //写入
        for(int i=0;i<5;i++){
            final int temp=i;
            new Thread(()->{
                myCache.put(temp+"",temp+"");
            },String.valueOf(i)).start();
        }
        //读取
        for(int i=0;i<5;i++){
            final int temp=i;
            new Thread(()->{
                myCache.get(temp+"");
            },String.valueOf(i)).start();
        }
    }
}


/**
 * 自定义缓存
 */
class MyCache{
    private volatile Map<String,Object> map=new HashMap<>();


    //存入
    public void put(String key,Object value){
        System.out.println(Thread.currentThread().getName()+"写入"+key);
        map.put(key,value);
        System.out.println(Thread.currentThread().getName()+"写入完毕");
    }
    //读取
    public void get(String key){
        System.out.println(Thread.currentThread().getName()+"读取"+key);
        Object o=map.get(key);
        System.out.println(Thread.currentThread().getName()+"读取完毕");
    }
}

输出如下,在一个线程写入的过程中另一个线程也在写入,这种情况是不能发生的

使用了读写锁之后,写操作只会允许一个线程执行,读操作则会有多个线程同时进行.。


/**
 * readwritelock
 */
public class readwritelockdemo {
    public static void main(String[] args) {
//        MyCache  myCache=new MyCache();
        MyCacheLock myCacheLock=new MyCacheLock();

        //写入
        for(int i=0;i<5;i++){
            final int temp=i;
            new Thread(()->{
                myCacheLock.put(temp+"",temp+"");
            },String.valueOf(i)).start();
        }
        //读取
        for(int i=0;i<5;i++){
            final int temp=i;
            new Thread(()->{
                myCacheLock.get(temp+"");
            },String.valueOf(i)).start();
        }
    }
}
/**
 * 加上锁之后
 */
class MyCacheLock{
    private volatile Map<String,Object> map=new HashMap<>();
    //读写锁,可以更加细力度的控制
    private ReadWriteLock readWriteLock=new ReentrantReadWriteLock();

    //存入,只有一个线程写
    public void put(String key,Object value){
        readWriteLock.writeLock().lock();
        try{
            System.out.println(Thread.currentThread().getName()+"写入"+key);
            map.put(key,value);
            System.out.println(Thread.currentThread().getName()+"写入完毕");
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            readWriteLock.writeLock().unlock();
        }
    }
    //读取,所有线程都能读
    public void get(String key){
        readWriteLock.readLock().lock();
        try{
            System.out.println(Thread.currentThread().getName()+"读取"+key);
            Object o=map.get(key);
            System.out.println(Thread.currentThread().getName()+"读取完毕");
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            readWriteLock.readLock().unlock();
        }

    }
}

阻塞队列

JUC有这样一个阻塞队列的接口,t它的实现有一个SynchronousQueue同步队列,还有一些数组阻塞队列,和链表阻塞队列等等

可以看见Queue和List和Set是同一级别的,在Queue接口下有这个BlockingQueue接口和Deque和AbstractQueue。

 典型使用场景: 多线程并发处理,线程池,生产者消费者。

阻塞队列四组API

1.抛出异常2.不会抛出异常3.阻塞等待4.超时等待

方式抛出异常有返回值,不抛出异常阻塞等待超时等待
添加addoffer()put()offer()
移除removepoll()take()poll()
检测队首元素elementpeek
public class Test {
    public static void main(String[] args) throws InterruptedException {
//        test1();
        test4();
    }
    /**
     * 抛出异常
     */
    public static void test1(){
        //设置队列大小
        ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);
        System.out.println(blockingQueue.add("a"));
        System.out.println(blockingQueue.add("b"));
        System.out.println(blockingQueue.add("c"));
        //Exception in thread "main" java.lang.IllegalStateException: Queue full
//        blockingQueue.add("d");
        System.out.println("_______________");
        System.out.println(blockingQueue.remove());
        System.out.println(blockingQueue.remove());
        System.out.println(blockingQueue.remove());
        //Exception in thread "main" java.util.NoSuchElementException
//        System.out.println(blockingQueue.remove());
    }


    /**
     * 有返回值,不抛出异常
     */
    public static void test2(){
        ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);
        System.out.println(blockingQueue.offer("a"));
        System.out.println(blockingQueue.offer("b"));
        System.out.println(blockingQueue.offer("c"));
        System.out.println(blockingQueue.offer("d"));  //不抛出异常,返回false
        System.out.println("_______________————————————————————");
        System.out.println(blockingQueue.poll());
        System.out.println(blockingQueue.poll());
        System.out.println(blockingQueue.poll());
        System.out.println(blockingQueue.poll());  //也不抛出异常,返回null
    }


    /**
     * 等待,阻塞(一直阻塞)
     */
    public static void test3() throws InterruptedException {
        ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);
        blockingQueue.put("a");
        blockingQueue.put("b");
        blockingQueue.put("c");
//        blockingQueue.put("d");  //一直阻塞
        System.out.println(blockingQueue.take());
        System.out.println(blockingQueue.take());
        System.out.println(blockingQueue.take());
        System.out.println(blockingQueue.take());//也是一直阻塞
    }

    /**
     * 等待,阻塞(等待超时)
     */
    public static void test4() throws InterruptedException {
        ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);
        blockingQueue.offer("a");
        blockingQueue.offer("b");
        blockingQueue.offer("c");
        blockingQueue.offer("d",2, TimeUnit.SECONDS); //超时时间和单位
        System.out.println(blockingQueue.poll());
        System.out.println(blockingQueue.poll());
        System.out.println(blockingQueue.poll());
        System.out.println(blockingQueue.poll(2, TimeUnit.SECONDS));
    }
}

 

同步队列SynchronousQueue

没有容量,进去一个元素必须等待取出来之后,才能再往里面放一个元素。

/**
 * 同步队列
 */
public class SynchronousQueueDemo {
    public static void main(String[] args) {
        SynchronousQueue<String> blockingQueue = new SynchronousQueue<>();//同步队列
        new Thread(()->{
            try {
                System.out.println(Thread.currentThread().getName()+"put 1");
                blockingQueue.put("1");
                System.out.println(Thread.currentThread().getName()+"put 2");
                blockingQueue.put("2");
                System.out.println(Thread.currentThread().getName()+"put 3");
                blockingQueue.put("3");
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        },"t1").start();
        new Thread(()->{
            try {
                TimeUnit.SECONDS.sleep(3);
                System.out.println(Thread.currentThread().getName()+"=>"+blockingQueue.take());
                TimeUnit.SECONDS.sleep(3);
                System.out.println(Thread.currentThread().getName()+"=>"+blockingQueue.take());
                TimeUnit.SECONDS.sleep(3);
                System.out.println(Thread.currentThread().getName()+"=>"+blockingQueue.take());
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        },"t2").start();
    }
}

线程池(重点)

线程池:三大方法,7大参数,4中拒绝策略。

池化技术:事先准备好资源,来人就用,用完放回。

程序的运行,本质:占用系统的资源!优化资源的使用!=>池化技术

线程池,连接池,内存池,对象池。

线程池的好处:

1、降低资源的消耗

2、提高响应的速度

3、方便管理。

线程复用、可以控制最大并发数、管理线程

线程的三大方法

//Executors 工具类、三大方法。
public class Demo01 {
    public static void main(String[] args) {
//        ExecutorService threadPoll = Executors.newSingleThreadExecutor();//单个线程
//        ExecutorService threadPoll =Executors.newFixedThreadPool(5); //创建一个固定线程池的大小
        ExecutorService threadPoll =Executors.newCachedThreadPool(); //可伸缩

        try {
            for(int i=0;i<10;i++){
                //使用了线程池后,使用线程池创建线程
                threadPoll.execute(()->{
                    System.out.println(Thread.currentThread().getName()+"  OK");
                });
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }finally {
            //线程池用完,程序结束,关闭线程池
            threadPoll.shutdown();
        }
    }
}

7大参数

源码分析

第一个方法的源码

    public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }

 

第二个方法的源码 

    public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }

第三个方法的源码 

    public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }

可以看见三种开启方法都是用的ThreadPoolExecutor,它的源码如下

 可以看见有7个参数

1.核心线程池大小2.最大核心线程池大小3.存活时间,4.超时单位5.阻塞队列6.线程工厂,用于创建线程7.拒绝策略。

    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {
        if (corePoolSize < 0 ||
            maximumPoolSize <= 0 ||
            maximumPoolSize < corePoolSize ||
            keepAliveTime < 0)
            throw new IllegalArgumentException();
        if (workQueue == null || threadFactory == null || handler == null)
            throw new NullPointerException();
        this.acc = System.getSecurityManager() == null ?
                null :
                AccessController.getContext();
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }

前面三个方法的前两个参数分别是1,1  5,5   0,Inter.MaxValue。(21亿大小)

因此阿里巴巴的手册里面才会这样写。

四大策略 

核心线程池大小为2,最大为5,一开始只有2个,但是阻塞队列里面满了之后又来人了会开放剩下三个,又慢了之后就不给进了,这就是拒绝策略。

等到了那三个队列空闲后后,经过了超时时间就会关闭释放。

四个实现类对应四大策略 

 自定义线程池、

拒绝策略

会抛出异常.

//Executors 工具类、三大方法。
public class Demo01 {
    public static void main(String[] args) {
        ExecutorService threadPoll =new ThreadPoolExecutor(
                2,
                5,
                3,
                TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(3),
                Executors.defaultThreadFactory(),  //一般不变
                new ThreadPoolExecutor.AbortPolicy());  //该拒绝策略是银行满了,还有人进来时就不处理该人并抛出异常。

        try {
            for(int i=0;i<10;i++){
                //使用了线程池后,使用线程池创建线程
                threadPoll.execute(()->{
                    System.out.println(Thread.currentThread().getName()+"  OK");
                });
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }finally {
            //线程池用完,程序结束,关闭线程池
            threadPoll.shutdown();
        }
    }
}

第二策略

哪来的回哪里去,由原本的线程来执行。

//Executors 工具类、三大方法。
public class Demo01 {
    public static void main(String[] args) {
        ExecutorService threadPoll =new ThreadPoolExecutor(
                2,
                5,
                3,
                TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(3),
                Executors.defaultThreadFactory(),  //一般不变
                new ThreadPoolExecutor.CallerRunsPolicy());  //哪来的去哪里!

        try {
            for(int i=0;i<10;i++){
                //使用了线程池后,使用线程池创建线程
                threadPoll.execute(()->{
                    System.out.println(Thread.currentThread().getName()+"  OK");
                });
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }finally {
            //线程池用完,程序结束,关闭线程池
            threadPoll.shutdown();
        }
    }
}

第三策略

队列满了不会抛出异常。会直接丢掉任务。

package com.yhy.pool;

import java.util.concurrent.*;

/**
 * new ThreadPoolExecutor.AbortPolicy());  //该拒绝策略是银行满了,还有人进来时就不处理该人并抛出异常。
 * new ThreadPoolExecutor.CallerRunsPolicy());  //哪来的去哪里!
 * new ThreadPoolExecutor.DiscardPolicy()); //队列满了就踢了并且不抛出异常。
 * new ThreadPoolExecutor.DiscardOldestPolicy());  //队列满了不会抛出异常,尝试去和最早的竞争,也不会抛出异常!
 */
public class Demo01 {
    public static void main(String[] args) {


        ExecutorService threadPoll =new ThreadPoolExecutor(
                2,
                5,
                3,
                TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(3),
                Executors.defaultThreadFactory(),  //一般不变
                new ThreadPoolExecutor.DiscardPolicy());  

        try {
            for(int i=0;i<10;i++){
                //使用了线程池后,使用线程池创建线程
                threadPoll.execute(()->{
                    System.out.println(Thread.currentThread().getName()+"  OK");
                });
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }finally {
            //线程池用完,程序结束,关闭线程池
            threadPoll.shutdown();
        }
    }
}

可以看见只有8条输出,有两条被踢了。

第四策略

//Executors 工具类、三大方法。
public class Demo01 {
    public static void main(String[] args) {
        ExecutorService threadPoll =new ThreadPoolExecutor(
                2,
                5,
                3,
                TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(3),
                Executors.defaultThreadFactory(),  //一般不变
                new ThreadPoolExecutor.DiscardOldestPolicy());  //队列满了不会抛出异常,尝试去和最早的竞争,也不会抛出异常!

        try {
            for(int i=0;i<10;i++){
                //使用了线程池后,使用线程池创建线程
                threadPoll.execute(()->{
                    System.out.println(Thread.currentThread().getName()+"  OK");
                });
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }finally {
            //线程池用完,程序结束,关闭线程池
            threadPoll.shutdown();
        }
    }
}

  

CPU密集型,IO密集型(扩展)

经常会被问,池的最大大小如何去设置。

CPU密集型: 12核的CPU最多12条线程同时执行,多少核就

IO密集型:       程序 15个大型任务 IO十分占用资源 ,一般设置为两倍30个线程。

/**
 * new ThreadPoolExecutor.AbortPolicy());  //该拒绝策略是银行满了,还有人进来时就不处理该人并抛出异常。
 * new ThreadPoolExecutor.CallerRunsPolicy());  //哪来的去哪里!
 * new ThreadPoolExecutor.DiscardPolicy()); //队列满了就踢了并且不抛出异常。
 * new ThreadPoolExecutor.DiscardOldestPolicy());  //队列满了不会抛出异常,尝试去和最早的竞争,也不会抛出异常!
 */
public class Demo01 {
    public static void main(String[] args) {

        //自定义线程池! 工作 ThreadPoolExecutor
        //最大线程池如何定义
        //1、CPU 密集型,几核,就是几,可以保CPu的效率最高!
        //2、IO密集型
        //      程序 15个大型任务 IO十分占用资源

        //获取CPU核数
        System.out.println(Runtime.getRuntime().availableProcessors());

        ExecutorService threadPoll =new ThreadPoolExecutor(
                2,
                Runtime.getRuntime().availableProcessors(),
                3,
                TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(3),
                Executors.defaultThreadFactory(),  //一般不变
                new ThreadPoolExecutor.DiscardOldestPolicy());  //队列满了不会抛出异常,尝试去和最早的竞争,也不会抛出异常!

        try {
            for(int i=0;i<10;i++){
                //使用了线程池后,使用线程池创建线程
                threadPoll.execute(()->{
                    System.out.println(Thread.currentThread().getName()+"  OK");
                });
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }finally {
            //线程池用完,程序结束,关闭线程池
            threadPoll.shutdown();
        }
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1053000.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

国庆作业 9月30 消息队列实现进程间通信

01_write.c&#xff1a; #include <myhead.h> #define MAX 1024//x消息结构体 typedef struct {long msgtype; //消息类型char msg[MAX]; //消息正文 }Msg;#define SIZE sizeof(Msg)-sizeof(long) //正文大小int main(int argc, const char *argv[]) {//1:创建一个key…

机器学习之单层神经网络的训练:增量规则(Delta Rule)

文章目录 权重的调整单层神经网络使用delta规则的训练过程 神经网络以权值的形式存储信息,根据给定的信息来修改权值的系统方法称为学习规则。由于训练是神经网络系统地存储信息的唯一途径&#xff0c;因此学习规则是神经网络研究中的一个重要组成部分 权重的调整 &#xff08…

企业级磁盘阵列存储系统由硬到软全析

企业级磁盘阵列是由一组设备构成的存储系统,主要包括两种类型的设备,分别是控制器和扩展柜,其中控制器只有一台,扩展柜可以没有,也可以有多台。在EMC的Unity中分别称为DPE(Disk Processor Enclosure)和DAE(Disk Array Enclosure),在华为的OceanStor里面称为控制框和硬…

【网络模型】OSI七层网络模型、TCP/IP网络模型、键入网址到页面显示的过程、DNS是什么等重点知识汇总

目录 OSI 的七层模型 TCP/IP 网络模型 键入网址到网页显示发生了什么 你知道DNS是什么&#xff1f; OSI 的七层模型 简要概括 应用层&#xff1a;为用户的应用进程提供网络通信服务表示层&#xff1a;处理用户信息的表示问题&#xff0c;数据的编码&#xff0c;压缩和解压…

在枚举类中“优雅地”使用枚举处理器

使用枚举类的一大好处就是&#xff0c;代码易懂&#xff0c;方便自己或他人维护。如&#xff0c;枚举状态、异常等。 下面有两个类&#xff08;枚举类和实体类&#xff09;&#xff1a; package com.zhang.enums;import lombok.Getter;/*** Author lgz* Description* Date 202…

【生物信息学】基因差异分析Deg(数据读取、数据处理、差异分析、结果可视化)

目录 一、实验介绍 二、实验环境 1. 配置虚拟环境 2. 库版本介绍 3. IDE 三、实验内容 0. 导入必要的工具包 1. 定义一些阈值和参数 2. 读取数据 normal_data.csv部分展示 tumor_data.csv部分展示 3. 绘制箱型图 4. 删除表达量低于阈值的基因 5. 计算差异显著的基…

Git多账号管理通过ssh 公钥的方式,git,gitlab,gitee

按照目前国内访问git&#xff0c;如果不科学上网&#xff0c;我们很大可能访问会超时。基于这个&#xff0c;所以我现在的git 配置已经增加到了3个了 一个公司gitlab&#xff0c;一个git&#xff0c;一个gitee. 以下基于这个环境&#xff0c;我们来说明下如何创建配置ssh公钥。…

VBA技术资料MF63:遍历形状并改变颜色

【分享成果&#xff0c;随喜正能量】人生&#xff0c;一站有一站的风景&#xff0c;一岁有一岁的味道&#xff0c;你的年龄应该成为你生命的勋章而不是你伤感的理由。生活嘛&#xff0c;慢慢来&#xff0c;你又不差&#xff01;。 我给VBA的定义&#xff1a;VBA是个人小型自动…

【数据结构与算法】栈与队列相关算法的实现

目录 检查括号是否成对出现 反转字符串 循环队列的实现 使用队列实现栈 使用栈实现队列 检查括号是否成对出现 算法要求 给定一个只包括 (&#xff0c;)&#xff0c;{&#xff0c;}&#xff0c;[&#xff0c;] 的字符串&#xff0c;判断该字符串是否有效。 有效字符串需满…

树莓派基本配置(2)

安装motion $sudo apt-get update $sudo apt-get install motion配置motion sudo nano /etc/default/motionsudo nano /etc/motion/motion.conf主要改这些参数 //让Motion作为守护进程运行 daemon on ... //用这个端口号来读取数据 stream_port 8081 ... //网络上其它主机…

2023年上海市安全员B证证模拟考试题库及上海市安全员B证理论考试试题

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 2023年上海市安全员B证证模拟考试题库及上海市安全员B证理论考试试题是由安全生产模拟考试一点通提供&#xff0c;上海市安全员B证证模拟考试题库是根据上海市安全员B证最新版教材&#xff0c;上海市安全员B证大纲整理…

正点原子嵌入式linux驱动开发——TF-A使用

上一篇笔记STM32MP157芯片的开发环境&#xff0c;之后就直接简写为MP1。为了保证安全ARM推出了 Arm Trusted Firmware的可信固件&#xff0c;简称 TF-A。它是一个开源的软件&#xff0c;最早是用在Armv8-A&#xff0c;ST也在MP1里面使用到了TF-A。它的作用就是隔离硬件&#xf…

递推+模拟---想好如何存储?

递推模拟 输入输出问题CCF-CSP考试历年真题题型分类分组输入——可能有多组测试数据&#xff0c;对于每组数据 递推---从前面已知态--->后续未知态AcWing 3777. 砖块AcWing 1208. 翻硬币AcWing 1211. 蚂蚁感冒AcWing 3433. 吃糖果AcWing 821. 跳台阶 模拟202212-2-csp-训练计…

C语言--atoi函数详解及模拟实现

本篇概要 本篇博客主要讲述atoi函数的定义&#xff0c;用法及模拟实现。 文章目录 本篇概要1.atoi函数简介2.atoi函数的原型及参数3.atoi函数的使用举例4.atoi函数的模拟实现 1.atoi函数简介 atoi() 是 C语言的一个标准库函数&#xff0c;定义在<stdlib.h>头文件中。 at…

【RocketMQ】基本使用:Java操作RocketMQ(rocketmq-client)

【RocketMQ】基本使用&#xff1a;Java操作RocketMQ&#xff08;rocketmq-client&#xff09; 1.引入依赖 <dependency><groupId>org.apache.rocketmq</groupId><artifactId>rocketmq-client</artifactId><version>4.3.2</version>…

公司知识库搭建步骤,知识库建设与运营的四个步骤分享

在知识管理方面&#xff0c;团队中的每一员&#xff0c;都像是一名独行侠&#xff0c;自己的知识&#xff0c;满足自己的需要&#xff0c;这其中&#xff0c;就造成了很多无意义的精力消耗。 公司知识库搭建必要性 比如&#xff0c;一名员工撰写一QA文档&#xff0c;并没有将它…

CDH 6.3.2升级Flink到1.17.1版本

CDH&#xff1a;6.3.2 原来的Flink&#xff1a;1.12 要升级的Flink&#xff1a;1.17.1 操作系统&#xff1a;CentOS Linux 7 一、Flink1.17编译 build.sh文件&#xff1a; #!/bin/bash set -x set -e set -vFLINK_URLsed /^FLINK_URL/!d;s/.*// flink-parcel.properties FLI…

【模板语法+数据绑定+el与data的两种写法+MVVM模型】

模板语法数据绑定el与data的两种写法MVVM模型 1 模板语法1.1 插值语法1.2 指令语法 2 数据绑定2.1 单向数据绑定2.2 双向数据绑定 3 el与data的两种写法4 MVVM模型 1 模板语法 1.1 插值语法 双大括号表达式功能&#xff1a;用于解析标签体内容语法&#xff1a;{{xxx}}&#x…

Postman的高级用法—Runner的使用

1.首先在postman新建要批量运行的接口文件夹&#xff0c;新建一个接口&#xff0c;并设置好全局变量。 2.然后在Test里面设置好要断言的方法 如&#xff1a; tests["Status code is 200"] responseCode.code 200; tests["Response time is less than 10000…

分子相互作用的人工智能

8 分子相互作用的人工智能 正如在第 5、6 和 7 节中所描述的那样&#xff0c;人工智能已经彻底改变了分子学习、蛋白质科学和材料科学领域。尽管已经广泛研究了用于单个分子的人工智能&#xff0c;但分子的物理和生物功能通常是由它们与其他分子的相互作用驱动的。在本节中&am…