面试官:小伙子你连多线程输出数列都不会,还敢说本身会多线程?

1、前言

计算机的操做系统大多采用任务和分时设计,多任务是指在一个操做系统中能够同时运行多个程序,例如,能够在使用qq聊天的同时听音乐,即有多个独立运行的任务,每一个任务对应一个进程,每一个进程又能够产生多个线程。java

1.进程

进程是程序的一次动态执行过程,它对应了从代码加载、执行至执行完毕的一个完整过程,这个过程也是进程自己从产生、发展至消亡的过程。操做系统同时管理一个计算机系统中的多个进程,让计算机系统中的多个进程轮流使用CPU资源。
进程的特色:多线程

  1. 进程是系统运行程序的基本单位
  2. 每个进程都有本身独立的一块内存空间、一组系统资源
  3. 每个进程的内部数据和状态都是彻底独立的

2.线程

线程是进程中执行运算的最小单位,一个进程在其执行过程当中能够产生多个线程而线程必须在某个进程内执行。
线程是进程内部的一个执行单元,是可完成一个独立任务的顺序控制流程,若是在一个进程中同时运行了多个线程,用来完成不一样的工做,则称之为多线程。
线程按处理级别能够分为核心级线程和用户及线程并发

1.核心机线程
核心级线程是和系统任务相关的额线程,他负责处理不一样进程之间的多个线程。容许不一样进程中的线程按照同一相对优先调度方法对线程进行调度,使他们有条不紊的工做,能够发挥多处理器的并发优点,以充分利用计算机的软/硬件资源测试

2.用户级线程
在开发程序时,因为程序的须要而编写的线程即用户级线程,这些线程的建立,执行和消亡都是在编写应用时进行控制的。对于用户级线程的切换,一般发生在一个应用程序的诸多线程之间,如迅雷中的多线程下载就属于用户线程操作系统

3.线程和进程的联系以及区别
1.一个进程中至少有一个线程
2.资源分配给进程,同一个进程的全部线程共享该进程的全部资源
3.处理机分配给线程,即真正在处理机上运行的是线程线程

4.多线程的优点
1.多线程程序能够带来更好的用户体验,避免因程序执行过慢而致使出现计算机死机或者白屏的状况。
2.多线程能够最大限度地提升计算机系统的利用效率,如迅雷的多线程下载。设计

2、编写线程类

每一个程序至少自动拥有一个线程,称为主线程。当程序加载到内存时启动主线程。java程序中的main方法时主线程的入口,运行java程序时,会先执行这个方法。开发中,用户编写的线程通常都是指除了主线程以外的其余线程
使用一个线程的过程通常有如下4个步骤code

  1. 定义一个线程,同时指明这个线程所要执行的代码,即指望完成的功能
  2. 建立线程对象
  3. 启动线程
  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();
    }
}

3、线程的状态

线程生命周期4阶段:新生状态,可运行状态,阻塞状态,死亡状态

1.新生状态

线程在还没有调用start方法以前就有了生命,线程仅仅是一个空对象,系统没有为其分配资源,此时只能启动和终止线程,任何其余操做都会发生异常。

2.可运行状态

当调用start方法后启动线程后,系统为该线程分配出CPU外的所需资源,这时线程就处于可运行的状态。
固然在这个状态中,线程也可能未运行。对于只有一个CPU的机器而言,任什么时候刻只能有一个处于可运行状态的线程占用处理机

3.阻塞状态

一个正在运行的线程因某种缘由不能继续运行时,进入阻塞状态,阻塞状态是不可运行的状态。
致使阻塞状态的缘由

  1. 调用了Thread的静态方法sleep()
  2. 一个线程执行到一个I/O操做时,I/O操做还没有完成
  3. 若是一个线程的执行须要获得一个对象锁,而这个对象的锁正在被别的线程用,那么会致使阻塞
  4. 线程的suspend()方法被调用而使线程被挂起时,线程进入阻塞状态

3.死亡状态

当一个线程的run方法运行完毕,stop方法被调用或者在运行过程当中出现未捕获的异常时,线程进入死亡状态。

4、线程调度

当同时有多个线程处于可运行状态,他们须要排队等待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相关技术文章或行业资讯,欢迎你们关注和转发文章!

相关文章
相关标签/搜索