多线程程序将单个任务按照功能分解成多个子任务来执行,每一个子任务称为一个线程,多个线程共同完成主任务的运行过程,这样能够缩短用户等待时间,提升服务效率。本篇博客将简单介绍Java开发中多线程的使用。html
目录:java
程序(program)多线程
☃ 程序是为完成特定任务、用某种语言编写的一组指令的集合。即指一段静态的代码,静态对象。并发
进程(process)ide
☃ 进程(process)是程序的一次执行过程,或是正在运行的一个程序。进程是一个动态的过程:有它自身的产生、存在和消亡的过程——生命周期this
↝ 如运行中的音乐播放器,运行的QQ操作系统
↝ 程序是静态的,进程是动态的.net
↝ 进程做为资源分配的单位,系统在运行时会为每一个进程分配不一样的内存区域
线程(thread)
☃ 进程可进一步细化为线程,是一个程序内部的一条执行路径。
↝ 若一个进程同一时间并行执行多个线程,称之为多线程运行
↝ 线程做为调度和执行的单位,每一个线程拥有独立的运行栈和程序计数器(pc),线程切换的开销较小
↝ 一个进程中的多个线程共享相同的内存单元/内存地址空间(它们从同一堆中分配对象),能够访问相同的变量和对象。这就使得线程间通讯更简便、高效。但多个线程操做共享的系统资源可能会带来安全隐患。
☃ 一个Java应用程序java.exe,至少有三个线程:main()主线程,gc()垃圾回收线程,异常处理线程。固然若是发生异常,会影响主线程。
单核CPU和多核CPU概念
☃ 单核CPU,实际上是一种假的多线程,由于在一个时间单元内,也只能执行一个线程的任务(线程交替执行)。例如:有多车道(对应多线程)最终汇聚到一个收费站口(对应cpu),只有前面的车付完过路费(一个线程执行完毕),后面的车才能继续经过,不然就要挂起等待。
☃ 若是是多核的话,才能更好的发挥多线程的效率。多条车道,多个收费口。
并行与并发
☃ 并行:多个CPU同时执行多个任务。好比:多我的同时作一个任务中不一样的事。
☃ 并发:一个CPU(采用时间片)同时执行多个任务。好比:网上商城秒杀活动、多我的同时作同一件事。
我的对多线程,并行,并发,cpu核数之间的理解(若是有不对的地方欢迎评论指正):
(1)cpu核数是硬件条件,核数越大程序执行速度越快。在单核cpu上是伪多线程(单元时间内只能执行一个线程),只有在多核cpu上才能更好的体现多线程的优势,换句话说多线程就是为了提升进程运行效率,提升cpu利用率。
(2)并行和并发与cpu核数的关系:并行是多个cpu同时处理不一样的任务,并发是一个cpu同一时间内执行多个任务。单核严格意义上不存在并行(即便有也是伪并行);并发不管是单核仍是多核都有可能发生,只要相同时间内同时有多个请求要执行某一段代码或者指令就会发生并发。
(3)多线程与并行和并发的关系:多线程与并行和并发是不一样的概念。多线程不等同于并行或并发,多线程是将复杂的进程分为多个子进程运行,能够理解为多线程是为了合理的利用系统资源。若是子进程之间相互独立,而且运行时互不干扰,那么能够认为它是并行的;若是子进程在执行时同时调用一个公共资源,那么它就是并发的。
多线程优势
☃ 提升应用程序的响应。对图形化界面更有意义,可加强用户体验。
☃ 提升计算机系统CPU的利用率。
☃ 改善程序结构。将既长又复杂的进程分为多个线程,独立运行,利于理解和修改。
多线程不必定就比单线程快,若是线程都是在一个cpu中运行,那么单线程要比多线程快,由于在一个cpu核中多线程来回交替运行须要花费更多的时间;若是是多线程在多核中运行一般就会比单线程快。
☃ 程序须要同时执行两个或多个任务。
☃ 程序须要实现一些须要等待的任务时,如用户输入、文件读写操做、网络操做、搜索等。
☃ 须要一些后台运行的程序时。
Java语言的JVM容许程序运行多个线程,它经过java.lang.Thread
类来体现。
jdk1.5以前提供两种Java API方式建立新执行线程
Thread类的特色
☃ 每一个线程都是经过某个特定Thread对象的run()方法来完成操做的,常常把run()方法的主体称为线程体
。
☃ 经过该Thread对象的start()方法来启动这个线程,而非直接调用run()。
Thread类的构造器
☃ Thread() :建立新的Thread对象
☃ Thread(String threadname):建立线程并指定线程实例名
☃ Thread(Runnable target) :它实现了Runnable接口中的run方法。
☃ Thread(Runnable target, String name) :指定建立线程的目标对象和线程实例名
☃ 定义子类继承Thread类。
☃ 子类中重写Thread类中的run方法。
☃ 建立Thread子类对象(即线程对象)。
☃ 调用线程对象start方法:启动线程,调用run方法。
//一、MyThread类继承Thread class MyThread extends Thread{ // 二、重写Thread中的run() @Override public void run() { for (int i = 1; i <= 100; i++) { if(i % 2 == 0){ System.out.println("偶数:" + i); } } } } public class ThreadTest { public static void main(String[] args) { //三、建立Thread类的子类对象 MyThread thread1 = new MyThread(); //四、子类对象调用start方法 thread1.start(); for (int i = 1; i <= 100; i++) { if (i % 2 != 0){ System.out.println("奇数:" + i); } } MyThread thread2 = new MyThread(); thread2.start(); } }
重点:
↝ 手动调用run()方法则只是普通方法,没有启动多线程模式。
↝ run()方法由JVM调用,何时调用,执行的过程控制都由操做系统的CPU调度决定
↝ 必须使用start方法才能启动多线程
↝ 一个线程对象只能调用一次start()方法启动,若是重复调用了,则将抛出“IllegalThreadStateException”异常。
Thread类的相关方法 |
---|
☄ void start(): 启动线程,并执行线程对象的run()方法 |
☄ run(): 线程在被调用时执行的操做 |
☄ String getName(): 返回线程的名称 |
☄ void setName(String name): 设置线程的名称 |
☄ static Thread currentThread():返回当前线程。在Thread子类中就是this,一般用于主线程和Runnable实现类 |
☄ static void yield(): : 线程让步 暂停当前正在执行的线程,把执行机会让给优先级相同或更高的线程,若队列中没有同优先级或更高优先级的线程,则忽略此方法 |
☄ join() :线程插队,当在线程a中调用线程b的join(),此时线程a就进入阻塞状态,直到线程b执行完之后a线程才结束阻塞状态(低优先级的线程也能够得到执行) |
☄ static void sleep(long millis) :(指定时间:毫秒) 睡眠,延时执行 令当前活动线程在指定时间段内放弃对CPU控制,使其余线程有机会被执行,时间到后从新排队。 须要抛出InterruptedException异常 |
☄ stop():强制结束线程的生命周期(过期,不推荐使用) |
☄ boolean isAlive():判断线程是否还在生命周期内 |
调度策略
➢ 时间片
➢ 抢占式:高优先级的线程抢占CPU
Java的调度方法
➢ 同优先级线程组成先进先出队列(先到先服务),使用时间片策略
➢ 对高优先级,使用优先调度的抢占式策略
➢ MAX_PRIORITY :10
➢ NORM_PRIORITY :5
➢ MIN _PRIORITY :1
线程优先级相关方法
➢ getPriority():返回线程优先级
➢ setPriority(int newPriority):改变线程的优先级
重点说明:
➢ 建立子线程时继承父线程的优先级
➢ 低优先级只是得到调度的几率低,并不是必定是在高优先级线程以后才被调用,除改变线程的优先级外还可对低优先级线程使用join()方法使其优先执行
代码示例:
class TestThread1 extends Thread{ @Override public void run() { for (int i = 0; i < 100; i++){ if(i % 2 == 0){ try { sleep(500); //睡眠,延时执行 } catch (InterruptedException e) { e.printStackTrace(); } //currentThread()获取当前线程,getName()获取线程名,getPriority()获取线程优先级 System.out.println(Thread.currentThread().getName()+":" + getPriority() + ":" + i); //System.out.println(this.currentThread().getName()+":" + i); this等同于Thread } if(i % 10 == 0){ yield(); //线程让步 } } } } class TestThread2 extends Thread{ public TestThread2(String name){ super(name); } @Override public void run() { for (int i = 0; i < 100; i++){ if(i % 2 != 0){ System.out.println(Thread.currentThread().getName()+":" + i); } if(i == 80){ this.stop(); //强制结束当前线程,已过期 } } } } public class ThreadMethodTest{ public static void main(String[] args) { TestThread1 thread1 = new TestThread1(); thread1.setName("线程1"); //设置线程名 System.out.println(Thread.currentThread()); //返回当前线程 thread1.setPriority(Thread.MAX_PRIORITY); //thread1.setPriority(7) thread1.start(); TestThread2 thread2 = new TestThread2("线程2"); //构造器方式设置线程名 thread2.start(); Thread.currentThread().setName("主线程"); for(int i = 0; i < 100; i++){ System.out.println(Thread.currentThread().getName() + i); if (i % 10 ==0){ try{ thread2.join(); //线程插队 }catch (InterruptedException e){ e.printStackTrace(); } } } System.out.println(thread1.isAlive()); //判断线程是否还在生命周期内 System.out.println(thread2.isAlive()); } }
☃ 定义子类,实现Runnable接口。
☃ 子类中重写Runnable接口中的run方法。
☃ 经过Thread类含参构造器建立线程对象。
☃ 将Runnable接口的子类对象做为实际参数传递给Thread类的构造器中。
☃ 调用Thread类的start方法:开启线程,调用Runnable子类接口的run方法。
//一、建立Runnable接口实现类 class RunnableThread implements Runnable{ //实现Runnable中的抽象方法run() @Override public void run() { for (int i = 0; i < 100; i++){ if(i % 2 == 0){ System.out.println(Thread.currentThread().getName()+":" + Thread.currentThread().getPriority() + ":" + i); } } } } public class RunnableTest { public static void main(String[] args) { //三、建立实现类的对象 RunnableThread r1 = new RunnableThread(); //四、将Runnable接口的子类对象做为实际参数传递给Thread类的构造器中 Thread t1 = new Thread(r1); //五、调用Thread类的start方法:开启线程,调用Runnable子类接口的run方法 t1.setName("线程一"); t1.start(); Thread t2 = new Thread(r1); t2.setName("线程二"); t2.start(); } }
区别
➢ 继承Thread:线程执行代码存放Thread子类run方法中。
➢ 实现Runnable:线程代码存在接口的子类的run方法中。
联系
➢ Thread也是实现了Runnable中的run()方法。
Runnable接口线程的优势
➢ 避免了单继承的局限性
➢ 多个线程能够共享同一个接口实现类的对象,很是适合多个线程来处理同一份资源
练习:
三个窗口售卖100张票
//继承Thread方式 class SellTicket extends Thread{ private static int ticketNum = 100; public SellTicket(String windownName){ super.setName(windownName); } @Override public void run() { while(true){ if(ticketNum > 0){ System.out.println(getName() + "窗口卖票,票号为:" + ticketNum--); }else{ System.out.println("票已卖光"); break; } } } } public class Thread_SellTicket { public static void main(String[] args) { SellTicket t1 = new SellTicket("窗口1"); SellTicket t2 = new SellTicket("窗口2"); SellTicket t3 = new SellTicket("窗口3"); t1.start(); t2.start(); t3.start(); } } *********************************************** //实现Runnable接口方式 class R_SellTicket implements Runnable{ private int ticketNum = 100; @Override public void run() { while(true){ if(ticketNum > 0){ System.out.println(Thread.currentThread().getName() + "窗口卖票,票号为:" + ticketNum--); }else{ System.out.println("票已卖光"); break; } } } } public class Runnable_SellTicket { public static void main(String[] args) { R_SellTicket r = new R_SellTicket(); Thread t1 = new Thread(r, "窗口1"); Thread t2 = new Thread(r, "窗口2"); Thread t3 = new Thread(r, "窗口3"); t1.start(); t2.start(); t3.start(); } } //存在线程安全问题,待解决
本博客与CSDN博客༺ཌ༈君☠纤༈ད༻同步发布