JUC并发编程【狂神说java详解】,超详细。

基于《狂神说Java》JUC并发编程--学习笔记 本笔记仅做学习与复习使用,不存在刻意抄袭。给各位学友强烈推荐《遇见狂神说》他的整套Java学习路线使我获益匪浅!!!点击跳转至遇见狂神说哔哩哔哩首页如果你也是狂神的小迷弟,可以加我好友一起探讨学习。 ... 阅读详情

总结笔记,课程来自:https://www.bilibili.com/video/BV1B7411L7tE

前言

多线程进阶=》JUC并发编程

一、什么是JUC

JUC指的是:Java里的三个包
java.util.concurrent
java.util.concurrent.atomic:原子性
java.util.concurrent.locks:lock锁

二、线程和进程

1.进程

程序执行的一次过程,一个进程包含一个或多个线程。进程是资源分配的单位。

2.线程

可以指程序执行过程中,负责实现某个功能的单位。线程是CPU调度和执行的单位。

3.并发

同一时刻,多个线程交替执行。(一个CPU交替执行线程)

4.并行

同一时刻,多个线程同时执行。(多个CPU同时执行多个线程)

# 获取cpu的核数(cpu密集型;io密集型)
System.out.println(Runtime.getRuntime().availableProcessors());

并发编程的本质

并发编程的本质是充分利用cpu资源。

问题:Java真的可以开启线程吗

Java创建Thread类调用start方法,底层是把线程放到一个组里面,然后调用一个本地方法start0;方法底层是C++;Java无法操作硬件。

三、多线程回顾

1.线程的几种状态

    public enum State {
        /**
         * Thread state for a thread which has not yet started.
         */
         // 新生
        NEW,
        /**
         * Thread state for a runnable thread.  A thread in the runnable
         * state is executing in the Java virtual machine but it may
         * be waiting for other resources from the operating system
         * such as processor.
         */
         // 运行
        RUNNABLE,
        /**
         * Thread state for a thread blocked waiting for a monitor lock.
         * A thread in the blocked state is waiting for a monitor lock
         * to enter a synchronized block/method or
         * reenter a synchronized block/method after calling
         * {@link Object#wait() Object.wait}.
         */
         // 阻塞
        BLOCKED,
        /**
         * Thread state for a waiting thread.
         * A thread is in the waiting state due to calling one of the
         * following methods:
         * <ul>
         *   <li>{@link Object#wait() Object.wait} with no timeout</li>
         *   <li>{@link #join() Thread.join} with no timeout</li>
         *   <li>{@link LockSupport#park() LockSupport.park}</li>
         * </ul>
         *
         * <p>A thread in the waiting state is waiting for another thread to
         * perform a particular action.
         *
         * For example, a thread that has called <tt>Object.wait()</tt>
         * on an object is waiting for another thread to call
         * <tt>Object.notify()</tt> or <tt>Object.notifyAll()</tt> on
         * that object. A thread that has called <tt>Thread.join()</tt>
         * is waiting for a specified thread to terminate.
         */
         // 等待,死死的等待
        WAITING,
        /**
         * Thread state for a waiting thread with a specified waiting time.
         * A thread is in the timed waiting state due to calling one of
         * the following methods with a specified positive waiting time:
         * <ul>
         *   <li>{@link #sleep Thread.sleep}</li>
         *   <li>{@link Object#wait(long) Object.wait} with timeout</li>
         *   <li>{@link #join(long) Thread.join} with timeout</li>
         *   <li>{@link LockSupport#parkNanos LockSupport.parkNanos}</li>
         *   <li>{@link LockSupport#parkUntil LockSupport.parkUntil}</li>
         * </ul>
         */
         // 超时等待
        TIMED_WAITING,
        /**
         * Thread state for a terminated thread.
         * The thread has completed execution.
         */
         // 终止
        TERMINATED;
    }

2.sleep和wait的区别

  1. sleep是Thread类的本地方法;wait是Object类的方法。
  2. sleep不释放锁;wait释放锁。
  3. sleep不需要和synchronized关键字一起使用;wait必须和synchronized代码块一起使用。
  4. sleep不需要被唤醒(时间到了自动退出阻塞);wait需要被唤醒。
  5. sleep一般用于当前线程休眠,或者轮循暂停操作;wait则多用于多线程之间的通信。

四、Lock锁

1.传统的synchronized

不加Synchronized
package com.fang.demo0;

/**
* 真正的多线程开发
* 线程就是一个资源类,没有任何附属的操作
*/

public class SaleTicket {
   public static void main(String[] args) {
       Ticket ticket = new Ticket();
       //Runnable接口为函数式接口
       new Thread(()->{
           for (int i = 0; i < 40; i++) {
               ticket.sale();
           }
       },"a").start();
       new Thread(()->{
           for (int i = 0; i < 40; i++) {
               ticket.sale();
           }
       },"b").start();
       new Thread(()->{
           for (int i = 0; i < 40; i++) {
               ticket.sale();
           }
       },"c").start();
   }
}
//资源类oop编程
class Ticket {
   //属性,方法
   private int number = 50;
   //买票的方式
   public void sale() {
       if (number>0) {
           System.out.println(Thread.currentThread().getName()+"卖出了"+(number--)+"票,剩余:"+number);
       }
   }
}

在这里插入图片描述

加了锁后正常

public synchronized void sale() {
        if (number>0) {
            System.out.println(Thread.currentThread().getName()+"卖出了"+(number--)+"票,剩余:"+number);
        }
    }

2.公平锁和非公平锁(锁的底层)

公平锁:十分公平,不能插队。
非公平锁:十分不公平,可以插队。(默认非公平锁)

3.Lock锁

Lock锁是一个接口,他有三个实现类:

  • ReentrantLock类
  • ReentrantReadWriteLock.ReadLock
  • ReentrantReadWriteLock.WriteLock

4.Lock锁和synchronized的区别

  1. Synchronized是内置Java关键字;Lock是一个Java类。
  2. Synchronized无法判断获取锁的状态;Lock可以判断是否获取到了锁。(boolean b = lock.tryLock();)
  3. Synchronized会自动释放锁;Lock必须要手动释放锁,如果不释放锁,死锁。
  4. Synchronized线程1获得锁阻塞时,线程2会一直等待下去;Lock锁线程1获得锁阻塞时,线程2等待足够长的时间后中断等待,去做其他的事。
  5. Synchronized可重入锁,不可以中断的,非公平;Lock,可重入锁,可以判断锁,非公平(可以自己设置)。
    lock.lockInterruptibly();方法:当两个线程同时通过该方法想获取某个锁时,假若此时线程A获取到了锁,而线程B只有在等待,那么对线程B调用threadB.interrupt()方法能够中断线程B的等待过程。
  6. Synchronized适合锁少量的代码同步问题;Lock适合锁大量的同步代码。

5.生产消费者

  1. 生产者和消费者问题:synchronized版

    public class Demo04 {
     public static void main(String[] args) {
         Data data = new Data();
         new Thread(() -> {
             for (int i = 0; i < 10; i++) {
                 try {
                     data.increment();
                 } catch (InterruptedException e) {
                     e.printStackTrace();
                 }
             }
         }, "A").start();
         new Thread(() -> {
             for (int i = 0; i < 10; i++) {
                 try {
                     data.decrement();
                 } catch (InterruptedException e) {
                     e.printStackTrace();
                 }
             }
         }, "B").start();
     }
    }
    // 判断等待,业务,通知
    class Data {
     private int i = 0;
     // +1
     public synchronized void increment() throws InterruptedException {
         if (i != 0) {
             this.wait();
         }
         i++;
         System.out.println(Thread.currentThread().getName() + "=>" + i);
         // 通知其他线程我+1完成
         this.notifyAll();
     }
     // -1
     public synchronized void decrement() throws InterruptedException {
         if (i==0){
             this.wait();
         }
         i--;
         System.out.println(Thread.currentThread().getName() + "=>" + i);
         // 通知其他线程,我-1完毕
         this.notifyAll();
     }
    }
    

    问题存在:A、B、C、D四个线程!虚假唤醒问题

    if改成while解决虚假唤醒

     public class Demo04 {
         public static void main(String[] args) {
             Data data = new Data();
             new Thread(() -> {
                 for (int i = 0; i < 10; i++) {
                     try {
                         data.increment();
                     } catch (InterruptedException e) {
                         e.printStackTrace();
                     }
                 }
             }, "A").start();
             new Thread(() -> {
                 for (int i = 0; i < 10; i++) {
                     try {
                         data.decrement();
                     } catch (InterruptedException e) {
                         e.printStackTrace();
                     }
                 }
             }, "B").start();
             new Thread(() -> {
                 for (int i = 0; i < 10; i++) {
                     try {
                         data.increment();
                     } catch (InterruptedException e) {
                         e.printStackTrace();
                     }
                 }
             }, "C").start();
             new Thread(() -> {
                 for (int i = 0; i < 10; i++) {
                     try {
                         data.decrement();
                     } catch (InterruptedException e) {
                         e.printStackTrace();
                     }
                 }
             }, "D").start();
         }
     }
     // 判断等待,业务,通知
     class Data {
         private int i = 0;
         // +1
         public synchronized void increment() throws InterruptedException {
             while (i != 0) {
                 this.wait();
             }
             i++;
             System.out.println(Thread.currentThread().getName() + "=>" + i);
             // 通知其他线程我+1完成
             this.notifyAll();
         }
         // -1
         public synchronized void decrement() throws InterruptedException {
             while (i==0){
                 this.wait();
             }
             i--;
             System.out.println(Thread.currentThread().getName() + "=>" + i);
             // 通知其他线程,我-1完毕
             this.notifyAll();
         }
     }
    
  2. 生产者和消费者问题:JUC版

    public class Demo04 {
     public static void main(String[] args) {
         Data data = new Data();
         new Thread(() -> {
             for (int i = 0; i < 10; i++) {
                 try {
                     data.increment();
                 } catch (InterruptedException e) {
                     e.printStackTrace();
                 }
             }
         }, "A").start();
         new Thread(() -> {
             for (int i = 0; i < 10; i++) {
                 try {
                     data.decrement();
                 } catch (InterruptedException e) {
                     e.printStackTrace();
                 }
             }
         }, "B").start();
         new Thread(() -> {
             for (int i = 0; i < 10; i++) {
                 try {
                     data.increment();
                 } catch (InterruptedException e) {
                     e.printStackTrace();
                 }
             }
         }, "C").start();
         new Thread(() -> {
             for (int i = 0; i < 10; i++) {
                 try {
                     data.decrement();
                 } catch (InterruptedException e) {
                     e.printStackTrace();
                 }
             }
         }, "D").start();
     }
    }
    // 判断等待,业务,通知
    class Data {
     private int i = 0;
     Lock lock = new ReentrantLock();
     Condition condition = lock.newCondition();
     // +1
     public  void increment() throws InterruptedException {
         lock.lock();
         try {
             while (i != 0) {
                 condition.await();
             }
             i++;
             System.out.println(Thread.currentThread().getName() + "=>" + i);
             // 通知其他线程我+1完成
             condition.signalAll();
         } catch (InterruptedException e) {
             e.printStackTrace();
         } finally {
             lock.unlock();
         }
     }
     // -1
     public void decrement() throws InterruptedException {
         lock.lock();
         try {
             while (i==0){
                 condition.await();
             }
             i--;
             System.out.println(Thread.currentThread().getName() + "=>" + i);
             // 通知其他线程,我-1完毕
             condition.signalAll();
         } catch (InterruptedException e) {
             e.printStackTrace();
         } finally {
             lock.unlock();
         }
     }
    }
    

3.Condition实现精准通知唤醒

public class Demo05 {
    public static void main(String[] args) {
        Data01 data01 = new Data01();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    data01.A();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "A").start();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    data01.B();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "B").start();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    data01.C();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "C").start();
    }
}
// 判断等待,业务,通知
//A执行完调用B,B执行完调用C,C执行完调用A
class Data01 {
    private int num = 1;// 1A 2B 3C
    private Lock lock = new ReentrantLock();
    private Condition condition1 = lock.newCondition();
    private Condition condition2 = lock.newCondition();
    private Condition condition3 = lock.newCondition();
    public void A() throws InterruptedException {
        lock.lock();
        try {
            // 业务代码,判断=>执行=>通知!
            while (num!=1){
                condition1.await();
            }
            System.out.println(Thread.currentThread().getName()+"=>AAAAA");
            num = 2;
            // 唤醒指定的线程,B
            condition2.signal();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
    public void B() throws InterruptedException {
        lock.lock();
        try {
            while (num!=2){
                condition2.await();
            }
            num = 3;
            System.out.println(Thread.currentThread().getName()+"=>BBBBB");
            // 唤醒指定的线程,C
            condition3.signal();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
    public void C() throws InterruptedException {
        lock.lock();
        try {
            while (num!=3){
                condition3.await();
            }
            num = 1;
            System.out.println(Thread.currentThread().getName()+"=>CCCCC");
            // 唤醒指定的线程,A
            condition1.signal();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}

五、8锁现象(就是关于锁的八个问题)

如何判断锁的是谁!永远的知道什么锁,锁到底锁的是谁!

六、集合类不安全

ArryList集合

多线程下不安全;可能会报错:java.util.ConcurrentModificationException(并发修改异常)

// java.util.ConcurrentModificationException:并发修改异常
public class Test11 {
    public static void main(String[] args) {
        List<String> strings = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            new Thread(()->{
                strings.add(UUID.randomUUID().toString().substring(0,5));
                System.out.println(strings);
            }).start();
        }
    }
}

解决方案:

  1. List list = new Vector<>();
  2. List strings = Collections.synchronizedList(new ArrayList<>());
  3. List strings = new CopyOnWriteArrayList<>();

概念:CopyOnWrite写入时复制,计算机程序设计语言的一种优化策略。(保证效率和性能问题)

HashSet集合

多线程下不安全;可能会报错:java.util.ConcurrentModificationException(并发修改异常)

// java.util.ConcurrentModificationException:并发修改异常
public class Test11 {
    public static void main(String[] args) {
        // Set<String> strings = Collections.synchronizedSet(new HashSet<>());
        HashSet<String> strings = new HashSet<>();
        for (int i = 0; i < 10; i++) {
            new Thread(()->{
                strings.add(UUID.randomUUID().toString().substring(0,5));
                System.out.println(strings);
            }).start();
        }
    }
}

解决方案:

  1. Set strings = Collections.synchronizedSet(new HashSet<>());
  2. Set strings = new CopyOnWriteArraySet<>();

hashset集合的底层是hashmap的key

HashMap集合

多线程下不安全;可能会报错:java.util.ConcurrentModificationException(并发修改异常)

// java.util.ConcurrentModificationException:并发修改异常
public class Test11 {
    public static void main(String[] args) {
        // 默认相当于
        Map<String, String> map = new HashMap<>(16, 0.75F);
        for (int i = 0; i < 10; i++) {
            new Thread(()->{
                map.put(Thread.currentThread().getName(),UUID.randomUUID().toString().substring(0,5));
                System.out.println(map);
            }).start();
        }
    }
}

解决方案:

  1. 使用Map<String, String> concurrentHashMap = new ConcurrentHashMap<>();

七、Callable接口

Callable接口类似于Runnable接口,线程第三种创建方式。

  1. 可以抛出异常。
  2. 可以有返回值。
  3. 方法不同与Runnable接口。Call方法
public class CallableTest {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        FutureTask futureTask = new FutureTask(new MyThread());// 适配类
        new Thread(futureTask,"A").start();
        new Thread(futureTask,"B").start();// 打印一个Call,结果会被缓存,提高效率
        Integer s = (Integer) futureTask.get();// get方法可能会产生阻塞
        System.out.println(s);
    }
}
class MyThread implements Callable<Integer>{
    @Override
    public Integer call(){
        System.out.println("Call");
        return 1024;
    }
}

原理图:
img

八、常用辅助类(AbstractQueuedSynchronizer(AQS))

1.CountDownLatch

应用场景:1.多线程任务汇总。2.多线程任务阻塞住,等待发令枪响,一起执行。
img

每次有线程调用,数量-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
            }).start();
        }
        countDownLatch.await();// 等待计数器归零,然后再往下执行
        System.out.println("关门");
    }
}

打印:
img

2.CyclicBarrier([ˈsaɪklɪk]、[ˈbæriə®])

应用场景:比如LOL类游戏,满10人一组,开始游戏。
img

// 相当于加法计数器
public class CyclicBarrierDemo {
    public static void main(String[] args) {
        // 集齐七颗龙珠召唤神龙
        CyclicBarrier cyclicBarrier = new CyclicBarrier(7, () -> {// 如果计数器为7,线程只有6个,则会等待,不进行召唤神龙
            System.out.println("召唤神龙");
        });
        for (int i = 0; i < 7; i++) {
            final int temp = i;
            new Thread(() -> {
                System.out.println(Thread.currentThread().getName() + "收集" + temp + "个龙珠!");
                try {
                    cyclicBarrier.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }
}

打印:
img

3.Semaphore([ˈseməfɔː®])

Semaphore:信号量
img

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

打印:
img
原理:
semaphore.acquire();获得,假设已经满了则等待,等待其他线程释放。
semaphore.release();释放,会将当前的信号量释放+1,然后唤醒等待的线程。

九、读写锁

ReadWriteLock接口有一个实现类ReentrantReadWriteLock类。

读可以被多个线程同时读,写的时候只能有一个线程去写

img

/**
 * 独占锁(写锁):一次只能被一个线程占有
 * 共享锁(读锁):多个线程可以同时占有
 * ReentrantLock:
 * 读-读:可以共存
 * 读-写:不可以共存
 * 写-写:不可以共存
 */
public class ReentrantLockDemo {
    public static void main(String[] args) {
        MyCacheLock myCache = new MyCacheLock();
        // 5个线程写
        for (int i = 1; i <= 5; i++) {
            final int temp = i;
            new Thread(() -> {
                myCache.put(temp + "", temp + "");
            }, String.valueOf(i)).start();
        }
        // 5个线程读
        for (int i = 1; i <= 5; i++) {
            final int temp = i;
            new Thread(() -> {
                myCache.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 obj) {
        readWriteLock.writeLock().lock();
        try {
            System.out.println(Thread.currentThread().getName() + "写入");
            map.put(key, obj);
            System.out.println(Thread.currentThread().getName() + "写入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() + "读取");
            map.get(key);
            System.out.println(Thread.currentThread().getName() + "读取OK");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            readWriteLock.readLock().unlock();
        }
    }
}

十、阻塞队列BlockingQueue

img
img
img

四组API

方式抛出异常有返回值,不抛出异常阻塞,一直等待阻塞,超时等待
添加add()offer()put()offer(,,)
移除remove()pull()take()pull(,)
检测队首元素element()peek()--
public class Test {
    public static void main(String[] args) throws InterruptedException {
        test4();
    }
    // 抛出异常:java.lang.IllegalStateException: Queue full
    public static void test1(){
        // 队列的大小为3
        ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);
        // add()方法返回boolean值
        boolean flag1 = blockingQueue.add("a");
        boolean flag2 = blockingQueue.add("b");
        boolean flag3 = blockingQueue.add("c");
        boolean flag4 = blockingQueue.add("d");// add添加元素超过队列的长度会抛出异常java.lang.IllegalStateException: Queue full
        System.out.println(blockingQueue.element());// 获得队首元素
        System.out.println("=========");
        // remove()返回本次移除的元素
        Object e1 = blockingQueue.remove();
        Object e2 = blockingQueue.remove();
        Object e3 = blockingQueue.remove();
        Object e4 = blockingQueue.remove();// 队列中没有元素仍继续移除元素会抛出异常java.util.NoSuchElementException
    }
    // 有返回值,不抛出异常
    public static void test2(){
        // 队列的大小为3
        ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);
        // offer返回boolean值
        boolean flag1 = blockingQueue.offer("a");
        boolean flag2 = blockingQueue.offer("b");
        boolean flag3 = blockingQueue.offer("c");
        //boolean flag4 = blockingQueue.offer("d");// offer添加元素超过队列的长度会返回false
        System.out.println(blockingQueue.peek());// 获得队首元素
        System.out.println("=========");
        // poll()返回本次移除的元素
        Object poll1 = blockingQueue.poll();
        Object poll2 = blockingQueue.poll();
        Object poll3 = blockingQueue.poll();
        Object poll4 = blockingQueue.poll();// 队列中没有元素仍继续移除元素会打印出null
    }
    // 阻塞,一直等待
    public static void test3() throws InterruptedException {
        // 队列的大小为3
        ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);
        // put没有返回值
        blockingQueue.put("a");
        blockingQueue.put("b");
        blockingQueue.put("c");
        //blockingQueue.put("d");// put添加元素超过队列的长度会一直等待
        System.out.println("=========");
        // take()返回本次移除的元素
        Object take1 = blockingQueue.take();
        Object take2 = blockingQueue.take();
        Object take3 = blockingQueue.take();
        Object take4 = blockingQueue.take();// 队列中没有元素仍继续移除元素会一直等待
    }
    // 阻塞,超时等待
    public static void test4() throws InterruptedException {
        // 队列的大小为3
        ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);
        // offer返回boolean值
        boolean flag1 = blockingQueue.offer("a");
        boolean flag2 = blockingQueue.offer("b");
        boolean flag3 = blockingQueue.offer("c");
        // offer添加元素超过队列的长度会返回false;并且等待指定时间后推出,向下执行
        boolean flag4 = blockingQueue.offer("d", 2, TimeUnit.SECONDS);
        System.out.println("=========");
        // poll()返回本次移除的元素
        Object poll1 = blockingQueue.poll();
        Object poll2 = blockingQueue.poll();
        Object poll3 = blockingQueue.poll();
        // 队列中没有元素仍继续移除元素会打印出null,等待指定之间后退出。
        Object poll4 = blockingQueue.poll(2,TimeUnit.SECONDS);
    }
}

SynchronousQueue同步队列

进去一个元素,必须等待取出这个元素后,才能放下一个元素。put()、take()

十一、线程池

3大方法、7大参数、4大拒绝策略

池化技术及线程池的使用

程序的运行,本质:占用系统的资源!优化资源的使用
线程池,连接池,内存池,对象池
池化技术:事先准备好一些资源,有人要用就来拿,用完之后归还

线程池的好处

1.降低资源的消耗
2.提高响应速度
3.方便管理
线程可以复用,可以控制最大并发量,管理线程
线程池:三大方法,7大参数,4种拒绝策略

线程的三大方法

线程池不允许使用Executors去创建,而是通过ThreadPoolExecutor的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。 说明:Executors各个方法的弊端:
1)newFixedThreadPool和newSingleThreadExecutor:
  主要问题是堆积的请求处理队列可能会耗费非常大的内存,甚至OOM。
2)newCachedThreadPool和newScheduledThreadPool:
  主要问题是线程数最大数是Integer.MAX_VALUE(约为21亿),可能会创建数量非常多的线程,甚至OOM。

public static void main(String[] args) {
    //Executors工具类,三大方法
    ExecutorService threadPool = Executors.newSingleThreadExecutor();//单个线程
    for (int i = 0; i < 10; i++) {
        //使用了线程池之后,使用线程池来创建线程
        threadPool.execute(()->{
            System.out.println(Thread.currentThread().getName()+"ok");
        });
    }
    //线程池用完,程序结束,关闭线程池
    try {
        threadPool.shutdown();
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
    }
}

img

//Executors工具类,三大方法
// ExecutorService threadPool = Executors.newSingleThreadExecutor();//单个线程
// ExecutorService threadPool = Executors.newFixedThreadPool(5);//创建一个固定大小得线程池
ExecutorService threadPool = Executors.newCachedThreadPool();//可伸缩,线程数可变

7大参数及自定义线程池

源码分析

public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue()));
}
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue());
}
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue());
}

三大方法底层都调用得是ThreadPoolExecutor

三大方法底层都调用得是ThreadPoolExecutor

//七个参数
    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;
    }

手动创建一个线程池
————————————————
版权声明:本文为优快云博主「想去22世纪」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.youkuaiyun.com/weixin_48412846/article/details/115681611

四种拒绝策略

img

new ThreadPoolExecutor.AbortPolicy(); // 抛出异常
new ThreadPoolExecutor.CallerRunsPolicy();// 哪来的去哪(主线程来的,就回去让主线程执行)
new ThreadPoolExecutor.DiscardPolicy();// 丢掉任务,不抛出异常
new ThreadPoolExecutor.DiscardOldestPolicy();// 尝试和最早的竞争,竞争失败了也丢掉任务,也不抛出异常

最大线程应该如何设置?

  1. CPU密集型:最大线程数,CPU几核的就是几,可以保持CPU效率最高。

  2. IO密集型:判断程序中十分耗IO的线程数量,大于这个数,一般是这个数的两倍。

    程序 15个大型任务 io十分暂用资源
    System.out.println(Runtime.getRuntime().availableProcessors());//获得cpu的核心数

十二、四大函数式接口

新时代程序员必须掌握:lambda表达式、链式编程、函数式接口、stream流式计算。
函数式接口:只有一个方法的接口。

Function函数型接口

有一个输入参数,有一个输出(返回值)。

img

    public static void main(String[] args) {
//        Function function = new Function<String,String>() {
//            @Override
//            public String apply(String str) {
//                return str;
//            }
//        };
        Function function = (str)->{return str;};
        System.out.println(function.apply("abc"));
    }

predicate断定型接口

有一个输入参数,返回值只能是boolean值。

img

    public static void main(String[] args) {
        //判断字符串是否为空
        Predicate<String> predicate = new Predicate<String>() {
            @Override
            public boolean test(String s) {
                return s.isEmpty();
            }
        };
        Predicate<String> predicate = (str)- >{return str.isEmpty();
        System.out.println(predicate.test(""));
    }

consumer消费型接口

有一个输入参数,没有返回值。

img

supplier供给型接口

没有输入参数,有一个输出(返回值)。

十三、stream流式计算

存储+计算
存储:mysql,集合
计算都要交给流计算你

public class Test {
   public static void main(String[] args) {
       User user1 = new User(1,21,"张三");
       User user2 = new User(2,23,"李四");
       User user3 = new User(3,29,"王五");
       User user4 = new User(4,18,"赵六");
       //集合存储
       List<User> userList = Arrays.asList(user1, user2, user3, user4);
       //计算交给流
       userList.stream().filter(user -> {return user.getId()%2==0;})
               .filter(user -> {return user.getAge()>20;})
               .map(user -> {return user.getName().toUpperCase(Locale.ROOT);})
               .sorted((u1,u2)->{return u2.compareTo(u1); })
//                .limit(1)//分页
               .forEach(System.out::println);
   }
}

十四、forkjoin详解

什么是ForkJoin

ForkJoin在JDk1.7,并行执行任务!提高效率,数据量大!
大数据:Map Reduce把大任务拆分为小任务.
特点:

特点工作窃取

十五、异步回调

Future的设计初衷:

    public static void main(String[] args) throws ExecutionException, InterruptedException {
//        //发起一个请求,没有返回值得异步回调
//        CompletableFuture<Void> completableFuture = CompletableFuture.runAsync(()->{
//            try {
//                TimeUnit.SECONDS.sleep(2);
//            } catch (InterruptedException e) {
//                e.printStackTrace();
//            }
//            System.out.println(Thread.currentThread().getName()+"run");
//        });
//        System.out.println("1111");
//        //获取阻塞执行结果
//        completableFuture.get();
       //有返回值的异步回调
       //ajax,成功和失败回调
       //返回的是错误信息
       CompletableFuture<Integer> completableFuture = CompletableFuture.supplyAsync(()->{
           System.out.println("completableFuture"+Thread.currentThread().getName());
           int i = 10/0;
           return 1024;
       });
       System.out.println(completableFuture.whenComplete((t, u) -> {
           System.out.println(t);//正常的返回结果
           System.out.println(u);//错误信息java.util.concurrent.CompletionException: java.lang.ArithmeticException: / by zero
       }).exceptionally((e) -> {
           System.out.println(e.getMessage());//java.lang.ArithmeticException: / by zero
           return 233;
       }).get());
   }

十六、JMM

volatile的JVM提供的轻量级的同步机制。

  1. 可见性。(可见性和JMM挂钩)
  2. 不保证原子性。
  3. 禁止指令重排。

什么是JMM?

Java的内存模型,是一个概念,不存在。
JMM同步约定:

  1. 线程解锁前,必须把共享变量立即刷回主内存。
  2. 线程枷锁前,必须读取主内存中最新值到工作内存中。
  3. 加锁和解锁是同一把锁。

Java内存模型定义了8种操作来完成,虚拟机实现必须保证每一种操作都是原子的、不可再拆分的(double和long类型例外)。

  • lock(锁定):作用于主内存的变量,它把一个变量标识为一条线程独占的状态。
  • unlock(解锁):作用于主内存的变量,它把一个处于锁定状态的变量释放出来,释放后的变量才可以被其他线程锁定。
  • read(读取):作用于主内存的变量,它把一个变量的值从主内存传输到线程的工作内存中,以便随后的load动作使用。
  • load(载入):作用于工作内存的变量,它把read操作从主内存中得到的变量值放入工作内存的变量副本中。
  • use(使用):作用于工作内存的变量,它把工作内存中一个变量的值传递给执行引擎,每当虚拟机遇到一个需要使用到变量的值的字节码指令时将会执行这个操作。
  • assign(赋值):作用于工作内存的变量,它把一个从执行引擎接收到的值赋给工作内存的变量,每当虚拟机遇到一个给变量赋值的字节码指令时执行这个操作。
  • store(存储):作用于工作内存的变量,它把工作内存中一个变量的值传送到主内存中,以便随后的write操作使用。
  • write(写入):作用于主内存的变量,它把store操作从工作内存中得到的变量的值放入主内存的变量中。

8种操作必须满足的规则:

  • 不允许read和load、store和write操作之一单独出现。即不允许一个变量从主内存读取了但工作内存不接受;或者从工作内存发起回写了但主内存不接受的情况出现。
  • 不允许一个线程丢弃它的最近的assign操作。即变量在工作内存中改变了之后必须把该变化同步回主内存。
  • 不允许一个线程无原因地(没有发生过任何assign操作)把数据从线程的工作内存同步回主内存。
  • 一个新的变量只能在主内存中“诞生”,不允许在工作内存中直接使用一个未被初始化(load或assign)的变量,换句话说,就是对一个变量实施use、store操作之前,必须先执行过了assign和load操作。
  • 一个变量在同一时刻只允许一条线程对其进行lock操作,但lock操作可以被同一条线程重复执行多次,多次执行lock后,只有执行相同次数的unlock操作,变量才会被解锁。
  • 如果对一个变量执行lock操作,那将会清空工作内存中此变量的值,在执行引擎使用这个变量前,需要重新执行load或assign操作初始化变量的值。
  • 如果一个变量事先没有被lock操作锁定,那就不允许对它执行unlock操作,也不允许去unlock一个被其他线程锁定住的变量。
  • 对一个变量执行unlock操作之前,必须先把此变量同步回主内存中(执行store、write操作)。

img

指令重排

你写的程序,计算机并不一定你写的去执行。
源代码->编译器优化的重排->指令并行也可能会重排->内存系统也会重排->执行
处理器在进行指令重排时,考虑数据之间的依赖性。

volatile避免指令重排:
内存屏障(CPU的指令)。

  1. 保证特定操作的执行顺序。
  2. 可以保证某些变量的内存可见性。

十七、单例模式

https://blog.youkuaiyun.com/aijson_update/article/details/126935677

十八、深入理解CAS

什么是CAS

比较并交换:compare and swap !
比较当前内存中的值和主内存中的值,如果主内存中的值是期望的,就执行操作,否则不执行操作。

public class CASDemo {
    public static void main(String[] args) {
        AtomicInteger atomicInteger = new AtomicInteger(2020);
        // 期望,更新:public final boolean compareAndSet(int expect, int update)
        // 如果我期望的值达到了(2020)就更新,否则不更新。
        System.out.println(atomicInteger.compareAndSet(2020, 2021));// true
        System.out.println(atomicInteger.get());// 2021
        System.out.println(atomicInteger.compareAndSet(2020, 2021));// false
        System.out.println(atomicInteger.get());// 2021
    }
}

Unsafe类

img
img
img

CAS的缺点

  1. 循环会耗时。
  2. 一次性只能保证一个共享变量的原子性。
  3. 存在ABA问题。

原子引用解决ABA问题

(乐观锁)版本号+1

十九、各种锁的理解

公平锁、非公平锁

可重入锁

某个线程已经获得某个锁,可以再次获取锁而不会出现死锁。(加锁的方法可以相互调用)

public class Demo01 {
    public static void main(String[] args) {
        Phone phone = new Phone();
        new Thread(()->{phone.sms();},"A").start();
        new Thread(()->{phone.sms();},"B").start();
    }
}
class Phone {
    public synchronized void sms() {
        System.out.println(Thread.currentThread().getName() + "发短信");
        call();// 这个方法也有锁
    }
    private synchronized void call() {
        System.out.println(Thread.currentThread().getName() + "打电话");
    }
}

打印:
img

自旋锁

不断尝试,直到成功。
img

死锁

死锁:线程A持有锁A,想要获得锁B;线程B持有锁B,想要获得锁A。

解决方法:查看堆栈信息

  1. 使用jps -l命令查看进程号。(该命令在JDK的bin目录下)
  2. 使用jstack+进程号,找到死锁问题。

总结

    System.out.println(atomicInteger.get());// 2021
    System.out.println(atomicInteger.compareAndSet(2020, 2021));// false
    System.out.println(atomicInteger.get());// 2021
}

}

###Unsafe类
在这里插入图片描述
在这里插入图片描述

CAS的缺点

  1. 循环会耗时。
  2. 一次性只能保证一个共享变量的原子性。
  3. 存在ABA问题。

原子引用解决ABA问题

(乐观锁)版本号+1

十九、各种锁的理解

公平锁、非公平锁

可重入锁

某个线程已经获得某个锁,可以再次获取锁而不会出现死锁。(加锁的方法可以相互调用)

```java
public class Demo01 {
    public static void main(String[] args) {
        Phone phone = new Phone();
        new Thread(()->{phone.sms();},"A").start();
        new Thread(()->{phone.sms();},"B").start();
    }
}
class Phone {
    public synchronized void sms() {
        System.out.println(Thread.currentThread().getName() + "发短信");
        call();// 这个方法也有锁
    }
    private synchronized void call() {
        System.out.println(Thread.currentThread().getName() + "打电话");
    }
}

打印:
在这里插入图片描述

自旋锁

不断尝试,直到成功。
在这里插入图片描述

死锁

死锁:线程A持有锁A,想要获得锁B;线程B持有锁B,想要获得锁A。

解决方法:查看堆栈信息

  1. 使用jps -l命令查看进程号。(该命令在JDK的bin目录下)
  2. 使用jstack+进程号,找到死锁问题。

总结

面试的:单例模式、排序算法、生产者和消费者、死锁

JUC并发编程入门学习笔记(狂神说) 因为热爱所以坚持,因为热爱所以等待。熬过漫长无戏可演的日子,终于换来了人生的春天,共勉!!! 1. 什么是JUC JUC就是java.util.concurrent下面的类包,专门用于多线程的开发。 2. 线程和进程 进程是操作系统中的应用程序、是资源分配的基本单位,线程是用来执行具体的任务和功能,是CPU调度和分派的最小单位 一个进程往往可以包含多个线程,至少包含一个 1)进程 一个程序,QQ.EXE Music.EXE;数据+代码+pcb 一个进程可以包含多个线程,至少包含一个线.. 阅读详情

相关推荐

狂神JUC并发笔记大全+Linux等资料

狂神JUC并发笔记大全+Linux等资料

【JUC并发编程】

JUC是Java Util Concurrent的缩写,是Java SE 5.0中新增的一个包,主要提供了在并发编程中使用的工具类和框架。JUC包含了许多用于多线程编程的工具类和接口,如锁、原子变量、线程池、阻塞队列等。这些工具类和接口可以帮助开发者更方便地进行多线程编程,从而提高程序的并发性能和可靠性。JUC包中的工具类和接口都是线程安全的,可以在多线程环境下安全地使用。它们的设计和实现都是基于并发编程的最佳实践和原则,可以有效地避免线程安全问题的发生。

蛋饼吧的博客 1823

狂神说JUC.pdf

Bilibili狂神老师的JUC课程笔记,讲的非常的好,希望大家结合视频学习狂神老师的JUC编程,精进自己的技术。

个人学习笔记(狂神JUC笔记)

JUC就是java.util下的工具包、包、分类等。是实现多并发,多线程的基本知识。

xiaobai_1hao的博客 1021

第六节 旅烨cms实战练习 -2021.03.11

第六节 旅烨cms实战练习 -2021.03.11 前期使用win10里小皮搭建旅烨cms,以为自己的各项配置有问题,但是小皮能够正常带动DVWA,始终带动不了旅烨cms。 后来与其他安全人员交流,发现win7可以正常运行旅烨cms。 所以目前为何出现问题尚不清楚 1.环境搭建 win7+小皮 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nOSTEFUw-1615475228402)(C:\Users\LuXian\AppData\Roaming\Typora\typora

lyx3429的博客 571

视觉语言模型对抗攻击与防御:从传统方法到多模态挑战

本文深入探讨了视觉语言模型(VLM)面临的对抗攻击与防御挑战,从传统单模态攻击到多模态威胁的演进。分析了包括FGSM、PGD等经典攻击方法,以及新兴的多模态攻击如跨模态对抗样本和越狱攻击。同时提出了跨模态一致性检查、动态防御机制等创新防御策略,并结合医疗影像诊断等实战案例,为提升AI系统安全性提供了重要见解。

weixin_29231725的博客 391

【狂神说Java】JUC笔记

整理的这些资料希望对Java开发的朋友们有所参考以及少走弯路,本文的重点是你有没有收获与成长,其余的都不重要,希望读者们能谨记这一点。其实面试这一块早在第一个说的25大面试专题就全都有的。以上提及的这些全部的面试+学习的各种笔记资料,我这差不多来回搞了三个多月,收集整理真的很不容易,其中还有很多自己的一些知识总结。正是因为很麻烦,所以对以上这些学习复习资料感兴趣**注意:Integer 使用了对象缓存机制,默认范围是 -128 ~ 127 ,推荐使用静态工厂方法 valueOf 获取对象实。

2401_85379544的博客 1185

3D点云处理:PointNet++与PointTransformer的对比

摘要: 3D点云处理中,PointNet++和PointTransformer是两种主流深度学习模型。PointNet++通过分层采样和分组策略提取局部特征,而PointTransformer利用自注意力机制捕捉全局上下文。实验表明,PointTransformer在分类(ModelNet40准确率92.1%)和分割(ShapeNet mIoU 85.7%)任务上性能更优,但计算开销较大(训练时间180秒/轮)。PointNet++在实时性场景更具优势(训练时间120秒/轮)。未来可结合两者优势,或优化Tr

shejizuopin的博客 1117

狂神说juc笔记

JUC就是java.util.concurrent包及其子包java默认有两个线程,一个是main线程,一个是GC线程对于java而言有三中开启线程的方式:Thread,Runnable,Callable,但是实际上并不是java开启的线程,调用的是底层的C++开启线程并发:多个线程操作同一个资源并行:多个线程同时进行,只有多核cpu才能并行java获取CPU核心数量 并发编程的本质:充分利用CPU的资源线程的状态:来自不同类,wait来自Object,sleep来自Thread企业中休眠使用的是TimeU

m0_51654746的博客 1297

10、网络安全工具:Bettercap 与 Web 应用测试工具全解析

本文详细解析了网络安全工具Bettercap及其在中间人攻击和无线攻击中的应用,同时介绍了多种Web应用测试工具,如Amass、Cadaver、Dirb等,并探讨了工具的使用场景、流程、注意事项以及未来发展趋势,为网络安全测试提供了全面的指导。

u0v1w2x3的博客 226

rk3506上移植lvgl应用

本文介绍在RK3506开发板上移植和运行LVGL图形库的方法。

m0_60805802的博客 1157

JUC并发编程学习笔记(狂神)

目录JUC并发编程1. 什么是JUC2. 线程和进程1)进程2)线程3)并发4)并行5)线程的状态6)wait/sleep3.Lock1)传统的 synchronized2)Lock3. Synchronized 与Lock 的区别4. 生产者和消费者的关系1)Synchronzied 版本2)存在问题(虚假唤醒)3)Lock版4)Condition的优势5. 8锁现象6. 集合不安全1)List 不安全2)set 不安全3)Map不安全7. Callable8. 常用的辅助类1)CountDownLatc

weixin_44491927的博客 1万+

性能优化-高通cDSP(QDSP6)架构详解

高通cDSP开发的商业价值30秒快速认识高通cDSP开发的商业价值【高通cDSP旨在处理一些简单但需要大量计算的场景,如图像处理、科学计算、AI训练、计算机视觉等。这些场景使用CPU来运算会极大的降低CPU的吞吐量,因此利用现代寄存器较宽的特性设计出了适配数据并行的DSP芯片,cDSP就是典型的代表。利用cDSP+CPU既可以满足一般的使用场景,也可以完成现代图像和视频数据的处理。

发狂的小花的博客 5942

JUC并发编程狂神说笔记(超详细)

List item List item List item List item

weixin_48412846的博客 8598

【UVM实战】第七章:UVM中的寄存器模型(1)寄存器模型简介

寄存器模型简介~

悟已往之不谏 知来者之可追 2070

基于YOLOv8的违规抽烟/打电话检测系统:从数据集构建到部署实战

模型优点缺点精度高速度慢(5 FPS)SSD速度快小目标检测差YOLOv5平衡性好官方支持停止YOLOv8最新、最快、最准需要PyTorch环境YOLOv8相比前代:骨干网络升级(C2f模块)无锚框检测(Anchor-Free)动态任务对齐分配(TaskAlignedAssigner)支持分类/检测/分割/姿态估计。

YOLO 97

JUC并发编程超详细详解篇

文章目录前言一、JUC并发编程简介及准备1.1.本系列讲解思路1.2.知识储备1.3.用到的依赖1.4.logback.xml 配置二、进程与线程2.1.进程与线程2.1.1.进程2.1.2.线程2.1.3.对比2.2.并行与并发2.2.1.两者区别直观解释2.3.应用2.3.1. 应用之异步调用(案例1)2.3.2.应用之提高效率(案例1)三、Java 线程3.1.本节主讲内容3.2.创建和运行线程3.2.1.方法一,直接使用 Thread3.2.2.方法二,使用 Runnable 配合 Thread3.

白大锅的博客,毕设WangLoveBai_999 7万+

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

JUC并发编程——JUC并发编程概述及Lock锁(重点)(基于狂神说的学习笔记)

JUC时java.util工具包中的三个包的简称业务:普通的线程代码中,我们常使用Runnable接口但Runnable没有返回值,且效率相比较于Callable来说相对较低,功能也没有Callable强大Lock锁与synchronized锁的区别要分清在多线程情况下,不要使用if条件语句来判断是否wait或await某一线程,要用while循环语句来判断,否则线程不安全。

Lyrical-Wander的博客 1663

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

狂神说JUC

狂神说JUC课程地址:https://www.bilibili.com/video/BV1B7411L7tE/ 狂神说JUC 1、什么是JUC **源码+官方文档 ** 面试高频问! java.util 工具包、包、分类 业务:普通的线程代码 Thread Runnable 没有返回值、效率相比于Callable相对较低 2、线程和进程 进程是操作系统中的应用程序、是资源分配的基本单位,线程是用来执行具体的任务和功能,是CPU调度和分派的最小单位 一个进程往往可以包含多个线程,至少包含一个 1)

m0_62672124的博客 1250

SAP B1中文培訓教材_SDK

公司内部培训资料,对开发工具 ,DIAPI, UIAPI,Add-ON有详细解说

JUC底层的详细学习+狂神JUC笔记+全网最全

1、什么是JUC 源码+官方文档 JUC是 java util concurrent 面试高频问JUC~! java.util 是Java的一个工具包~ 业务:普通的线程代码 Thread Runnable: 没有返回值、效率相比于Callable 相对较低! 2、线程和进程 进程:一个程序,QQ.EXE Music.EXE;数据+代码+pcb 一个进程可以包含多个线程,至少包含一个线程! Java默认有几个线程?2个线程! main线程、GC线程 线程:开了一个进程Typora,写字,等待几分钟会进行

qq_41617848的博客 2万+

【狂神说Java】JUC并发编程

【狂神说Java】JUC并发编程 准备工作 新建一个Maven项目,引入一个lombok依赖. <dependencies> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.8</vers

ZJC'博客 1013

多线程进阶==JUC并发编程狂神说

多线程进阶==>JUC并发编程 视频地址 1.什么是JUC java.util工具包 业务: 普通的线程代码, 之前都是用的thread或者runnable接口 但是相比于callable来说,thread没有返回值,且效率没有callable高 2. 线程和进程 线程,进程 进程 : 一个运行中的程序的集合; 一个进程往往可以包含多个线程,至少包含一个线程 java默认有几个线程? 两个 main线程 gc线程 线程 : 线程(thread)是操作系统能够进行运算调度的最小单位。 对于

weixin_44502509的博客 2177

JUC并发编程-B站【狂神说Java笔记】

狂神视频地址:https://www.bilibili.com/video/BV1B7411L7tE 准备工作 新建一个Maven项目,引入一个lombok依赖. <dependencies> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId>

晴曦的博客 970

狂神笔记链接整理

狂神所有笔记链接整理 有更好的烦请call me 感谢 技术 链接 基础面试题 https://gitee.com/li123456sen/openclass 多线程 (凑合用) https://blog.youkuaiyun.com/weixin_44502509/article/details/106794227 网络编程(凑合用) https://blog.youkuaiyun.com/qq_38377803/article/details/105480602 java注解与反射(凑合用) ht

lisen01070107的博客 7654
上一篇: 23种设计模式
下一篇: ConcurrentHashMap原理,以及在jdk7和jdk8版本的区别
半鸭学长
博客等级 码龄5年 131粉丝 23原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值