一、前言
计算机的操作系统大多采纳工作和分时设计,多任务是指在一个操作系统中能够同时运行多个程序,例如,能够在应用qq聊天的同时听音乐,即有多个独立运行的工作,每个工作对应一个过程,每个过程又能够产生多个线程。
1.过程
过程是程序的一次动静执行过程,它对应了从代码加载、执行至执行结束的一个残缺过程,这个过程也是过程自身从产生、倒退至沦亡的过程。操作系统同时治理一个计算机系统中的多个过程,让计算机系统中的多个过程轮流应用CPU资源。
过程的特点:
- 过程是零碎运行程序的根本单位
- 每一个过程都有本人独立的一块内存空间、一组系统资源
- 每一个过程的外部数据和状态都是齐全独立的
2.线程
线程是过程中执行运算的最小单位,一个过程在其执行过程中能够产生多个线程而线程必须在某个过程内执行。
线程是过程外部的一个执行单元,是可实现一个独立工作的顺序控制流程,如果在一个过程中同时运行了多个线程,用来实现不同的工作,则称之为多线程。
线程按解决级别能够分为外围级线程和用户及线程
1.外围机线程
外围级线程是和零碎工作相干的额线程,他负责解决不同过程之间的多个线程。容许不同过程中的线程依照同一绝对优先调度办法对线程进行调度,使他们井井有条的工作,能够施展多处理器的并发劣势,以充沛利用计算机的软/硬件资源
2.用户级线程
在开发程序时,因为程序的须要而编写的线程即用户级线程,这些线程的创立,执行和沦亡都是在编写利用时进行管制的。对于用户级线程的切换,通常产生在一个应用程序的诸多线程之间,如迅雷中的多线程下载就属于用户线程
3.线程和过程的分割以及区别
1.一个过程中至多有一个线程
2.资源分配给过程,同一个过程的所有线程共享该过程的所有资源
3.处理机调配给线程,即真正在处理机上运行的是线程
4.多线程的劣势
1.多线程程序能够带来更好的用户体验,防止因程序执行过慢而导致呈现计算机死机或者白屏的状况。
2.多线程能够最大限度地进步计算机系统的利用效率,如迅雷的多线程下载。
二、编写线程类
每个程序至多主动领有一个线程,称为主线程。当程序加载到内存时启动主线程。java程序中的main办法时主线程的入口,运行java程序时,会先执行这个办法。开发中,用户编写的线程个别都是指除了主线程之外的其余线程
应用一个线程的过程个别有以下4个步骤
- 定义一个线程,同时指明这个线程所要执行的代码,即冀望实现的性能
- 创立线程对象
- 启动线程
- 终止线程
定义一个线程通常由两种办法,别离是继承java.lang.Thread类和java.lang.Runnable接口
1.应用Thread类创立线程
Thread类的罕用办法:
办法 | 阐明 |
---|---|
void run() | 执行工作操作的办法 |
void start() | 使该线程开始执行 |
void sleep(long mils) | 在指定的毫秒数内让以后正在执行的线程休眠(暂停执行) |
String getName() | 返回该线程的名称 |
int getPririt() | 返回线程的优先级 |
void setPriority(int newPoriority) | 更改线程的优先级 |
Thread.State getState() | 返回该线程的状态 |
boolean is Alive() | 测试线成是否处于活动状态 |
void join() | 期待该线程终止 |
void interrupt() | 中断线程 |
void yield() | 暂停以后正在执行的线程对象,并执行其余线程 |
创立线程时继承Thread类并重写Thread类run()办法。其中run办法时线程要执行操作工作的办法,所以线程要执行的操作代码都要写在run办法中,并通过调用start办法来启动线程。
示例:应用继承Thread类的形式来创立线程,在线程中输出1-100的整数
实现步骤:
1.定义一个类来继承Thread类,重写run办法,在run办法中实现数据输入
2.创立线程对象
3.调用start办法启动线程
public class MyThread extends Thread{ //示例:应用继承Thread类的形式来创立线程,在线程中输出1-100的整数 private int count = 0; //重写run办法 public void run(){ for (int i = 1; i <=100 ; i++) { System.out.println(i); } }}
启动线程
package xc.test1;public class Test { public static void main(String[] args) { //实例化线程对象 MyThread mt = new MyThread(); //启动线程 mt.start(); }}
2.应用Runnable接口创立线程
尽管Thread类的形式创立线程简单明了合乎大家的习惯,但他也有一个毛病,如果定义的类曾经继承了其余类则无奈再继承Thread类。应用Runnable接口创立线程的形式能够解决上述问题。
Runnable接口中申明了一个run办法(),即public void run()。一个类能够通过实现Runnable接口并实现其run()办法实现线程的所有流动,已实现的run办法称为该对象的线程体。任何实现runable接口的对象都能够作为一个线程的指标对象。
示例:应用继承Thread类的形式来创立线程,在线程中输出1-100的整数
实现步骤:
1.定义了MyThread类实现Runable接口,并实现Runnable接口的run办法,在run办法中输入数据
2.创立线程对象
3.调用start办法启动线程
public class MyThread implements Runnable{ //示例:应用继承Thread类的形式来创立线程,在线程中输出1-100的整数 private int count = 0; //重写run办法 public void run(){ for (int i = 1; i <=100 ; i++) { System.out.println(i); } }}
启动start
package xc.test1;public class Test { public static void main(String[] args) { //实例化线程对象 Thread thread = new Thread(new MyThread()); //启动线程 thread.start(); }}
三、线程的状态
线程生命周期4阶段:新生状态,可运行状态,阻塞状态,死亡状态
1.新生状态
线程在尚未调用start办法之前就有了生命,线程仅仅是一个空对象,零碎没有为其分配资源,此时只能启动和终止线程,任何其余操作都会产生异样。
2.可运行状态
当调用start办法后启动线程后,零碎为该线程调配出CPU外的所需资源,这时线程就处于可运行的状态。
当然在这个状态中,线程也可能未运行。对于只有一个CPU的机器而言,任何时刻只能有一个处于可运行状态的线程占用处理机
3.阻塞状态
一个正在运行的线程因某种原因不能持续运行时,进入阻塞状态,阻塞状态是不可运行的状态。
导致阻塞状态的起因
- 调用了Thread的静态方法sleep()
- 一个线程执行到一个I/O操作时,I/O操作尚未实现
- 如果一个线程的执行须要失去一个对象锁,而这个对象的锁正在被别的线程用,那么会导致阻塞
- 线程的suspend()办法被调用而使线程被挂起时,线程进入阻塞状态
3.死亡状态
当一个线程的run办法运行结束,stop办法被调用或者在运行过程中呈现未捕捉的异样时,线程进入死亡状态。
四、线程调度
当同时有多个线程处于可运行状态,他们须要排队期待CPU资源,每个线程会主动 取得一个线程的优先级,优先级的高下反映出线程的重要或紧急水平,可运行状态的线程按优先级排队。线程调度根据建设在优先级根底上的“先到先服务”准则。
线程调度室抢占式调度,即在以后线程执行过程中如果有一个更高优先级的线程进入可运行状态,则这个更高优先级的线程立刻被调度执行。
1.线程优先级
线程的优先级用1~10示意,10示意优先级最高,默认值是5,每个优先级对应一个Thread类的专用动态常量
例如:public static final int NORM_PRIORITY=5;
每个线程的优先级都介于Thread.MIN_PRIORITY和Thread.MAX_PRIORITY之间
线程的优先级能够通过setPrioity(int grade)办法更改
2.实现线程调度的办法
实现线程调度的办法
1.join()办法
join办法使以后线程暂停执行,期待调用该办法的线程完结后再继续执行本线程。
它有3中重载模式
public final void join()
public final void join(long mills)
public final void join(long mills,int nanos)
示例:应用join()阻塞线程
实现步骤:
1.定义线程类,输入5次以后线程的名称
2.定义测试类,应用join办法阻塞主线程
package xc.test2;public class MyThread extends Thread { public MyThread(String name){ super(name); } public void run(){ for (int i = 0; i <5 ; i++) { //输入以后线程的名称 System.out.println(Thread.currentThread().getName()+""+i); } }}
package xc.test2;public class Test { public static void main(String[] args) { //主线程运行五次后,开始运行MyThread线程 for (int i = 0; i <10 ; i++) { if (i==5){ MyThread t = new MyThread("MyThread"); try { t.start(); t.join();//把该线程通过join办法插入到主线程背后 } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println(Thread.currentThread().getName()+""+i); } }}
示例:应用sleep()办法阻塞线程
实现步骤:
定义线程
在run()办法中应用sleep()办法阻塞线程
定义测试类
package xc.test3;public class Wait { //==示例:应用sleep()办法阻塞线程== public static void bySec(long s){ for (int i = 0; i < s; i++) { System.out.println((i+1)+"秒"); } try { Thread.sleep(1000);//括号中的是毫秒 } catch (InterruptedException e) { e.printStackTrace(); } }}
package xc.test3;public class Test { public static void main(String[] args) { System.out.println("wait");//提醒期待 Wait.bySec(5);//让主线程期待五秒再执行 System.out.println("start");//提醒复原执行 }}
2.yield()办法
语法格局:
- public static void yield()
yield办法可让以后线程暂停执行,容许其余线程执行,但该线程仍处于可运行状态,并不变为阻塞状态。此时,零碎抉择其余雷同或更高优先级线程执行,若无其余雷同或更高优先级线程,则该线程继续执行。
示例:应用yield办法暂停线程
实现步骤:
1.定义两个线程
2.在run办法中应用yield办法暂停线程
3.定义测试类
package xc.test4;public class FirstThread extends Thread{ public void run(){ for (int i = 0; i < 5; i++) { System.out.println("第一个线程的第"+(i+1)+"次运行"); Thread.yield();//暂停线程 } }}
package xc.test4;public class SecThread extends Thread{ public void run(){ for (int i = 0; i < 5; i++) { System.out.println("第二个线程的第"+(i+1)+"次运行"); Thread.yield(); } }}
package xc.test4;public class Test { public static void main(String[] args) { FirstThread f = new FirstThread(); SecThread s = new SecThread(); f.start(); s.start(); }}
sleep办法与yield办法的区别
sleep()办法 | yield()办法 |
---|---|
使以后线程进入被阻塞的状态 | 使以后线程进入暂停执行的状态 |
即便没有其余期待运行的线程,以后线程也会期待指定的工夫 | 如果没有其余期待执行的线程,以后线程会马上复原执行 |
其余期待执行的线程的机会是均等的 | 会运行优先级雷同或更高的线程 |
最初
感激你看到这里,看完有什么的不懂的能够在评论区问我,感觉文章对你有帮忙的话记得给我点个赞,每天都会分享java相干技术文章或行业资讯,欢送大家关注和转发文章!