1、线程调度 线程调度模型 a、分时调度模型 所有线程轮流使用 CPU 的使用权,平均分配每个线程占用 CPU 的时间片 b、抢占式调度模型 抢占式调度模型 优先让优先级高的线程使用 CPU,如果线程的优先级相同,那么会随机选择一个,优先级高的线程获取的 CPU 时间片相对多一些 Java使用的是抢占式调度模型 如何获取和设置线程优先级 public final int getPriority () public final void setPriority(int newPriority) 2、线程控制 线程休眠 public static void sleep(long millis ) 线程加入 public final void join() 线程礼让 public static void yield() 后台线程 public final void setDaemon(boolean on) 中断线程 public final void stop() public void interrupt() 3、线程的生命周期
2.可以根据系统的承受能力,调整线程池中工作线线程的数目,防止因为消耗过多的内存,而把服务器累趴下(每个线程需要大约1MB内存,线程开的越多,消耗的内存也就越大,最后死机)。 1. newSingleThreadExecutor 创建一个单线程的线程池。这个线程池只有一个线程在工作,也就是相当于单线程串行执行所有任务。 每次提交一个任务就创建一个线程,直到线程达到线程池的最大大小。线程池的大小一旦达到最大值就会保持不变,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程。 如果线程池的大小超过了处理任务所需要的线程, 那么就会回收部分空闲(60秒不执行任务)的线程,当任务数增加时,此线程池又可以智能的添加新线程来处理任务。 此线程池不会对线程池大小做限制,线程池大小完全依赖于操作系统(或者说JVM)能够创建的最大线程大小。 4.newScheduledThreadPool 创建一个大小无限的线程池。
线程池 线程池:三大方法,七大参数,4中拒绝策略 Executors 是一个工具类,三个常用方法 // 创建一个线程 var es = Executors.newSingleThreadExecutor 如果正在运行的线程数等于corePoolSize时,则新任务被添加到队列中,直到队列满。当队列满了后,会继续开辟新线程来处理任务,但不超过最大线程数。 maximumPoolSize 最大线程池大小 keepAliveTime 当线程空闲超过keepAliveTime,非核心线程会被回收,若allowCoreThreadTimeOut为true则核心线程也会被回收 (jdk默认策略) CallerRunsPolicy -- 当任务添加到线程池中被拒绝时,会在线程池当前正在运行的Thread线程池中处理被拒绝的任务。 DiscardPolicy -- 当任务添加到线程池中被拒绝时,线程池将丢弃被拒绝的任务。
当你需要同时执行多个任务时,Python中的多线程可以帮助你实现并发执行。以下是一个简单的示例,演示了如何在Python中使用多线程。 import threading import time # 定义一个函数作为线程的目标函数 def print_numbers(): for i in range(5): print = threading.Thread(target=print_numbers) # 启动线程 thread1.start() thread2.start() # 等待线程执行结束 thread1 然后创建了两个线程thread1和thread2,并分别将print_numbers函数设置为它们的目标函数。通过调用start方法来启动线程,然后使用join方法等待线程执行结束。 需要注意的是,由于全局解释器锁(GIL)的存在,Python中的多线程并不能实现真正的并行执行,但对于I/O密集型的任务,多线程仍然可以提供性能上的优势。
有些初学者对中断的概念可能会有些许小误会,比如线程调用Thread.interrupt()方法,就认为线程会被中断,停止执行,其实不是这样的,让我们来看下中断interrupt详解。 ),则会抛出异常,后续如果线程不想继续被操作,可以利用这个异常来让线程运行退出,比如for循环的break,或者直接return。 ,如果当前线程已经中断则返回true,否则返回false。 结果发现,线程是否被中断检测方法返回了true。 上述结果发现,线程在调用Thread.sleep后并没有被中断。加入isInterrupted方法观察线程中断标志位情况: ? ?
如果有几十万个甚至百万级别的,排队切换的时间就不能忽略不计了,这个时候就可以考虑多线程了。 这就是今天的内容,代码如下: #! if __name__ == '__main__': main() 中间实现的函数就隐藏了,看了会眼花,有感兴趣的小伙伴可以私信我获取,主要还是线程的调用方式,class MyThread(threading.Thread )继承一个线程的类,然后main()中设置多个线程,再运行。
,相互拥挤,反而不如10个处理的好,所以,多线程处理,线程数要开的恰当,就可以提高效率。 多线程使用的目的: 1、 吞吐量:做WEB,容器帮你做了多线程,但是它只能帮你做请求层面的,简单的说,就是一个请求一个线程(如struts2,是多线程的,每个客户端请求创建一个实例,保证线程安全),或多个请求一个线程 多线程的使用场景: 1、 常见的浏览器、Web服务(现在写的web是中间件帮你完成了线程的控制),web处理请求,各种专用服务器(如游戏服务器) 2、 servlet多线程 3、 FTP下载,多线程操作文件 4、 数据库用到的多线程 5、 分布式计算 6、 tomcat,tomcat内部采用多线程,上百个客户端访问同一个WEB应用,tomcat接入后就是把后续的处理扔给一个新的线程来处理,这个新的线程最后调用我们的 ,多任务的分割,由一个主线程分割给多个线程完成 13、desktop应用开发,一个费时的计算开个线程,前台加个进度条显示 14、 swing编程 举一个小栗子: 一个文本文件有100M,全是字符串,我要执行切分字符串
,自然有对线程的管理池即线程池。 ,这个线程池可以在线程死后(或发生异常时)重新启动一个线程来替代原来的线程继续执行下去! :线程池最大线程数,这个参数也是一个非常重要的参数,它表示在线程池中最多能创建多少个线程; keepAliveTime:表示线程没有任务执行时最多保持多久时间会终止。 默认情况下,只有当线程池中的线程数大于corePoolSize时,keepAliveTime才会起作用,直到线程池中的线程数不大于corePoolSize,即当线程池中的线程数大于corePoolSize return 0; } } } 运行结果: 可以看到15个耗时的操作很快就并行执行完成,并且还能返回执行的成功结果数 以上就是我对线程池的理解和应用
//原来队列容量若为0,这一步的写操作需要唤醒读操作 signalNotEmpty(); return true; } //这个方法与put方法相比,不会阻塞线程 · LinkedBlockingQueue读方法 //读取元素 会阻塞线程 public E take() throws InterruptedException { E x; int 可以对比看出,读线程的锁操作与写线程的锁操作类似。 他也是线程安全的阻塞队列,阻塞条件为读操作时如果队列为空则阻塞、写操作时如果队列满则阻塞。 他们在读操作遇到队列为空或者写操作队列为满时都会阻塞线程。 (3)构造函数不同,LinkedBlockingQueue可以指定最大容量的小或者不指定,不指定时他的容量大小为最大值。
利用闲暇时间在UI线程的空闲做一些不占时间的操作(不另起线程)和利用委托新建线程实现。
一、LinkedBlockingDeque简介 在多线程阻塞队列的应用中上一篇已经讲述了ArrayBlockingQueue,在这一篇主要介绍思想与他差不多的另一个阻塞队列,基于链表的阻塞队列- 基于链表的阻塞队列和基于数组的阻塞队列相同,内部都有一把可重入锁,对于该队列的写操作和读操作都会进行加锁,所以他们都是线程安全的,但是写操作和读操作都会占用锁资源所以在并发量大的情况下会降低性能。 //原来的头结点的上一个结点为当前新插入的结点 f.prev = node; //当前容量增加 ++count; //唤醒读取时因队列中无元素而导致阻塞的线程 //将原尾结点的下一个结点指向新插入的节点 l.next = node; //当前容量增加 ++count; //唤醒读取时因队列中无元素而导致阻塞的线程 null,因为他已经作为了头结点 所以不需要指向上一个结点 n.prev = null; //当前数量减少 --count; //唤醒因添加元素时队列容量满导致阻塞的线程
C#使用线程时首先需要创建线程,使用Thread类构造函数创建实例需要用到ThreadStart委托或者ParameterizedThreadStart 委托创建 Thread 类的实例,ThreadStart 线程不会直接运行,直至调用Start()方法时为止。 // Control.CheckForIllegalCrossThreadCalls = false; //实例化一个线程,该线程的代理指向一个带参数的线程函数 Thread // Control.CheckForIllegalCrossThreadCalls = false; //实例化一个线程,该线程的代理指向一个带参数的线程函数 Thread : SayHelloToXiaohouye,在这个方法里声明一个变量,并输出.这就构成了最简单的多线程的例子,一般情况下,我们都是用这个的。
模块17重点 本模块将深入探讨多线程的高级应用,包括: 使用wait和notify两个方法。 使用Lock锁对象。 利用Callable接口实现多线程。 使用线程池完成多线程。 notify():线程唤醒,一次唤醒一个等待线程;如果有多条线程等待,则随机唤醒一条等待线程。 notifyAll():唤醒所有等待线程。 _实现多线程方式四 问题:之前来一个线程任务,就需要创建一个线程对象去执行,用完还要销毁线程对象,如果线程任务多了,就需要频繁创建线程对象和销毁线程对象,这样会耗费内存资源,所以我们就想线程对象能不能循环利用 ,用的时候直接拿线程对象,用完还回去 线程池是管理线程对象的一种机制,可以循环利用线程对象,减少创建和销毁线程对象的开销。 System.out.println("金莲对涛哥说:涛哥,快起床了~~~"); } }, new Date(), 2000L); } } 小结 通过今天的学习,希望可以帮助你掌握多线程的高级应用
@Async应用自定义线程池 自定义线程池,可对系统中线程池更加细粒度的控制,方便调整线程池大小配置,线程执行异常控制和处理。 在设置系统自定义线程池代替默认线程池时,虽可通过多种模式设置,但替换默认线程池最终产生的线程池有且只能设置一个(不能设置多个类继承AsyncConfigurer)。 所以可以在项目中,定义名称为TaskExecutor的bean生成一个默认线程池。也可不指定线程池的名称,申明一个线程池,本身底层是基于TaskExecutor.class便可。 ,可不指定线程池名称。 @Async注解,使用系统默认或者自定义的线程池(代替默认线程池)。
多线程协同 TensorFlow为我们提供了多线程协同操作的类—tf.Coordinator,其函数主要有: should_stop():确定当前线程是否退出 request_stop():通知其他线程退出 join():等待所有线程终止 假设有五个线程同时在工作,每个线程自身会先判断should_stop()的值,当其返回值为True时,则退出当前线程;如果为Flase,也继续该线程。 此时如果线程3发出了request_stop()通知,则其它4个线程的should_stop()将全部变为True,然后线程4自身的should_stop()也将变为True,则退出了所有线程。 多线程操作队列 前面说到了队列的操作,多线程协同的操作,在多线程协同的代码中让每一个线程打印自己的id编号,下面我们说下如何用多线程操作一个队列。 TensorFlow提供了队列tf.QueueRunner类处理多个线程操作同一队列,启动的线程由上面提到的tf.Coordinator类统一管理,常用的操作有: QueueRunner():启动线程
.实现简单多线程: QThread库提供了跨平台的多线程管理方案,通常一个QThread对象管理一个线程,在使用是需要从QThread类继承并重写内部的Run方法,并在Run方法内部实现多线程代码. : 线程在执行前可以通过调用MyThread中的自定义函数,并在函数内实现参数赋值,实现线程传参操作. : QMutex类是基于互斥量的线程同步锁,该锁lock()锁定与unlock()解锁必须配对使用,线程锁保证线程间的互斥,利用线程锁能够保证临界资源的安全性.线程锁解决的问题: 多个线程同时操作同一个全局变量 ,为了防止资源的无序覆盖现象,从而需要增加锁,来实现多线程抢占资源时可以有序执行.临界资源(Critical Resource): 每次只允许一个线程进行访问 (读/写)的资源.线程间的互斥(竞争): 多个线程在同一时刻都需要访问临界资源.一般性原则: 每一个临界资源都需要一个线程锁进行保护.
} } }); t1.start(); t2.start(); }}执行效果:2) join方法的应用场景 我们在main线程中开启了一个新的线程(t1),t1线程对num进行赋值,然后在main线程中进行打印,很显然num的值为0,因为t1线程的阻塞不会让main线程也阻塞,当t1线程阻塞时,main线程会继续往下执行 Demo03_join_应用场景 { static int num = 0; // 使用join改造 public static void main(String[] args) { e.printStackTrace(); } System.out.println(num); // 10 }}Tips:join方法一般应用于线程 因为它们没有影响应用程序主要功能的能力,所以它们可以随时被中断和终止。
但是可以拷贝指向互斥量的指针,这样就可以使多个函数或线程共享互斥量来实现同步。 当调用线程已经锁住互斥量之后,就不能再加锁该互斥量。试图这样做的结果可能是返回错误(EDEADLK)或者可能陷入“自死锁”,使线程永远等待下去。 不能解锁一个已经解锁的互斥量,也不能解锁由其他线程锁住的互斥量。被锁住的互斥量是属于加锁线程的。 线程函数依次处理alarm_list 中每个闹钟的请求,线程永不停止,当main函数返回时,线程“政蒸发”。如果列表中没有闹钟请求,则线程阻塞自己1秒,解锁互斥量,以便主线程可以添加新的闹钟请求。 在线程睡眠或阻塞之前,总要解锁互斥量。如果互斥量仍被锁住,则主线程即就无法向列表中添加请求,这将使程序变成同步工作方式。
07.07自我总结 一.多进程的应用 1.多进程模块 multiprocessing 其中常用到的几个功能 Process用于定义进程 #定义进程有两种方式 from multiprocessing import 中必须把和子程序相关的丢入main里面中 在linux中只是会记录主程序的自上而下运行后最后的运行状态,而不会重新运行一遍,所以在linux中也不需要丢入main 综上所述还是将子进程丢入main里面运行更加合适 二.多线程的应用 而Lock却不允许这种情况 active_count:存活的线程数量,返回的个数中包含主线程。 start():进程准备就绪,等待CPU调度 run():strat()调用run方法,是主线程了运行了run而不是子进程 terminate():不管任务是否完成,立即停止工作线程 3.线程的属性 与进程相似 idend:线程号。 4.线程的守护 与进程相似 5.子线程的运行在linux与windows中区别 没有区别,都与进程在linux运行的方式一样
boolean fair为true时采用公平锁,线程获取锁的顺序会和线程调用lock获取锁的顺序一样,但是公平锁需要增加阻塞和唤醒的时间开销。 如果参数为false时,采用非公平锁,线程获取锁的顺序是随机获取,因此,可以根据对应的场景来选择是否采用公平锁,只有在特别需要他的时候再使用公平锁。 添加操作完成后,还会唤醒因元素为空无法获取元素而阻塞住的线程。另外放入元素后队列容量达到最大值时,会重置putIndex的位置为0。 (); } 在这个元素中比较有意思的是用了notFull和NotEmpty监控用于线程的阻塞与唤醒。 notFull.signal()可以唤醒因队列空间满而无法将元素放入数组导致阻塞的线程,notEmpty()可以唤醒因队列空间无数据而无法取出数组中的元素导致阻塞的线程。