cpu 核心数和线程数:
估算线程池大小的公式:
Nthreads=NcpuUcpu(1+w/c),其中
Ncpu=CPU核心数
Ucpu=cpu使用率,0~1
W/C=等待时间与计算时间的比率
线程数=Ncpu/(1-阻塞系数)阻塞大于50%表示是 IO 小于是 计算
其实这两个是一个意思
至此结论就是:
IO密集型=2Ncpu(可以测试后自己控制大小,2Ncpu一般没问题)(常出现于线程中:数据库数据交互、文件上传下载、网络数据传输等等)
对于计算密集型的任务,在拥有N个处理器的系统上,当线程池的大小为N+1时,通常能实现最优的效率。(即使当计算密集型的线程偶尔由于缺失故障或者其他原因而暂停时,这个额外的线程也能确保CPU的时钟周期不会被浪费。)
即,计算密集型=Ncpu+1,但是这种做法导致的多一个cpu上下文切换是否值得,这里不考虑。读者可自己考量。
CPU 时间片轮转机制
https://hacpai.com/article/1541479518505
时间片轮转算法的基本思想是,系统将所有的就绪进程按先来先服务算法的原则,排成一个队列,每次调度时,系统把处理机分配给队列首进程,并让其执行一个时间片。当执行的时间片用完时,由一个计时器发出时钟中断请求,调度程序根据这个请求停止该进程的运行,将它送到就绪队列的末尾,再把处理机分给就绪队列中新的队列首进程,同时让它也执行一个时间片。一个时间片大概 10~100ms.
进程和线程
进程和线程都是一个时间段的描述,是CPU工作时间段的描述。不过是颗粒大小不同。
进程就是包换上下文切换的程序执行时间总和 = CPU加载上下文+CPU执行+CPU保存上下文。
进程的颗粒度太大,每次都要有上下的调入,保存,调出。如果我们把进程比喻为一个运行在电脑上的软件,那么一个软件的执行不可能是一条逻辑执行的,必定有多个分支和多个程序段,就好比要实现程序A,实际分成 a,b,c等多个块组合而成。
那么这里具体的执行就可能变成:程序A得到CPU =》CPU加载上下文,开始执行程序A的a小段,然后执行A的b小段,然后再执行A的c小段,最后CPU保存A的上下文。这里a,b,c的执行是共享了A的上下文,CPU在执行的时候没有进行上下文切换的。
这里的a,b,c就是线程,也就是说线程是共享了进程的上下文环境,的更为细小的CPU时间段。
狭义定义:进程就是一段程序的执行过程。
广义定义:进程是一个具有一定独立功能的程序关于某个数据集合的一次运行活动。它是操作系统动态执行的基本单元,在传统的操作系统中,进程既是基本的分配单元,也是基本的执行单元。
1、进程与线程的区别:
进程和线程的主要差别在于它们是不同的操作系统资源管理方式。进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其它进程产生影响,而线程只是一个进程中的不同执行路径。线程有自己的堆栈和局部变量,但线程之间没有单独的地址空间,一个线程死掉就等于整个进程死掉,所以多进程的程序要比多线程的程序健壮,但在进程切换时,耗费资源较大,效率要差一些。但对于一些要求同时进行并且又要共享某些变量的并发操作,只能用线程,不能用进程。
-
简而言之,一个程序至少有一个进程,一个进程至少有一个线程.
-
线程的划分尺度小于进程,使得多线程程序的并发性高。
-
另外,进程在执行过程中拥有独立的内存单元,而多个线程共享内存,从而极大地提高了程序的运行效率。
-
线程在执行过程中与进程还是有区别的。每个独立的线程有一个程序运行的入口、顺序执行序列和程序的出口。但是线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。
-
从逻辑角度来看,多线程的意义在于一个应用程序中,有多个执行部分可以同时执行。但操作系统并没有将多个线程看做多个独立的应用,来实现进程的调度和管理以及资源分配。这就是进程和线程的重要区别。
https://www.cnblogs.com/fuchongjundream/p/3829508.html
3种启动线程的方式:
1.继承 Thread 类,重写 run 方法。
class ThreadDemo1 extends Thread{
@Override
public void run(){
for (int i = 0;i < 5;i++){
System.out.println("Hello Thread" + i);
}
}
}
public class CreateThreadDemo{
public static void main(String[] args){
ThreadDemo1 threadDemo1 = new ThreadDemo1();
threadDemo1.start();
}
}
2.实现 Runnable 接口,相比继承 Thread 类的好处是实现接口可以对类进行扩展。更加灵活。
public class ThreadDemo2 implements Runnable{
@Override
for (int i = 0;i < 5;i++){
System.out.println("Hello Thread" + i);
}
}
}
public class CreateThreadDemo {
public static void main(String[] args) {
Thread threadDemo = new Thread(new ThreadDemo()2);
threadDemo.start();
}
}
3.实现 Callable 接口,与 Runnable 接口的不同之处在于:如果你想要在线程执行完毕之后得到带有返回值的线程则实现 Callable 接口。
public ThreadDemo3 implements Callable<String>{
@Override
public String call() throws Exception{
return "Hello Callable";
}
}
public class CreateThreadDemo{
public static void main(String[] args) throws InterruptedException,ExecutionException{
ExecutorService exec = Executors.newCachedThreadPool();
Future<String> submit = exec.submit(new ThreadDemo3));
String string = submit.get();
System.out.println(string);
}
}
安全的终止线程
java.lang.Thread类包含了一些常用的方法,如:start(), stop(), stop(Throwable) ,suspend(), destroy() ,resume()。通过这些方法,我们可以对线程进行方便的操作,但是这些方法中,只有start()方法得到了保留。
在JDK帮助文档以及Sun公司的一篇文章《Why are Thread.stop, Thread.suspend and Thread.resume Deprecated?》中都讲解了舍弃这些方法的原因。
简单来说是因为:使用stop方法虽然可以强行终止正在运行或挂起的线程,但使用stop方法是很危险的,就象突然关闭计算机电源,而不是按正常程序关机一样,可能会产生不可预料的结果,因此,并不推荐使用stop方法来终止线程。
1.使用退出标志
当 run 方法执行完后,线程就会退出。但有时 run 方法是永远不会结束的,如在服务端程序中使用线程进行监听客户端请求,或是其他的需要循环处理的任务。在这种情况下,一般是将这些任务放在一个循环中,如 while 循环。如果想使 while 循环在某一特定条件下退出,最直接的方法就是设一个 boolean 类型的标志,并通过设置这个标志为 true 或 false 来控制 while 循环是否退出。
package safetyEndThread;
import java.text.SimpleDateFormat;
import java.util.Date;
public class useExit {
//退出标志,volatile 关键字目的是同步 exit,同一时刻只有一个线程修改 exit。
public static volatile boolean exit = false;
public static void main(String[] args){
new Thread(){
public void run(){
System.out.println("线程启动啦");
while(!exit){
try{
Thread.sleep(1000);
}catch (InterruptedException e){
e.printStackTrace();
}
System.out.println(new SimpleDateFormat("HH:mm:ss").format(new Date()));
}
System.out.println("线程安全停止了");
};
}.start();
//在启一个线程,5s 后改变退出标志。
new Thread(){
public void run(){
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
exit = true;
}
}.start();
}
}
2.使用interrupt
如果一个线程由于等待某些事件的发生而被阻塞,又该怎样停止该线程呢?
这种情况经常会发生,比如当一个线程由于需要等候键盘输入而被阻塞,或者调用 Thread.join() 方法,或者 Thread.sleep() 方法,在网络中调用 ServerSocket.accept() 方法,或者调用了 DatagramSocket.receive() 方法时,都有可能导致线程阻塞,使线程处于处于不可运行状态时,即使主程序中将该线程的共享变量设置为 true,但该线程此时根本无法检查循环标志,当然也就无法立即中断。
这里我们给出的建议是,不要使用 stop() 方法,而是使用 Thread 提供的 interrupt() 方法,因为该方法虽然不会中断一个正在运行的线程,但是它可以使一个被阻塞的线程抛出一个中断异常,从而使线程提前结束阻塞状态,退出堵塞代码。
网友评论