【高并发系列】18、线程池那些事儿2 - 内部实现

python Threads and ThreadPoolExecutor and async python 进程和进程池 阅读详情

线程池内部实现

1、常用线程池定义

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

2、ThreadPoolExecutor构造方法详解

由以上代码可以看到,常用的几个方法虽然有着完全不同的功能特点,但内部实现都是ThreadPoolExecutor类的封装,下面是ThreadPoolExecutor类的构造方法:

public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory,
                          RejectedExecutionHandler handler)

其中:

corePoolSize

  • the number of threads to keep in the pool, even if they are idle, unless allowCoreThreadTimeOut is set
  • 线程池中线程数量

maximumPoolSize

  • the maximum number of threads to allow in the pool
  • 线程池中最大线程数量

keepAliveTime

  • when the number of threads is greater than the core, this is the maximum time that excess idle threads will wait for new tasks before terminating.
  • 当线程池数量超过corePoolSize时,多余的空闲线程的存活时间,即超过corePoolSize的空闲线程在多长时间内被销毁;

unit

  • the time unit for the keepAliveTime argument
  • keepAliveTime的单位

workQueue

  • the queue to use for holding tasks before they are executed.  This queue will hold only the Runnable tasks submitted by the execute method.
  • 任务队列,被提交但尚未被执行的任务存放到该队列;

threadFactory

  • the factory to use when the executor creates a new thread
  • 线程工厂,用于创建线程,一般使用默认值;

handler

  • the handler to use when execution is blocked because the thread bounds and queue capacities are reached
  • 拒绝策略;当任务太多来不及处理时如何拒绝任务;

2.1 workQueue

指被提交但未执行的任务队列;是java.util.concurrent.BlockingQueue接口对象,仅用于存放Runnable对象;分类如下:

  • 直接提交的队列 - SynchronousQueue

SynchronousQueue没有容量,每一个插入操作都要等待一个相应的删除操作,反之,每一个删除操作都要等待对应的插入操作;

提交的任务总是被提交给线程执行,如果没有空闲的线程则尝试新建线程,如果已经达到线程最大值则执行拒绝策略;

使用SynchronousQueue队列,通常设置很大的maximumPoolSize值,否则很容易执行拒绝策略;

如newCachedThreadPool()方法中:

public static ExecutorService newCachedThreadPool() {
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                  60L, TimeUnit.SECONDS,
                                  new SynchronousQueue<Runnable>());
}
  • 有界的任务队列 - ArrayBlockingQueue

构造方法中需传入队列最大容量值

public ArrayBlockingQueue(int capacity)

当有新任务需要执行时:

  1. )线程池实际线程数小于corePoolSize,则优先创建新线程;
  2. )线程池实际线程数大于corePoolSize,若等待队列未满可以加入,则将新任务加入等待队列;
  3. )若等待队列已满无法加入,并且总线程数不大于maximumPoolSize时,创建新线程执行任务;
  4. )若等待队列已满无法加入,并且总线程数大于maximumPoolSize时,执行拒绝策略;
  • 无界的任务队列 - LinkedBlockingQueue

与有界队列相比,除非系统资源耗尽,否则无界任务队列不存在任务入队失败的情况;

当有新任务需要执行时:

  1. )线程池实际线程数小于corePoolSize,则优先创建新线程;
  2. )线程池实际线程数大于corePoolSize,则将新任务加入等待队列,并且线程数不会增加了;

如newFixedThreadPool()方法中:

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

 如newSingleThreadExecutor()方法中:

public static ExecutorService newSingleThreadExecutor() {
    return new FinalizableDelegatedExecutorService
        (new ThreadPoolExecutor(1, 1,
                                0L, TimeUnit.MILLISECONDS,
                                new LinkedBlockingQueue<Runnable>()));
}
  • 优先任务队列 - PriorityBlockingQueue

可以根据任务自身的优先级顺序控制执行先后顺序;

attention:使用自定义线程池时,要根据具体情况选择合适的并发队列作为任务的缓冲;

3、ThreadPoolExecutor.execute()方法

/**
 * Executes the given task sometime in the future.  The task
 * may execute in a new thread or in an existing pooled thread.
 *
 * If the task cannot be submitted for execution, either because this
 * executor has been shutdown or because its capacity has been reached,
 * the task is handled by the current {@code RejectedExecutionHandler}.
 *
 * @param command the task to execute
 * @throws RejectedExecutionException at discretion of
 *         {@code RejectedExecutionHandler}, if the task
 *         cannot be accepted for execution
 * @throws NullPointerException if {@code command} is null
 */
public void execute(Runnable command) {
    if (command == null)
        throw new NullPointerException();
    /*
     * Proceed in 3 steps:
     *
     * 1. If fewer than corePoolSize threads are running, try to
     * start a new thread with the given command as its first
     * task.  The call to addWorker atomically checks runState and
     * workerCount, and so prevents false alarms that would add
     * threads when it shouldn't, by returning false.
     *
     * 2. If a task can be successfully queued, then we still need
     * to double-check whether we should have added a thread
     * (because existing ones died since last checking) or that
     * the pool shut down since entry into this method. So we
     * recheck state and if necessary roll back the enqueuing if
     * stopped, or start a new thread if there are none.
     *
     * 3. If we cannot queue task, then we try to add a new
     * thread.  If it fails, we know we are shut down or saturated
     * and so reject the task.
     */
    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);
}

先判断当前线程数是否小于corePoolSize核心线程数(workerCountOf(c) < corePoolSize);

如果小于,则通过addWorker(command, true)直接调度执行;

如果不小于,加入等待队列(workQueue.offer(command));

如果加入等待队列失败(如有界队列到达上限,或者使用了SynchronousQueue类等),则将任务直接提交给线程池执行else if (!addWorker(command, false));

如果提交失败,执行拒绝策略reject(command);

4、拒绝策略

是系统超负荷运行时的补救措施;

JDK内置拒绝策略,都实现了java.util.concurrent.RejectedExecutionHandler接口:

  • AbortPolicy:直接抛出异常,阻止系统正常工作;
  • CallerRunsPolicy:只要线程池未关闭,该策略会直接在调用者线程中,运行当前被丢弃的任务;

  • DiscardOldestPolicy:丢弃未执行任务中最老的任务,就是即将被执行的一个任务,并尝试再次提交当前任务;

  • DiscardPolicy:默默丢弃无法处理的任务,不做任何处理;

如果内置策略无法满足实际应用的需求,可以自己扩展RejectedExecutionHandler接口:

public interface RejectedExecutionHandler {
    /**
     * Method that may be invoked by a {@link ThreadPoolExecutor} when
     * {@link ThreadPoolExecutor#execute execute} cannot accept a
     * task.  This may occur when no more threads or queue slots are
     * available because their bounds would be exceeded, or upon
     * shutdown of the Executor.
     *
     * <p>In the absence of other alternatives, the method may throw
     * an unchecked {@link RejectedExecutionException}, which will be
     * propagated to the caller of {@code execute}.
     *
     * @param r the runnable task requested to be executed
     * @param executor the executor attempting to execute this task
     * @throws RejectedExecutionException if there is no remedy
     */
    void rejectedExecution(Runnable r, ThreadPoolExecutor executor);
}

示例:核心线程、最大线程数都是5,有界任务队列容量为10,所以最多可以容纳15个线程,多余的走拒绝策略;

public class RejectExecutionHandlerDemo {
	public static class TaskDemo implements Runnable {
		@Override
		public void run() {
			System.out.println(System.currentTimeMillis() + " : " + Thread.currentThread().getName());
			try {
				TimeUnit.MILLISECONDS.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
	public static void main(String[] args) throws InterruptedException {
		TaskDemo task = new TaskDemo();
		ExecutorService es = new ThreadPoolExecutor(5, 5, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>(10),
				new RejectedExecutionHandler() {
					@Override
					public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
						System.out.println(r.toString() + " is discard.");
					}
				});
		for (int i = 0; i < 16; i++) {
			es.submit(task);
			TimeUnit.MILLISECONDS.sleep(10);
		}
		es.shutdown();
	}
}

console

1550415828931 : pool-1-thread-1
1550415828943 : pool-1-thread-2
1550415828954 : pool-1-thread-3
1550415828964 : pool-1-thread-4
1550415828975 : pool-1-thread-5
java.util.concurrent.FutureTask@7d4991ad is discard.
1550415829933 : pool-1-thread-1
1550415829943 : pool-1-thread-2
1550415829955 : pool-1-thread-3
1550415829969 : pool-1-thread-4
1550415829978 : pool-1-thread-5
1550415830936 : pool-1-thread-1
1550415830944 : pool-1-thread-2
1550415830959 : pool-1-thread-3
1550415830974 : pool-1-thread-4
1550415830979 : pool-1-thread-5

5、自定义线程工厂 ThreadFactory

java.util.concurrent.ThreadFactory接口用于创建线程;

public interface ThreadFactory {
    /**
     * Constructs a new {@code Thread}.  Implementations may also initialize
     * priority, name, daemon status, {@code ThreadGroup}, etc.
     *
     * @param r a runnable to be executed by new thread instance
     * @return constructed thread, or {@code null} if the request to
     *         create a thread is rejected
     */
    Thread newThread(Runnable r);
}

示例:自定义线程工厂,设置所有线程为守护线程,当main线程退出后,强制注销该线程池;

public class ThreadFactoryDemo {
	public static class TaskDemo implements Runnable {
		@Override
		public void run() {
			System.out.println(System.currentTimeMillis() + " : " + Thread.currentThread().getName());
			try {
				TimeUnit.MILLISECONDS.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
	public static void main(String[] args) {
		TaskDemo task = new TaskDemo();
		ExecutorService es = new ThreadPoolExecutor(5, 5, 0L, TimeUnit.MILLISECONDS, new SynchronousQueue<Runnable>(),
				new ThreadFactory() {
					@Override
					public Thread newThread(Runnable r) {
						Thread t = new Thread(r);
						t.setDaemon(true);
						System.out.println("create " + t);
						return t;
					}
				});
		for (int i = 0; i < 5; i++) {
			es.submit(task);
		}
	}
}

console

create Thread[Thread-0,5,main]
create Thread[Thread-1,5,main]
create Thread[Thread-2,5,main]
1550416438149 : Thread-1
1550416438149 : Thread-0
create Thread[Thread-3,5,main]
1550416438150 : Thread-2
create Thread[Thread-4,5,main]
1550416438150 : Thread-3
1550416438150 : Thread-4

6、线程池扩展

可以覆写ThreadPoolExecutor中的以下三个方法,来对线程池进行控制:

protected void beforeExecute(Thread t, Runnable r) { }
protected void afterExecute(Runnable r, Throwable t) { }
protected void terminated() { }
public class ExtThreadPool {
	public static class TaskDemo implements Runnable {
		private String name;

		public TaskDemo(String name) {
			this.name = name;
		}
		@Override
		public void run() {
			System.out.println(Thread.currentThread().getName() + " - " + this.name + " - is running.");
			try {
				TimeUnit.MILLISECONDS.sleep(100);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
	public static void main(String[] args) throws InterruptedException {
		ExecutorService es = new ThreadPoolExecutor(5, 5, 0L, TimeUnit.MILLISECONDS,
				new LinkedBlockingQueue<Runnable>()) {
			@Override
			protected void beforeExecute(Thread t, Runnable r) {
				System.out.println(t.getName() + " - " + ((TaskDemo) r).name + " - beforeExecute");
			}
			@Override
			protected void afterExecute(Runnable r, Throwable t) {
				System.out.println(Thread.currentThread().getName() + " - " + ((TaskDemo) r).name + " - afterExecute");
			}
			@Override
			protected void terminated() {
				System.out.println("ThreadPool is terminated.");
			}
		};
		for (int i = 0; i < 5; i++) {
			TaskDemo task = new TaskDemo("TASK-" + i);
			es.execute(task);
			TimeUnit.MILLISECONDS.sleep(10);
		}
		es.shutdown();
	}
}

console

pool-1-thread-1 - TASK-0 - beforeExecute
pool-1-thread-1 - TASK-0 - is running.
pool-1-thread-2 - TASK-1 - beforeExecute
pool-1-thread-2 - TASK-1 - is running.
pool-1-thread-3 - TASK-2 - beforeExecute
pool-1-thread-3 - TASK-2 - is running.
pool-1-thread-4 - TASK-3 - beforeExecute
pool-1-thread-4 - TASK-3 - is running.
pool-1-thread-5 - TASK-4 - beforeExecute
pool-1-thread-5 - TASK-4 - is running.
pool-1-thread-1 - TASK-0 - afterExecute
pool-1-thread-2 - TASK-1 - afterExecute
pool-1-thread-3 - TASK-2 - afterExecute
pool-1-thread-4 - TASK-3 - afterExecute
pool-1-thread-5 - TASK-4 - afterExecute
ThreadPool is terminated.

7、优化线程池线程数量

Ncpu = CPU数量
Ucpu = 目标CPU使用率, 0<=Ucpu<=1
W/C = 等待时间与计算时间的比率

为保持处理器达到期望的使用率,最优的线程池大小为

Nthreads = Ncpu * Ucpu * (1 + W/C)

在Java中,通过如下代码获取可用CPU数量:

Runtime.getRuntime().availableProcessors()

8、打印线程池中的错误堆栈信息

public class TraceErrorDemo {
	public static class DivTask implements Runnable {
		private int a, b;
		public DivTask(int a, int b) {
			this.a = a;
			this.b = b;
		}
		@Override
		public void run() {
			double result = a / b;
			System.out.println(result);
		}

	}
	public static void main(String[] args) throws InterruptedException, ExecutionException {
		ThreadPoolExecutor pools = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 0L, TimeUnit.MILLISECONDS,
				new SynchronousQueue<>());
		for (int i = 0; i < 5; i++) {
			pools.submit(new DivTask(100, i));
		}
	}
}

console

100.0
25.0
33.0
50.0

只有4个输出,把i=0的情况漏掉了,0不能作为除数,但没有任何日志,没有错误提示,好像程序正常执行了一样;

找回部分异常信息:

1、放弃submit()方法,改用execute()方法;

pools.execute(new DivTask(100, i));

2、使用Future接收submit()方法返回值;

Future result = pools.submit(new DivTask(100, i));
result.get();

得到部分异常堆栈信息如下:

Exception in thread "pool-1-thread-1" java.lang.ArithmeticException: / by zero
	at com.threadpool.TraceErrorDemo$DivTask.run(TraceErrorDemo.java:18)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
	at java.lang.Thread.run(Thread.java:748)
100.0
25.0
33.0
50.0

但是还是不知道任务是在哪里提交的,提交位置已经被线程池淹没了,所以自己动手扩展ThreadPoolExecutor线程池,在调度任务之前先保存一下提交任务线程的堆栈信息:

public class TraceThreadPoolExecutor extends ThreadPoolExecutor {
	public TraceThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit,
			BlockingQueue<Runnable> workQueue) {
		super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
	}
	@Override
	public void execute(Runnable command) {
		super.execute(wrap(command, clientTrace(), Thread.currentThread().getName()));
	}
	@Override
	public Future<?> submit(Runnable command) {
		return super.submit(wrap(command, clientTrace(), Thread.currentThread().getName()));
	}
	private Runnable wrap(final Runnable task, final Exception clientStack, String clientThreadName) {
		return new Runnable() {
			@Override
			public void run() {
				try {
					task.run();
				} catch (Exception e) {
					clientStack.printStackTrace();
					throw e;
				}
			}
		};
	}
	private Exception clientTrace() {
		return new Exception("Client stack trace");
	}
	public static class DivTask implements Runnable {
		private int a, b;
		public DivTask(int a, int b) {
			this.a = a;
			this.b = b;
		}
		@Override
		public void run() {
			double result = a / b;
			System.out.println(result);
		}
	}
	public static void main(String[] args) {
		ThreadPoolExecutor pools = new TraceThreadPoolExecutor(0, Integer.MAX_VALUE, 0L, TimeUnit.MILLISECONDS,
				new SynchronousQueue<>());
		for (int i = 0; i < 5; i++) {
			pools.execute(new DivTask(100, i));
		}
	}
}

console

java.lang.Exception: Client stack trace
	at com.threadpool.TraceThreadPoolExecutor.clientTrace(TraceThreadPoolExecutor.java:36)
	at com.threadpool.TraceThreadPoolExecutor.execute(TraceThreadPoolExecutor.java:16)
	at com.threadpool.TraceThreadPoolExecutor.main(TraceThreadPoolExecutor.java:54)
Exception in thread "pool-1-thread-1" java.lang.ArithmeticException: / by zero
	at com.threadpool.TraceThreadPoolExecutor$DivTask.run(TraceThreadPoolExecutor.java:46)
	at com.threadpool.TraceThreadPoolExecutor$1.run(TraceThreadPoolExecutor.java:27)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
	at java.lang.Thread.run(Thread.java:748)
100.0
25.0
33.0
50.0

9、Fork/Join框架

“分而治之”的思想体现;

ForkJoinPool线程池对fork/join框架支持的接口

public <T> ForkJoinTask<T> submit(ForkJoinTask<T> task)

ForkJoinTask有两个重要的子类:RecursiveAction和RecursiveTask,分别表示没有返回值和可以携带返回值的任务;

public abstract class RecursiveAction extends ForkJoinTask<Void>
public abstract class RecursiveTask<V> extends ForkJoinTask<V>

示例:计算数列求和

public class CountTask extends RecursiveTask<Long> {
	private static final long serialVersionUID = 8000196622093620501L;
	// 任务分解规模
	private static final int THRESHOLD = 10000;
	private long start;
	private long end;
	public CountTask(long start, long end) {
		this.start = start;
		this.end = end;
	}
	@Override
	protected Long compute() {
		long sum = 0;
		boolean canCompute = (end - start) < THRESHOLD;
		if (canCompute) {
			for (long i = start; i <= end; i++) {
				sum += i;
			}
		} else {
			// 任务再次分解
			long step = (start + end) / 100;
			List<CountTask> subTasks = new ArrayList<>();
			long pos = start;
			for (int i = 0; i < 100; i++) {
				long lastOne = pos + step;
				if (lastOne > end) {
					lastOne = end;
				}
				CountTask subTask = new CountTask(pos, lastOne);
				pos += step + 1;
				subTasks.add(subTask);
				// 提交子任务
				subTask.fork();
			}
			// 等待所有子任务结束,并将结果再次求和
			for (CountTask t : subTasks) {
				sum += t.join();
			}
		}
		return sum;
	}
	public static void main(String[] args) {
		ForkJoinPool forkJoinPool = new ForkJoinPool();
		CountTask task = new CountTask(0, 200000L);
		ForkJoinTask<Long> result = forkJoinPool.submit(task);
		try {
			long res = result.get();
			System.out.println(res);
		} catch (InterruptedException e) {
			e.printStackTrace();
		} catch (ExecutionException e) {
			e.printStackTrace();
		}
	}
}

console

20000100000

10、Guava中对线程池的扩展

10.1 DirectExecutor

MoreExecutors中的DirectExecutor,将任务在当前线程中直接执行;

使用统一的编码风格来处理同步和异步调用,简化设计:

public class DirectExecutorDemo {
	public static void main(String[] args) {
		Executor exec = MoreExecutors.directExecutor();
		exec.execute(() -> System.out.println("I am runnint in " + Thread.currentThread().getName() + "."));
	}
}

console

I am runnint in main.

10.2 将普通线程池转为Daemon线程池

使用MoreExecutors中的如下方法:

public static ExecutorService getExitingExecutorService(ThreadPoolExecutor executor)
public class DaemonThreadPoolDemo {
	public static void main(String[] args) {
		ThreadPoolExecutor exec = (ThreadPoolExecutor) Executors.newFixedThreadPool(2);
		// 如果不设置,程序不会退出
		MoreExecutors.getExitingExecutorService(exec);
		exec.execute(() -> System.out.println("I am running in " + Thread.currentThread().getName() + "."));
	}
}

 

java并发编程面试题 文章目录1、Synchronized用过吗,其原理是什么?2、你刚才提到获取对象的锁,这个“锁”到底是什么?如何确定对象的 锁?3、什么是可重入性,为什么说Synchronized是可重入锁?4、JVM对Java的原生锁做了哪些优化?5、为什么说Synchronized是非公平锁?6、什么是锁消除和锁粗化?7、为什么说Synchronized是一个悲观锁?乐观锁的实现原理又是什么?什么是CAS,它有什么特性?8、乐观锁一定就是好的吗?9、跟Synchronized相比,可重入锁ReentrantLock其实 阅读详情

相关推荐

详解Java8与Runtime.getRuntime().availableProcessors()

主要介绍了详解Java8与Runtime.getRuntime().availableProcessors(),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

ThreadPoolExecutor线程池的keepAliveTime的具体含义

keepAliveTime含义 ThreadPoolExecutor 的 javadoc 对 keepAliveTime 参数的解释: keepAliveTime when the number of threads is greater than the core, this is the maximum time that excess idle threads will wait for new tasks before terminating. 大致意思是:keepAliveTime 就是 当

weixin_44730681的博客 5596

【多模态大模型】GLIP:零样本目标检测新范式与视觉语言理解

本文深入解析了多模态大模型GLIP在零样本目标检测和视觉语言理解领域的创新应用。GLIP通过统一目标检测与自然语言理解任务,实现了零样本学习能力,能够精准定位复杂文本描述对应的图像区域。文章详细介绍了GLIP的核心技术、数据策略及实战表现,并对比了与其他多模态模型的差异,为AI视觉理解提供了新范式。

weixin_29186249的博客 263

线程池

/** 线程池: 1.创建线程池(ExecutorService) 真正的线程池接口:java.util.concurrent.ExecutorService. Runnable形式。 2.创建线程池的步骤: 1.创建一个线程池。 2.通过Executors下的一个静态方法newFixedThreadPool(int nums),创建一个固定...

linggle的博客 353

Mac Studio M5 Ultra本地部署大模型实战:从内存原理到Ollama开发工作流

大模型推理的性能瓶颈往往不在算力,而在于内存能否装下整个模型、带宽能否持续供得上。Transformer架构每生成一个token都需要把权重完整扫一遍,决定了这本质上是内存密集型任务——统一内存架构因此成为本地部署的关键通路,超大容量与高带宽并行,让“装得下”和“跑得快”同时成立。模型量化技术(如Q4_K_M)则是另一项决定性软件策略,用轻微精度损失换取更低的硬件门槛,也是Ollama本地部署从7B到70B各类模型必做的前置功课。当本地推理服务跑起来后,借助VS Code等开发工具的OpenAI兼容API接

weixin_29043895的博客 453

NXP S32K1 SPI驱动实战:从配置到DMA传输的完整流程解析

本文深入解析了NXP S32K1系列MCU的SPI驱动开发全流程。基于S32 Design Studio环境,详细阐述了从引脚配置、LPSPI模块初始化,到实现阻塞、中断及DMA三种传输模式的实战代码与配置要点。文章重点剖析了DMA传输的配置细节与性能优化,旨在帮助开发者构建高效、稳定的嵌入式SPI通信,并提供了SPI Flash驱动的综合案例。

blue42的博客 716

java 多线程 线程池

java 多线程 ThreadPoolExecutor AbstractExecutorService ExecutorService Executor 以上就是多线程核心的4个类 他们的关系 ThreadPoolExecutor extends AbstractExecutorService implements ExecutorService implements Executor ...

灰太狼的博客 393

COCOS 3.8 接入高德定位SDK

/微信登录所必须的code。//是否获得code。

八喜互动 1993

Executors 线程池的7个参数详解

线程池创建有7个参数 下面为构造函数 /** * Creates a new {@code ThreadPoolExecutor} with the given initial * parameters. * * @param corePoolSize the number of threads to keep in the pool, even * if they are idle, unless {@code allowCoreThreadTimeOut} is set * @param

Jarvis.y的博客 779

从零到一:用STM32 FOC算法驯服无刷电机的艺术

本文详细介绍了如何使用STM32和FOC算法实现无刷电机的高性能控制。从硬件搭建、参数识别到闭环调节,一步步解析FOC算法的核心原理与调优技巧,帮助开发者掌握电机控制的关键技术,实现从零到一的精准驯服。

kkk56的博客 919

【Arduino 动手做】构建一个迷你 Las Vegas RGB LED 球体

《Arduino迷你拉斯维加斯球体项目:15岁少年的创意挑战》 15岁的Ben通过本项目展示了如何利用Arduino平台制作一个经济实惠的迷你拉斯维加斯球体夜灯。该项目涉及LED选择(采用WS2812B可编程灯带)、电子元件配置(含Arduino Nano微控制器和红外遥控)以及3D建模设计(100毫米直径球体)。项目难点包括LED布局优化、供电方案设计和3D打印外壳制作。Ben详细记录了从背景研究、电路设计到最终实现的完整过程,特别强调了分辨率与成本的平衡,以及如何通过白色扩散器改善视觉效果。这个项目不仅

雕爷学编程 1024

[java并发]java高并发系列 - 第18天:JAVA线程池,这一篇就够了

原文链接:查看原文 感谢公众号“ 路人甲Java”的分享,如有冒犯,请联系删除,快去关注他吧 本文主要内容 什么是线程池 线程池实现原理 线程池中常见的各种队列 自定义线程创建的工厂 常见的饱和策略 自定义饱和策略 线程池中两种关闭方法有何不同 扩展线程池 合理地配置线程池 10.线程池中线程数量的配置 什么是线程池? 大家用jdbc操作过数据库应该知道,操作数据库需要和数据库建立连接,拿到连接之后才能操作数据库,用完之后销毁。数据库连接的创建和销毁其实是比较耗时的,真正和业务相关的操作耗时是比

Aran_的博客 430

js逆向第3例:蝉妈妈大屏实时数据formDataSign加密

蝉妈妈大屏抖音直播实时数据是抓取抖音直播间数据的实时数据,有类似需求的公司但没有能力去搞定抖音,抓取蝉妈妈数据也是一个不错的选择。接下来就是调试找到加密点,说句题外话蝉妈妈的所有js文件打包方式用的都是webpack5,那如何区分打包方式可以阅读这篇文章。时间戳 ,经过观察发现加密参数是实时变化的。这个函数扣下来,在浏览器控制台中测试一下。言归正传,打断点找到变量。继续打断点向上找又发现了。查看请求参数,可以看到。请求的内容,那么这个。

花臂不花Home 1540

关于多线程学习总结(五) 线程池

必须得说点什么 今天在介绍线程池之前,我得来说说上上篇随笔。关于线程的优先级,我们知道在C#中可以设置线程的优先级,使重要的作业可以优先执行,但是这个优先级不是一成不变的,也就是说就算你设置一个线程的优先级非常高,但是也有可能在优先级较低的线程后执行。优先级高不代表就得到了绝对的通行证,给一个例子大家看看: 1 class Program 2 { 3

昨日书 327

Authentication Assertion Tickets,SAP 系统到系统单点登录里的短命凭证

SAP 系统间的身份传递机制存在 Authentication Assertion Ticket 和 SAP Logon Ticket 两种易混淆的凭证。前者用于系统间短时身份委托(有效期2分钟,通过HTTP头传输),后者用于浏览器SSO(可配置有效期,通过Cookie传输)。Assertion Ticket依赖签发系统的数字签名和严格的时间同步,适用于ABAP/Java系统间的后台作业调用。现代SAP架构已转向OAuth 2.0等标准协议,但老系统中仍需注意两种票据的关键差异:传输方式、有效期、接收方限制

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 602

IDEA的使用指南

IDEA基于java的使用手册,可以最快速的上手,在使用中不断的丰富自己的使用技巧.

java线程变量那点事儿

ThreadLocal解决的场景和存在的缺陷 InheritableThreadLocal解决的问题和缺陷 TransmittableThreadLocal的作用

littlehow的专栏 1万+

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

面试官都爱问!Java并发编程18道灵魂拷问:从Synchronized到虚拟线程

AQS 是 JUC 真正的底层核心,ReentrantLock、CountDownLatch、Semaphore 全基于它。核心三板斧state:volatile int 同步状态CLH 队列:抢不到锁的线程进入 FIFO 等待队列CAS:安全修改 state,抢占锁生活化理解:AQS 就是医院叫号系统,state 是叫号器,队列是等候区,CAS 是抢号。

HHX_01的博客 907

efm32 入门.pdf

本PDF共计22页,介绍的是操作驱动API,下载与否自己斟酌。

ssm框架搭建

这个是一个在idea上成功跑起来的ssm框架项目,保存在这里方便以后使用直接下载

100道Java中高级面试题汇总+详细拆解

出自:Java面试题精选1-10期【10期】Redis 面试常见问答【09期】说说hashCode() 和 equals() 之间的关系?【08期】说说Object类下面有几种方法呢?【...

Java知音 7001

python爬虫之线程池和进程池

一、需求   最近准备爬取某电商网站的数据,先不考虑代理、分布式,先说效率问题(当然你要是请求的太快就会被封掉,亲测,400个请求过去,服务器直接拒绝连接,心碎),步入正题。一般情况下小白的我们第一个想到的是for循环,这个可是单线程啊。那我们考虑for循环直接开他个5个线程,问题来了,如果有一个url请求还没有回来,后面的就干等,这么用多线程等于没用,到处贴创可贴。 二、性能考虑   确定...

weixin_30410119的博客 277

2024最新Java大厂面试题来袭,2024年Java高级面试题总结

引领完成Docker的安装、部署、管理和扩展,让其经历从测试到生产的整个开发生命周期,深入了解Docker适用于什么场景。并且这本Docker的学习权威指南介绍了其组件的基础知识,然后用Docker构建容器和服务来完成各种任务:利用Docker为新项目建立测试环境,演示如何使用持续集成的工作流集成Docker,如何构建应用程序服务和平台,如何使用Docker的API,如何扩展Docker。

2401_83704218的博客 880

2024网易Java高级面试题及答案,绝对干货分享

面试专题分为四个部分,分别如下Synchronized 相关问题可重入锁 ReentrantLock 及其他显式锁相关问题Java 线程池相关问题Java 内存模型相关问题问题一:Synchronized用过吗?其原理是什么?问题二:你刚才提到获取对象的锁 ,这个“ 锁 ”到底是什么?如何确定对象的锁?问题 三:什么是可重入性 , 为什么说Synchronized是可重入锁?问题四:JVM对Java的原生锁做了哪些优化?问题五:为什么说Synchronized是非公平锁?问题六:什么是锁消除和锁粗化 ?问题

weixin_42179304的博客 945

java基础入门第四章课后题答案,就是这么简单

这份清华大牛整理的进大厂必备的redis视频、面试题和技术文档祝大家早日进入大厂,拿到满意的薪资和职级~~~加油!!!《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!is视频、面试题和技术文档**祝大家早日进入大厂,拿到满意的薪资和职级~~~加油!!![外链图片转存中…(img-rjrK5wCD-1713852587746)]《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

2401_84408337的博客 509

java互联网架构师薪资,帮你突破瓶颈

问题四:既然提到可以通过配置不同参数创建出不同的线程池,那么Java中默认实现好的线程池又 有哪些呢?请比较它们的异同。问题三:既然volatile能够保证线程间的变量可见性,是不是就意味着基于volatile变量的运算就是并 发安全的?体系大纲是个人对知识的总结梳理,全程用的xmind手绘,不过文件不能上传,所以这里都是上传的图片形式,不过有原件。问题一:什么是Java的内存模型,Java中各个线程是怎么彼此看到对方的变量的?问题三:线程池中的线程是怎么创建的?是一开始就随着线程池的启动创建好的吗?

2401_84414990的博客 848

【java】Java经典面试题整理(附答案)

所谓反射,是java在运行时进行自我观察的能力,通过class、constructor、field、method四个方法获取一个类的各个组成部分。在Java运行时环境中,对任意一个类,可以知道类有哪些属性和方法。这种动态获取类的信息以及动态调用对象的方法的功能来自于反射机制。序列化就是一种用来处理对象流的机制。将对象的内容流化,将流化后的对象传输于网络之间。

DreamSun的博客 2921
上一篇: 【高并发系列】17、线程池那些事儿1
下一篇: 【高并发系列】19、JDK并发容器 - 线程安全的HashMap、List
hellboy0621
博客等级 码龄9年 61粉丝 147原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值