diff --git a/week_04/DelayQueue b/week_04/DelayQueue deleted file mode 100644 index ea9c10d0f7dd99929daafe38fc59c8439490f413..0000000000000000000000000000000000000000 --- a/week_04/DelayQueue +++ /dev/null @@ -1,13 +0,0 @@ -1、总结: - DelayQueue是java并发包下的延时阻塞队列,常用于实现定时任务。 - - 内部存储结构使用优先级队列PriorityQueue q = new PriorityQueue(); - - 入队方法,add(E e)/put(E e) /offer(E e, long timeout, TimeUnit unit)/ ,实现均为offer(E e) - 因为DelayQueue是阻塞队列,且优先级队列是无界的,所以入队不会阻塞不会超时; - - 出队方法,poll()/take() - - demo参考 死磕 java集合之DelayQueue源码分析 - -2、学习参考“彤哥读源码”公众号 diff --git a/week_05/09/Executors.md b/week_05/09/Executors.md new file mode 100644 index 0000000000000000000000000000000000000000..0b74a4c3a2858bca564370fa383a9555d30511b6 --- /dev/null +++ b/week_05/09/Executors.md @@ -0,0 +1,141 @@ +Executors + + +1)预定义线程池 + +```java +//创建固定数量的普通线程池 +public static ExecutorService newFixedThreadPool(int nThreads) { + //通过ThreadPoolExecutor实现类,创建核心线程数及最大线程数都为nThreads的线程池 + //空闲超时时间为0,且由于核心线程数和最大线程数一样,核心线程不会空闲超时后被回收 + //LinkedBlockingQueue为任务队列实现 + return new ThreadPoolExecutor(nThreads, nThreads, + 0L, TimeUnit.MILLISECONDS, + new LinkedBlockingQueue()); +} + +//nThread为线程池中线程的数量,线程创建的工厂类实现类为threadFactory +public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) { + return new ThreadPoolExecutor(nThreads, nThreads, + 0L, TimeUnit.MILLISECONDS, + new LinkedBlockingQueue(), + threadFactory); +} + +``` + +2)创建可任务窃取的线程池 + +```java +//parallelism并行任务数 +public static ExecutorService newWorkStealingPool(int parallelism) { + + //基于ForkJoinPool创建线程池; + //使用默认线程创建工厂类 + //执行任务异常处理类为null + //任务队列模型为FIFO,true为FIFI,false为FILO + return new ForkJoinPool + (parallelism, + ForkJoinPool.defaultForkJoinWorkerThreadFactory, + null, true); +} + +``` + + +3)创建单个普通线程池 + +```java +//创建单个普通线程池 +public static ExecutorService newSingleThreadExecutor() { + //用FinalizableDelegatedExecutorService进行封装,其会在 + //finalize()方法中调用线程池的shutdown()方法,对线程池进行关闭 + return new FinalizableDelegatedExecutorService + (new ThreadPoolExecutor(1, 1, + 0L, TimeUnit.MILLISECONDS, + new LinkedBlockingQueue())); +} + +public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) { + return new FinalizableDelegatedExecutorService + (new ThreadPoolExecutor(1, 1, + 0L, TimeUnit.MILLISECONDS, + new LinkedBlockingQueue(), + threadFactory)); +} + +``` + + +4)创建线程数量不限的普通线程池 + +```java +//创建缓存的线程池,即线程池数量不限 +public static ExecutorService newCachedThreadPool() { + return new ThreadPoolExecutor(0, Integer.MAX_VALUE, + 60L, TimeUnit.SECONDS, + new SynchronousQueue()); +} + + +public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) { + return new ThreadPoolExecutor(0, Integer.MAX_VALUE, + 60L, TimeUnit.SECONDS, + new SynchronousQueue(), + threadFactory); +} + +``` + +5)创建单个基于时间调度的线程池 + +```java +//创建单个基于时间调度的线程池 +public static ScheduledExecutorService newSingleThreadScheduledExecutor() { + + //利用DelegatedScheduledExecutorService对线程池进行包装 + //DelegatedScheduledExecutorService利用门面模式包装了ScheduledExecutorService + //使得用户无法修改ScheduledExecutorService中的一些参数 + return new DelegatedScheduledExecutorService + (new ScheduledThreadPoolExecutor(1)); +} + + +``` + + +6)创建多个基于时间调度的线程池 + +```java +//创建多个基于时间调度的线程池 +//创建固定线程池的可基于时间调度的线程池,corePoolSize为核心线程池测数量 +public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) { + return new ScheduledThreadPoolExecutor(corePoolSize); +} + + +public static ScheduledExecutorService newScheduledThreadPool( + int corePoolSize, ThreadFactory threadFactory) { + return new ScheduledThreadPoolExecutor(corePoolSize, threadFactory); +} +``` + + +7)包装配置不可更改的线程池 + +```java +//对ExecutorService类型线程池进行包装,使用户无法对一些参数进行修改 +public static ExecutorService unconfigurableExecutorService(ExecutorService executor) { + if (executor == null) + throw new NullPointerException(); + return new DelegatedExecutorService(executor); +} + +//对ScheduledExecutorService类型线程池进行包装,使用户无法对一些参数进行修改 +public static ScheduledExecutorService unconfigurableScheduledExecutorService(ScheduledExecutorService executor) { + if (executor == null) + throw new NullPointerException(); + return new DelegatedScheduledExecutorService(executor); +} + +``` \ No newline at end of file diff --git "a/week_05/09/Leetcode-\344\272\244\346\233\277\346\211\223\345\215\260foobar.md" "b/week_05/09/Leetcode-\344\272\244\346\233\277\346\211\223\345\215\260foobar.md" deleted file mode 100644 index c4db1df2d1220abe8001d6472146e297bd3cbcdb..0000000000000000000000000000000000000000 --- "a/week_05/09/Leetcode-\344\272\244\346\233\277\346\211\223\345\215\260foobar.md" +++ /dev/null @@ -1,72 +0,0 @@ -Leetcode——交替打印foobar - -解法一:利用synchronized - -```java -public class FooBar { - - private int n ; //输出次数 - private boolean isFooBar = true; //是否执行打印 - private Object o = new Object(); //锁对象 - - public FooBar(int n) { - this.n = n; - } - - public void foo(Runnable foo) throws InterruptedException { - for(int i = 0; i < n ; i ++){ - synchronized (o){ - if(!isFooBar){ - o.wait(); - } - foo.run(); - isFooBar = false; - System.out.println("foo"); - o.notify(); - } - - } - } - - public void bar(Runnable bar) throws InterruptedException { - for(int j = 0 ; j < n ;j++){ - synchronized (o){ - if(isFooBar){ - o.wait(); - } - bar.run(); - isFooBar = true; - System.out.println("bar"); - o.notify(); - } - - } - } - public static void main(String[] args) throws InterruptedException { - FooBar fooBar = new FooBar(2); - //创建一个foo线程: - new Thread(){ - public void run(){ - try { - fooBar.foo(new Thread()); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } - }.start(); - //创建一个bar线程 - new Thread(){ - public void run(){ - try { - fooBar.bar(new Thread()); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } - }.start(); - - } -} - -``` - diff --git "a/week_05/09/Leetcode\344\272\244\346\233\277\346\211\223\345\215\2600\344\270\216\345\245\207\345\201\266\346\225\260.md" "b/week_05/09/Leetcode\344\272\244\346\233\277\346\211\223\345\215\2600\344\270\216\345\245\207\345\201\266\346\225\260.md" new file mode 100644 index 0000000000000000000000000000000000000000..6c2a3d44e0960021f4893b2daa7b9ea9076cda6d --- /dev/null +++ "b/week_05/09/Leetcode\344\272\244\346\233\277\346\211\223\345\215\2600\344\270\216\345\245\207\345\201\266\346\225\260.md" @@ -0,0 +1,105 @@ +Leetcode交替打印0与奇偶数 + +题目: + + 每个线程都有一个 printNumber 方法来输出一个整数 + 请修改给出的代码以输出整数序列 010203040506... ,其中序列的长度必须为 2n + +```java + +//利用Semaphore控制实现 +public class ZeroEvenOdd { + + private int n; // n*2n的长度 + + private int num = 0 ; + + Semaphore z = new Semaphore(1); + Semaphore e = new Semaphore(0); + Semaphore o = new Semaphore(0); + + + public ZeroEvenOdd(int n) { + this.n = n; + } + + public void zero() throws InterruptedException { //仅打印出来0 + for(int i=0; i { + + try { + z1.even(); + } catch (InterruptedException ex) { + ex.printStackTrace(); + } + + }).start(); + + + //线程b 负责打印奇数 + new Thread(() -> { + + try { + z1.odd(); + } catch (InterruptedException ex) { + ex.printStackTrace(); + } + + }).start(); + + //线程c 负责打印0 + new Thread(() -> { + + try { + z1.zero(); + } catch (InterruptedException ex) { + ex.printStackTrace(); + } + + }).start(); + + } +} + +``` \ No newline at end of file diff --git a/week_05/09/ThreadLocal.md b/week_05/09/ThreadLocal.md new file mode 100644 index 0000000000000000000000000000000000000000..9e8b1883d55548a6509b0f69db76a2ad058d60ed --- /dev/null +++ b/week_05/09/ThreadLocal.md @@ -0,0 +1,105 @@ +ThreadLocal + +1、 + ThreadLocal 是 Thread的一个局部变量,保存线程的副本 + + ThreadLocalMap 是 ThreadLocal 的内部类,是用基于线性探测法的散列表实现的。 + 每一个线程对象可以往 Map 中添加多个 ThreadLocal 对象为键的键值对,每个键对应的值唯一。所以通过一个 ThreadLocal 对象设置的值,在每个线程中都是唯一且互相独立的。 + 唯一是因为键的唯一性,独立是因为每个线程都有自己的ThreadLocalMap内部变量,它是归线程所有的。 + ThreadLocal是线程Thread中属性threadLocals的管理者,也就是说我们对于ThreadLocal的get, set,remove的操作结果都是针对当前线程Thread实例的threadLocals存,取,删除操作。 + +2、内部类 + + ThreadLocalMap() + + ps:注意其中特殊的类WeakReference + + ```java + static class Entry extends WeakReference> { + /** The value associated with this ThreadLocal. */ + Object value; + + Entry(ThreadLocal k, Object v) { + super(k); + value = v; + } + } + ``` + 简单介绍下很少使用的这个类WeakReference + +| 类型 | 回收时间 | 应用场景| +| :------------------------------ | ---------- | ---------| +|强引用 |一直存活,除非GC Roots不可达|所有程序的场景,基本对象,自定义对象等| +|弱引用 |只能存活到下一次GC前 |生命周期很短的对象,例如ThreadLocal中的Key。| +|软引用 |内存不足时会被回收 |一般用在对内存非常敏感的资源上,用作缓存的场景比较多,例如:网页缓存、图片缓存 +|虚引用 |随时会被回收, 创建了可能很快就会被回收|可能被JVM团队内部用来跟踪JVM的垃圾回收活动| + + +3、构造器 + +```java +//默认构造器 +public ThreadLocal() { +} + +``` + +4、set() :当前线程的 ThreadLocalMap 对象添加键值对,键为 ThreadLocal 对象,值为设置的 value 对象 + +```java +public void set(T value) { + Thread t = Thread.currentThread(); + ThreadLocalMap map = getMap(t); + if (map != null) + map.set(this, value); + else + createMap(t, value); +} + +``` + +5、get(): 线程中调用 get 方法时,如果之前用 set 方法为这个 ThreadLocal 键添加过值,那么就返回设置过的值,否则就返回默认初始值 null + +```java +public T get() { + Thread t = Thread.currentThread(); + ThreadLocalMap map = getMap(t); + if (map != null) { + // 已经调用过set或get方法时,对象值已经设置过,就返回上一次的值 + ThreadLocalMap.Entry e = map.getEntry(this); + if (e != null) { + @SuppressWarnings("unchecked") + T result = (T)e.value; + return result; + } + } + // 没有调用过set或get方法时,对象值没有设置过,就自动设置初始值,并返回初始值 + return setInitialValue(); +} + +private T setInitialValue() { + //获得初始值 + T value = initialValue(); + //set方法 + Thread t = Thread.currentThread(); + ThreadLocalMap map = getMap(t); + if (map != null) + map.set(this, value); + else + createMap(t, value); + return value; +} + + + +``` + +6、模拟复现ThreadLocal导致内存泄漏(内存检察工具没有装好,暂时未提交) + + + +7、学习参考链接: +https://blog.csdn.net/zzg1229059735/article/details/82715741(包含github地址代码) + + + diff --git a/week_05/09/ThreadPoolExecutor.md b/week_05/09/ThreadPoolExecutor.md new file mode 100644 index 0000000000000000000000000000000000000000..4ad53341bd76c84bacde6747bbccb0244604975a --- /dev/null +++ b/week_05/09/ThreadPoolExecutor.md @@ -0,0 +1,265 @@ +ThreadPoolExecutor + +1、认识 + +```java +//继承AbstractExecutorService +public class ThreadPoolExecutor extends AbstractExecutorService{} + +//实现ExecutorService接口 +public abstract class AbstractExecutorService implements ExecutorService{} + +//继承Executor +public interface ExecutorService extends Executor {} + + +public interface Executor { + //启动线程 + void execute(Runnable command); +} + +``` + +2、构造器 + + + +```java + +public ThreadPoolExecutor(int corePoolSize, + int maximumPoolSize, + long keepAliveTime, + TimeUnit unit, + BlockingQueue workQueue, + ThreadFactory threadFactory) { + this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, + threadFactory, defaultHandler); +} + +public ThreadPoolExecutor(int corePoolSize, + int maximumPoolSize, + long keepAliveTime, + TimeUnit unit, + BlockingQueue workQueue, + RejectedExecutionHandler handler) { + this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, + Executors.defaultThreadFactory(), handler); +} + + +public ThreadPoolExecutor(int corePoolSize, + int maximumPoolSize, + long keepAliveTime, + TimeUnit unit, + BlockingQueue 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.corePoolSize = corePoolSize; + this.maximumPoolSize = maximumPoolSize; + this.workQueue = workQueue; + this.keepAliveTime = unit.toNanos(keepAliveTime); + this.threadFactory = threadFactory; + this.handler = handler; +} + + +``` + +| 序号 | 名称 | 类型 | 含义 | +| :---| --------------| ----------- |----------- | +| 1 | corePoolSize| int |核心线程池大小| +| 2 | maximumPoolSize| int |最大线程池大小| +| 3 | keepAliveTime | long |线程最大空闲时间| +| 4 | unit | TimeUnit|时间单位 | +| 5 | workQueue | BlockingQueue| 线程等待队列| +| 6 | threadFactory | ThreadFactory|线程创建工厂| +| 7 | handler | RejectedExecutionHandler|拒绝策略| + + +3、执行原理execute() + +```java + + public void execute(Runnable command) { + if (command == null) + throw new NullPointerException(); + int c = ctl.get(); + //工作线程 < 核心线程数量 + if (workerCountOf(c) < corePoolSize) { + //创建线程执行任务 + if (addWorker(command, true)) + return; + c = ctl.get(); + } + //运行态,并尝试将任务加入队列 + if (isRunning(c) && workQueue.offer(command)) { + int recheck = ctl.get(); + if (! isRunning(recheck) && remove(command)) + reject(command); + else if (workerCountOf(recheck) == 0) + addWorker(null, false); + } + //使用尝试最大线程运行 + else if (!addWorker(command, false)) + reject(command); //执行拒绝策略 +} + +//接下来看看addWorker(command,boolean)方法, +//先看不同参数值含义: + +addWorker(command, true): 创建核心线程执行任务; +addWorker(command, false):创建非核心线程执行任务; +addWorker(null, false): 创建非核心线程,当前任务为空 + +//继续具体看看该方法的源码分析 + +private boolean addWorker(Runnable firstTask, boolean core) { + // 第一部分:原子操作、自旋、CAS、重读ctl 等结合,直到确定是否可以创建worker, + // 可以则跳出循环继续操作,否则返回false + retry: + for (;;) { + int c = ctl.get(); + int rs = runStateOf(c); + + // Check if queue empty only if necessary. + if (rs >= SHUTDOWN && + ! (rs == SHUTDOWN && + firstTask == null && + ! workQueue.isEmpty())) + return false; + + for (;;) { + int wc = workerCountOf(c); + if (wc >= CAPACITY || + wc >= (core ? corePoolSize : maximumPoolSize)) + return false; + if (compareAndIncrementWorkerCount(c)) + break retry; + c = ctl.get(); // Re-read ctl + if (runStateOf(c) != rs) + continue retry; + // else CAS failed due to workerCount change; retry inner loop + } + } + + // 第二部分:创建worker,这部分使用ReentrantLock锁 + boolean workerStarted = false; //线程启动标志位 + boolean workerAdded = false; //线程是否加入workers 标志位 + Worker w = null; + try { + w = new Worker(firstTask); //创建worker + final Thread t = w.thread; + if (t != null) { + final ReentrantLock mainLock = this.mainLock; + mainLock.lock(); + try { + // Recheck while holding lock. + // Back out on ThreadFactory failure or if + // shut down before lock acquired. + int rs = runStateOf(ctl.get()); + + if (rs < SHUTDOWN || + (rs == SHUTDOWN && firstTask == null)) { + if (t.isAlive()) // precheck that t is startable + throw new IllegalThreadStateException(); + workers.add(w); + int s = workers.size(); + if (s > largestPoolSize) + largestPoolSize = s; + workerAdded = true; + } + } finally { + mainLock.unlock(); + } + if (workerAdded) { + t.start(); //启动线程 + workerStarted = true; + } + } + } finally { + if (! workerStarted) + addWorkerFailed(w); //失败操作 + } + return workerStarted; +} + + +``` + +4、内部类Worker + +```java +//实现了 Runnable 并继承了AbstractQueuedSynchronizer +private final class Worker + extends AbstractQueuedSynchronizer + implements Runnable{ + + /**每个worker有自己的内部线程,ThreadFactory创建失败时是null */ + final Thread thread; + /**初始化任务,可能是null*/ + Runnable firstTask; + /**每个worker的完成任务数*/ + volatile long completedTasks; + + Worker(Runnable firstTask) { + setState(-1); // 禁止线程在启动前被打断 + this.firstTask = firstTask; + this.thread = getThreadFactory().newThread(this); + } + + /*重要的执行方法 */ + public void run() { + runWorker(this); + } + + //state = 0 代表未锁;state = 1 代表已锁 + protected boolean isHeldExclusively() { + return getState() != 0; + } + + protected boolean tryAcquire(int unused) { + if (compareAndSetState(0, 1)) { + setExclusiveOwnerThread(Thread.currentThread()); + return true; + } + return false; + } + + protected boolean tryRelease(int unused) { + setExclusiveOwnerThread(null); + setState(0); + return true; + } + + public void lock() { acquire(1); } + public boolean tryLock() { return tryAcquire(1); } + public void unlock() { release(1); } + public boolean isLocked() { return isHeldExclusively(); } + // interrupt已启动线程 + void interruptIfStarted() { + Thread t; + // 初始化是 state = -1,不会被interrupt + if (getState() >= 0 && (t = thread) != null && !t.isInterrupted()) { + try { + t.interrupt(); + } catch (SecurityException ignore) { + } + } + } + +} + + +``` + + Worker 实现了简单的 非重入互斥锁,互斥容易理解,非重入是为了避免线程池的一些控制方法获得重入锁,比如setCorePoolSize操作。 + 注意 Worker 实现锁的目的与传统锁的意义不太一样。其主要是为了控制线程是否可interrupt,以及其他的监控,如线程是否 active(正在执行任务)。 + + diff --git a/week_05/09/thread.md b/week_05/09/thread.md new file mode 100644 index 0000000000000000000000000000000000000000..4a195219ee5f99e4a490842c79c1e44e4ec826cf --- /dev/null +++ b/week_05/09/thread.md @@ -0,0 +1,369 @@ +Thread + +1、关于线程 + 看见线程,首先会考虑想到线程与进程的区别,线程的基本状态,线程的调度,线程的切换等相关知识点。(如需要复习,自行查阅相关知识点) + +2、java.lang.Thread类 + + 1)实现Runnable接口 + + 2)属性 + +```java + //线程名字 + private volatile char name[]; + //线程优先级(最大值为10,最小值为1,默认值为5) + private int priority; + private Thread threadQ; + private long eetop; + + /* Whether or not to single_step this thread. */ + private boolean single_step; + + /* 表示线程是否是守护线 */ + private boolean daemon = false; + + /* JVM state */ + private boolean stillborn = false; + + /* 执行任务. */ + private Runnable target; + + /* The group of this thread */ + private ThreadGroup group; + + /* The context ClassLoader for this thread */ + private ClassLoader contextClassLoader; + + /* The inherited AccessControlContext of this thread */ + private AccessControlContext inheritedAccessControlContext; + + /* For autonumbering anonymous threads. */ + private static int threadInitNumber; + + /*栈的深度,默认为0,此数字如何被使用完全取决于虚拟机自己;也有一些虚拟机会忽略此变量值.*/ + private long stackSize; + + /* + * JVM-private state that persists after native thread termination. + */ + private long nativeParkEventPointer; + + /* + * Thread ID + */ + private long tid; + + /* For generating thread ID */ + private static long threadSeqNumber; + + /* + * 为工具提供的线程状态值,初始化值标识当前线程还未运行 + */ + private volatile int threadStatus = 0; + + /** + * The minimum priority that a thread can have. + */ + public final static int MIN_PRIORITY = 1; + + /** + * The default priority that is assigned to a thread. + */ + public final static int NORM_PRIORITY = 5; + + /** + * The maximum priority that a thread can have. + */ + public final static int MAX_PRIORITY = 10; +``` + + 3)构造器 + +```java + //默认构造器 + public Thread() + //传入Runnable接口实现 + public Thread(Runnable target) + //设置当前线程用户组 + public Thread(ThreadGroup group, Runnable target) + //设置传入线程名 + public Thread(String name) + //设置用户组,传入线程名 + public Thread(ThreadGroup group, String name) + //传入Runnable接口实现,传入线程名 + public Thread(Runnable target, String name) + //设置用户组,传入Runnable接口实现,传入线程名 + public Thread(ThreadGroup group, Runnable target, String name) + //设置用户组,传入Runnable接口实现,传入线程名,设置当前线程栈大小 + public Thread(ThreadGroup group, Runnable target, String name,long stackSize) + + + /** + * 初始化线程 + * + * @param g the Thread group + * @param target the object whose run() method gets called + * @param name the name of the new Thread + * @param stackSize the desired stack size for the new thread, or + * zero to indicate that this parameter is to be ignored. + * @param acc the AccessControlContext to inherit, or + * AccessController.getContext() if null + */ + private void init(ThreadGroup g, Runnable target, String name, + long stackSize, AccessControlContext acc) { + //线程名称不能为空 + if (name == null) { + throw new NullPointerException("name cannot be null"); + } + + Thread parent = currentThread(); + //获得系统的安全管理器 + SecurityManager security = System.getSecurityManager(); + if (g == null) { // 线程的组为空 + + if (security != null) { + // 线程的组为空,从系统安全管理器中获得线 + g = security.getThreadGroup(); + } + + //系统安全管理器为null,则使用当前执行线程的组 + if (g == null) { + g = parent.getThreadGroup(); + } + } + + //检查是否允许当前线程修改线程组的参数 + g.checkAccess(); + + /* + * 是否受限访问 + */ + if (security != null) { + if (isCCLOverridden(getClass())) { + security.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION); + } + } + + // 将线程组中的容器容量增加1 + g.addUnstarted(); + + this.group = g; + //继承父线程是否为守护线程 + this.daemon = parent.isDaemon(); + //继承父线程的优先级 + this.priority = parent.getPriority(); + this.name = name.toCharArray(); + // 获取类加载器 + if (security == null || isCCLOverridden(parent.getClass())) + this.contextClassLoader = parent.getContextClassLoader(); + else + this.contextClassLoader = parent.contextClassLoader; + this.inheritedAccessControlContext = + acc != null ? acc : AccessController.getContext(); + // 获取执行方法 + this.target = target; + setPriority(priority); + if (parent.inheritableThreadLocals != null) + this.inheritableThreadLocals = + ThreadLocal.createInheritedMap(parent.inheritableThreadLocals); + /*设置栈的深度 */ + this.stackSize = stackSize; + + /*设置线程id*/ + tid = nextThreadID(); + } +``` +3、常用方法 + +1)start(): + 用来启动一个线程,当调用start方法后,系统才会开启一个新的线程来执行用户定义的子任务,在这个过程中,会为相应的线程分配需要的资源 + +```java +/** + * 线程启动方法 + * 此方法的调用会引起当前线程的执行;JVM会调用此线程的run()方法, + */ +public synchronized void start() { + + /** + * 0状态值代表new状态 + */ + if (threadStatus != 0) + // 不是新创建的状态抛出异常 + throw new IllegalThreadStateException(); + + /** + * 向线程组里添加此线程,注意:线程创建的时候只是将线程组中的容器容量增加1 + */ + group.add(this); + + boolean started = false; + try { + // 使线程进入可运行状态(runnable) + start0(); + started = true; + } finally { + try { + if (!started) { + // 启动失败,再线程组中删除改线程 + group.threadStartFailed(this); + } + } catch (Throwable ignore) { + /* do nothing. If start0 threw a Throwable then + it will be passed up(放弃) the call stack */ + } + } + } + +``` + +2)run(): + 不需要用户来调用的,当通过start方法启动一个线程之后,当线程获得了CPU执行时间,便进入run方法体去执行具体的任务。注意,继承Thread类必须重写run方法,在run方法中定义具体要执行的任务。 + +```java + /** + *线程获得CPU执行权限后执行的方法,实际上是调用的Runnable中的run() + */ + @Override + public void run() { + if (target != null) { + target.run(); + } + } +``` + +3)exit(): + 此方法由系统调用,用于在一个线程退出前做一些扫尾工作。 + +```java + +private void exit() { + if (group != null) { + //加锁执行,删除该线程 + group.threadTerminated(this); + group = null; + } + /* GC回收,全部置为null: see bug 4006245 */ + target = null; + /* 加快释放资源*/ + threadLocals = null; + inheritableThreadLocals = null; + inheritedAccessControlContext = null; + blocker = null; + uncaughtExceptionHandler = null; + } + +``` + +4)interrupt(): + 中断;单独调用interrupt方法可以使得处于阻塞状态的线程抛出一个异常,也就说,它可以用来中断一个正处于阻塞状态的线程;另外,通过interrupt方法和isInterrupted()方法来停止正在运行的线程。 + ( 中断当前线程, 一般来说sleep(),join(),wait()在检查到线程的中断状态的时候,会抛出中断异常InteruptedExeption,以及清除线程的中断状态 ) + +```java + public void interrupt() { + if (this != Thread.currentThread()) + checkAccess(); + // 设置阻塞锁 + synchronized (blockerLock) { + Interruptible b = blocker; + if (b != null) { + interrupt0(); // Just to set the interrupt flag + b.interrupt(this); + return; + } + } + interrupt0(); + } +``` + +5)interrupted() + 中断当前线程是否是中断状态,线程的中断状态会被此方法清除, + 也就是如果此方法两次调用都成功,那么在第二次调用的返回结果为false,除非在第一次调用完后和第二次调用前,当前线程被再次中断, +```java + public static boolean interrupted() { + return currentThread().isInterrupted(true); + } + +``` +6)join() /join(long millis)/join(long millis, int nanos) + +```java + //等待线程死亡 + public final void join() throws InterruptedException { + join(0); + } + + //最多等待参数millis时长,当前线程就会死亡,参数为0时则需要继续等待 + public final synchronized void join(long millis)throws InterruptedException { + long base = System.currentTimeMillis(); + long now = 0; + + if (millis < 0) { //等待时长小于0,不合法,抛出异常 + throw new IllegalArgumentException("timeout value is negative"); + } + + if (millis == 0) { //等待时长为0,继续等 + while (isAlive()) { + wait(0); + } + } else { + while (isAlive()) { + long delay = millis - now; + if (delay <= 0) { + break; + } + wait(delay); + now = System.currentTimeMillis() - base; + } + } + } + + //最多等待参数millis毫秒加nanos纳秒时长,当前线程就会死亡,参数为0时则需要继续等待 + public final synchronized void join(long millis, int nanos)throws InterruptedException { + if (millis < 0) { + throw new IllegalArgumentException("timeout value is negative"); + } + + if (nanos < 0 || nanos > 999999) { + throw new IllegalArgumentException( + "nanosecond timeout value out of range"); + } + + if (nanos >= 500000 || (nanos != 0 && millis == 0)) { + millis++; + } + + join(millis); + } + +``` + +7) dumpStack() + +```java + //将当前线程的堆栈跟踪打印到标准错误流,此方法只用于debug + public static void dumpStack() { + new Exception("Stack trace").printStackTrace(); + } + +``` + +4、总结 + +5、阅读参考链接: + + https://blog.csdn.net/lxx19941220/article/details/92587043 + https://www.cnblogs.com/albertrui/p/8391447.html + + + + + + + + + + + + diff --git "a/week_06/09/1188\346\234\211\351\231\220\351\230\273\345\241\236\351\230\237\345\210\227.md" "b/week_06/09/1188\346\234\211\351\231\220\351\230\273\345\241\236\351\230\237\345\210\227.md" new file mode 100644 index 0000000000000000000000000000000000000000..2135317383f57efb1f9c3cd66fbee1ae05adb5d9 --- /dev/null +++ "b/week_06/09/1188\346\234\211\351\231\220\351\230\273\345\241\236\351\230\237\345\210\227.md" @@ -0,0 +1,109 @@ + +设计有限阻塞队列 + +```java + +package com.actec.dsc.websocket; + +import java.util.LinkedList; +import java.util.concurrent.locks.Condition; +import java.util.concurrent.locks.ReentrantLock; + +/** + * 设计有限阻塞队列 + * @Author: tiantao + * @Date: 2020-02-01 14:58 + */ +public class BoundedBlockingQueue { + + private LinkedList queue=new LinkedList<>(); + + private ReentrantLock lock=new ReentrantLock(); + private Condition empty=lock.newCondition(); + private Condition full=lock.newCondition(); + //目前队列中多少元素 + private Integer size=0; + //队列大小 + private Integer cap=null; + + + //构造函数初始化有界队列大小 + public BoundedBlockingQueue(int capacity) { + if (cap == null) { + lock.lock(); + try { + if (cap == null) { + cap = capacity; + } + } finally { + lock.unlock(); + } + } + } + + //入队方法 + public void enqueue(int element) throws InterruptedException{ + lock.lock(); + try{ + //如果队列已满,阻塞元素入队 + while(size>=cap){ + full.await(); + } + queue.offerFirst(element); + size+=1; + //唤醒empty + empty.signalAll(); + } catch (InterruptedException e) { + e.printStackTrace(); + } finally { + lock.unlock(); + } + } + + //出队方法 + public int dequeue() throws InterruptedException { + lock.lock(); + int res=-1; + try { + //队列空,阻塞元素出队 + while(size==0){ + empty.await(); + } + res=queue.pollLast(); + size-=1; + //唤醒full + full.signalAll(); + } finally { + lock.unlock(); + } + return res; + } + + public int size() { + return size; + } + + @Override + public String toString() { + return "BoundedBlockingQueue{" + + "queue=" + queue + + '}'; + } + + public static void main(String[] args) throws InterruptedException { + BoundedBlockingQueue boundedBlockingQueue = new BoundedBlockingQueue(2); + boundedBlockingQueue.enqueue(1); + int dequeue1 = boundedBlockingQueue.dequeue(); //dequeue1 返回为1 +// boundedBlockingQueue.dequeue(); //dequeue为空被阻塞 +// boundedBlockingQueue.enqueue(0); + boundedBlockingQueue.enqueue(2); + boundedBlockingQueue.enqueue(3); +// boundedBlockingQueue.enqueue(4); //达到队列长度2被阻塞 +// boundedBlockingQueue.dequeue(); + int size = boundedBlockingQueue.size(); + + } +} + + +``` \ No newline at end of file diff --git "a/week_06/09/707.\350\256\276\350\256\241\351\223\276\350\241\250.md" "b/week_06/09/707.\350\256\276\350\256\241\351\223\276\350\241\250.md" new file mode 100644 index 0000000000000000000000000000000000000000..6c9664d49be110cbf90743a2713818a8127be605 --- /dev/null +++ "b/week_06/09/707.\350\256\276\350\256\241\351\223\276\350\241\250.md" @@ -0,0 +1,165 @@ + +实现单链表 + + +```java +package com.actec.dsc.websocket; + +/** + * + * 单链表(删除有点问题) + * @Author: tiantao + * @Date: 2020-02-01 10:49 + */ +public class MyLinkedList { + + + private int size; + private ListNode first; + + /** Initialize your data structure here. */ + public MyLinkedList() { + size = 0; + first = new ListNode(0); + } + +// /** Get the value of the index-th node in the linked list. If the index is invalid, return -1. */ +// public int get(int index) { +// ListNode node = node(index); +// if (node == null) return -1; +// return node.val; +// } + + + public int get(int index) { + // if index is invalid + if (index < 0 || index >= size) return -1; + + ListNode curr = first; + // index steps needed + // to move from sentinel node to wanted index + for(int i = 0; i < index + 1; ++i) curr = curr.next; + return curr.val; + } + + + /** Add a node of value val before the first element of the linked list. After the insertion, the new node will be the first node of the linked list. */ + public void addAtHead(int val) { + addAtIndex(0, val); + } + + /** Append a node of value val to the last element of the linked list. */ + public void addAtTail(int val) { + addAtIndex(size, val); + } + + /** Add a node of value val before the index-th node in the linked list. If index equals to the length of linked list, the node will be appended to the end of linked list. If index is greater than the length, the node will not be inserted. */ + public void addAtIndex(int index, int val) { +// // 如果index为负数,插入到头部?! +// // if (index == 0) { +// if (index <= 0) { +// first = new ListNode(val, first); +// } else { +// ListNode prev = node(index - 1); +// if (prev == null) return; +// prev.next = new ListNode(val, prev.next); +// } +// size++; + + // If index is greater than the length, + // the node will not be inserted. + if (index > size) return; + + // [so weird] If index is negative, + // the node will be inserted at the head of the list. + if (index < 0) index = 0; + + ++size; + // find predecessor of the node to be added + ListNode pred = first; + for(int i = 0; i < index; ++i) pred = pred.next; + + // node to be added + ListNode toAdd = new ListNode(val); + // insertion itself + toAdd.next = pred.next; + pred.next = toAdd; + } + + /** Delete the index-th node in the linked list, if the index is valid. */ +// public void deleteAtIndex(int index) { +// if (rangeCheck(index) == false) return; +// if (index == 0) { +// first = first.next; +// } else { +// ListNode prev = node(index - 1); +// if (prev == null) return; +// System.out.println("7777:"+prev.val+","+prev.next+","+prev.next.next); +// prev.next = prev.next.next; +// } +// size--; +// } + + public void deleteAtIndex(int index) { + // if the index is invalid, do nothing + if (index < 0 || index >= size) return; + + size--; + // find predecessor of the node to be deleted + ListNode pred = first; + for(int i = 0; i < index; ++i) pred = pred.next; + + // delete pred.next + pred.next = pred.next.next; + } + + + /** 根据索引查找对应节点 */ + private ListNode node(int index) { + if (rangeCheck(index) == false) return null; + ListNode node = first; + for (int i = 0; i < index; i++) { + node = node.next; + } + return node; + } + + private boolean rangeCheck(int index) { + return !(index < 0 || index > (size - 1)); + } + + + + public class ListNode{ + int val; //当前节点的值 + ListNode next; //指向下一个节点的指针/引用 + ListNode(int val) { + this.val = val; + } + @Override + public String toString() { + return "ListNode{" + + "val=" + val + + ", next=" + next + + '}'; + } + } + + public static void main(String[] args) { + MyLinkedList myLinkedList = new MyLinkedList(); + myLinkedList.addAtHead(1); + myLinkedList.addAtHead(3); + myLinkedList.addAtIndex(1,2); ////链表变为1-> 2-> 3 + int get1 = myLinkedList.get(1); //返回2 + System.out.println("get1 " +get1); + myLinkedList.deleteAtIndex(1); //现在链表是1-> 3 + int get2 = myLinkedList.get(1); //返回1 + System.out.println("get2 " + get2); + + } + +} + + + +``` \ No newline at end of file diff --git "a/week_06/09/Leetcode706\345\223\210\345\270\214\346\230\240\345\260\204.md" "b/week_06/09/Leetcode706\345\223\210\345\270\214\346\230\240\345\260\204.md" new file mode 100644 index 0000000000000000000000000000000000000000..9f5983b93313644cd222e953d75044e59e47a57e --- /dev/null +++ "b/week_06/09/Leetcode706\345\223\210\345\270\214\346\230\240\345\260\204.md" @@ -0,0 +1,97 @@ +LeetCode设计一个哈希映射 + +```java +package com.actec.dsc.websocket; + +/** + * @Author: tiantao + * @Date: 2020-01-14 14:59 + */ +public class MyHashMap { + int k=10; + Node[] hashArray; + /** Initialize your data structure here. */ + public MyHashMap() { + hashArray=new Node[k]; + } + + /** value will always be non-negative. */ + public void put(int key, int value) { + Node node=hashArray[key%k]; + Node pre=node; + if(node==null) + { hashArray[key%k]=new Node(key,value); + return; + } + while(node!=null){ + if(node.k==key){ + node.v=value; + return; + } + pre=node; + node=node.next; + } + pre.next=new Node(key,value); + + } + + + /** Returns the value to which the specified key is mapped, or -1 if this map contains no mapping for the key */ + public int get(int key) { + Node node = hashArray[key % k]; + while (node != null) { + if (node.k == key) { + return node.v; + } + node = node.next; + } + return -1; + } + + + /** Removes the mapping of the specified value key if this map contains a mapping for the key */ + public void remove(int key) { + Node node=hashArray[key%k]; + Node pre=node; + while(node!=null){ + if(node.k==key){ + if(pre.k==node.k){ + hashArray[key%k]=node.next; + }else{ + pre.next=node.next; + } + } + pre=node; + node=node.next; + } + } + + public static void main(String[] args) { + MyHashMap myHashMap = new MyHashMap(); + myHashMap.put(1,10); + myHashMap.get(1); + System.out.println("myHashMap key1 value " +myHashMap.get(1)); + //myHashMap key1 value 10 + System.out.println("myHashMap key2 value " +myHashMap.get(2)); + //myHashMap key2 value -1 + myHashMap.remove(1); + System.out.println("myHashMap key1 value " +myHashMap.get(1)); + //myHashMap key1 value -1 + } + +} + +class Node{ + int k; + int v; + + Node next; + + public Node(int k,int v){ + this.k = k; + this.v = v; + next = null; + } + +} +``` \ No newline at end of file diff --git a/week_06/09/MyCircularDeque.md b/week_06/09/MyCircularDeque.md new file mode 100644 index 0000000000000000000000000000000000000000..9ef8c7d54c04f755671f14337bfe75a651f26196 --- /dev/null +++ b/week_06/09/MyCircularDeque.md @@ -0,0 +1,110 @@ +设计实现双端队列 + + +```java +package com.actec.dsc.websocket; + +/** + * + * 设计实现双端队列 + * @Author: tiantao + * @Date: 2020-01-31 20:38 + */ +public class MyCircularDeque { + + + //MyCircularDeque(k):构造函数,双端队列的大小为k。 + //insertFront():将一个元素添加到双端队列头部。 如果操作成功返回 true。 + //insertLast():将一个元素添加到双端队列尾部。如果操作成功返回 true。 + //deleteFront():从双端队列头部删除一个元素。 如果操作成功返回 true。 + //deleteLast():从双端队列尾部删除一个元素。如果操作成功返回 true。 + //getFront():从双端队列头部获得一个元素。如果双端队列为空,返回 -1。 + //getRear():获得双端队列的最后一个元素。 如果双端队列为空,返回 -1。 + //isEmpty():检查双端队列是否为空。 + //isFull():检查双端队列是否满了。 + + + private int[] elements; + private int size; + + public MyCircularDeque(int k) { + elements = new int[k]; + } + + public boolean insertFront(int value) { + if(isFull()){ + return false; + } + for(int i=size-1; i>=0; i--){ + elements[i+1] = elements[i]; + } + elements[0] = value; + size++; + return true; + } + + public boolean insertLast(int value) { + if(isFull()){ + return false; + } + elements[size] = value; + size++; + return true; + } + + public boolean deleteFront() { + if(isEmpty()){ + return false; + } + elements[0] = 0; + for(int i=0; i[] getConstructors() throws SecurityException { + checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true); + return copyConstructors(privateGetDeclaredConstructors(true)); +} + +//获取类中所有的构造方法(public、protected、default、private) +public Constructor[] getDeclaredConstructors() throws SecurityException { + checkMemberAccess(Member.DECLARED, Reflection.getCallerClass(), true); + return copyConstructors(privateGetDeclaredConstructors(false)); +} + +``` + +4、获取类中的方法 + +```java + +//获得类的public类型的方法 +@CallerSensitive +public Method[] getMethods() throws SecurityException { + checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true); + return copyMethods(privateGetPublicMethods()); +} + + +//获取类中所有的方法(public、protected、default、private) +@CallerSensitive +public Method[] getDeclaredMethods() throws SecurityException { + checkMemberAccess(Member.DECLARED, Reflection.getCallerClass(), true); + return copyMethods(privateGetDeclaredMethods(false)); +} + +``` + + +5、获取Class对象的三种方法 + +```java + +1、调用Class类的静态方法forName, 比如: Class.forName("java.lang.String") +2、使用类的.class语法, 比如:Class cls = String.class +3、调用对象的getClass方法, 比如:String str = "abc"; Class cls = str.getClass(); + +``` + + +6、测试 + +```java + +package com.actec.dsc.websocket; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Method; + +/** + * @Author: tiantao + * @Date: 2020-01-18 15:32 + */ +public class Person { + + private String name; // 姓名 + public int sex; // 性别, 为了测试新建一个public的属性 + + // 一个私有的构造方法 + private Person() { + } + // 一个公共的构造方法 + public Person(String name, int sex) { + this.name = name; + this.sex = sex; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public int getSex() { + return sex; + } + + public void setSex(int sex) { + } + + + public static void main(String[] args) throws ClassNotFoundException { + Class clazz = Class.forName("com.actec.dsc.websocket.Person"); + Constructor[] constructors = clazz.getConstructors(); + System.out.println("1. 获取公共的构造方法数量为: " + constructors.length); + Constructor[] allConstructors = clazz.getDeclaredConstructors(); + System.out.println("2. 获取所有的构造方法数量为: " + allConstructors.length); + + System.out.println("该类的所有公共字段属性列表如下:"); + Field[] fields = clazz.getFields(); + for (Field field : fields) { + System.out.println(field.getType().toString() + " " + field.getName()); + } + + System.out.println("该类的所有字段属性列表如下:"); + Field[] allFields = clazz.getDeclaredFields(); + for (Field field : allFields) { + System.out.println(field.getType().toString() + " " + field.getName()); + } + + System.out.println("----获取该类中所有的方法, 列表如下---"); + Method[] methods = clazz.getMethods(); + for (Method m : methods) { + StringBuilder sb = new StringBuilder(); + sb.append(m.getName()).append("("); + Class[] params = m.getParameterTypes(); + for (int i = 0; i < params.length; i++) { + sb.append(params[i].getName()); + } + sb.append(")"); + System.out.println(sb.toString()); + + } + System.out.println("----获取该类中所有的方法, end---"); + } + + +} + + +``` + + + + + + + + + + diff --git "a/week_06/09/\350\256\276\350\256\241\345\223\210\345\270\214\351\233\206\345\220\210.md" "b/week_06/09/\350\256\276\350\256\241\345\223\210\345\270\214\351\233\206\345\220\210.md" new file mode 100644 index 0000000000000000000000000000000000000000..e9196f1a00323e581d2ef8ed1f78c802b191cf3b --- /dev/null +++ "b/week_06/09/\350\256\276\350\256\241\345\223\210\345\270\214\351\233\206\345\220\210.md" @@ -0,0 +1,72 @@ +设计哈希集合 + + +```java +package com.actec.dsc.websocket; + +/** + * 设计一个哈希集合 + * @Author: tiantao + * @Date: 2020-01-16 14:23 + */ +public class MyHashSet { + + /** + * + * 示例: + * + * MyHashSet hashSet = new MyHashSet(); + * hashSet.add(1);         + * hashSet.add(2);         + * hashSet.contains(1);    // 返回 true + * hashSet.contains(3);    // 返回 false (未找到) + * hashSet.add(2);           + * hashSet.contains(2);    // 返回 true + * hashSet.remove(2);           + * hashSet.contains(2);    // 返回 false (已经被删除) + * + * + * 所有的值都在 [0, 1000000]的范围内。 + * 操作的总数目在[1, 10000]范围内。 + * 不要使用内建的哈希集合库 + */ + + + boolean[] map = new boolean[1000000]; + + public MyHashSet() { + + } + + public void add(int key){ + map[key] = true; + } + + + public boolean contains(int key){ + return map[key] == true; + } + + + public void remove(int key){ + map[key] = false; + } + + + public static void main(String[] args) { + MyHashSet hashSet = new MyHashSet(); + hashSet.add(1); + System.out.println(hashSet.contains(1)); //true + boolean contains = hashSet.contains(2); + System.out.println(contains); //false + hashSet.add(2); + hashSet.remove(2); + boolean removeFlag = hashSet.contains(2); + System.out.println(removeFlag); //true + } + + +} + + +``` \ No newline at end of file diff --git "a/week_06/09/\350\256\276\350\256\241\345\276\252\347\216\257\351\230\237\345\210\227622.md" "b/week_06/09/\350\256\276\350\256\241\345\276\252\347\216\257\351\230\237\345\210\227622.md" new file mode 100644 index 0000000000000000000000000000000000000000..1b75efd2cb0f5db7d3f7be2de74973994933109c --- /dev/null +++ "b/week_06/09/\350\256\276\350\256\241\345\276\252\347\216\257\351\230\237\345\210\227622.md" @@ -0,0 +1,130 @@ +设计循环队列 + +```java + +package com.actec.dsc.websocket; + +/** + * + * 设计循环队列 + * @Author: tiantao + * @Date: 2020-01-19 09:41 + */ +public class MyCircularQueueTest { + + private Integer []arr; + private int head; + private int tail; + + //MyCircularQueueTest(k): 构造器,设置队列长度为 k 。 + //Front: 从队首获取元素。如果队列为空,返回 -1 。 + //Rear: 获取队尾元素。如果队列为空,返回 -1 。 + //enQueue(value): 向循环队列插入一个元素。如果成功插入则返回真。 + //deQueue(): 从循环队列中删除一个元素。如果成功删除则返回真。 + //isEmpty(): 检查循环队列是否为空。 + //isFull(): 检查循环队列是否已满 + + + public MyCircularQueueTest(int k) { + arr=new Integer[k]; + head=0; + tail=0; + } + + + public boolean enQueue(int value) { + if(isFull()) { + return false; + }else { + arr[tail]=value; + tail=(tail+1)%(arr.length); + return true; + } + + } + + + public boolean deQueue() { + if(isEmpty()) { + return false; + }else { + arr[head]=null; + head=(head+1)%(arr.length); + return true; + } + } + + public int Front() { + if(isEmpty()) { + return -1; + }else { + return arr[head]; + } + } + + + public int Rear() { + if(isEmpty()) { + return -1; + }else { + if(tail!=0)return arr[tail-1]; + else return arr[arr.length-1]; + } + } + + public boolean isEmpty() { + if(head==tail&&arr[head]==null) { + return true; + }else { + return false; + } + } + + + public boolean isFull() { + if(head==tail&&arr[head]!=null) { + return true; + }else { + return false; + } + } + + public static void main(String[] args) { + + MyCircularQueueTest circularQueue = new MyCircularQueueTest(3); // 设置长度为 3 + + circularQueue.enQueue(1); + // 返回 true + + circularQueue.enQueue(2); + // 返回 true + + circularQueue.enQueue(3); + // 返回 true + + circularQueue.enQueue(4); + // 返回 false,队列已满 + + circularQueue.Rear(); + // 返回 3 + + circularQueue.isFull(); + // 返回 true + + circularQueue.deQueue(); + // 返回 true + + circularQueue.enQueue(4); + // 返回 true + + circularQueue.Rear(); + // 返回 4 + + + } + +} + + + +``` \ No newline at end of file