【十八掌●基本功篇】第一掌:Java之多线程--1-一些概念

这一篇博文是【大数据技术●降龙十八掌】系列文章的其中一篇,点击查看目录:这里写图片描述大数据技术●降龙十八掌


系列文章:
【十八掌●基本功篇】第一掌:Java之IO
【十八掌●基本功篇】第一掌:Java之多线程–1-一些概念
【十八掌●基本功篇】第一掌:Java之多线程–2-join、同步、死锁、等待

1、操作系统教程中对进程的描述

进程的两个基本特征
(1) 进程是一个拥有资源的独立单元
(2) 进程是一个被操作系统独立调度和执行的基本单元。
进程的特点
(1) 动态特征:进程是程序的一次执行过程,是临时的,有生命期的,是动态产生,动态消亡的;
(2) 并发特征:任何进程都可以同其他进程一起并发执行;
(3) 独立特征:进程是系统进行资源分配和调度的一个独立单位;
(4) 结构特征:进程由程序、数据和进程控制块三部分组成。
(5) 异步特征

2、进程切换过程

比如进程A切换到进程B,过程如下:
(1) 保存被中断进程A的处理器现场信息
(2) 修改被中断进程A的进程控制块信息:如进程状态等。
(3) 把被中断进程A的进程控制块加入到有关队列
(4) 选择下一个允许使用处理器资源的就绪状态的进程B
(5) 修改被选中进程B的进程控制块有关信息:比如进程状态
(6) 根据被选中的进程B的设置操作系统用到的地址转换信息和存储保护信息
(7) 根据被选中进程B恢复处理器现场。

3、线程的优点

(1) 创建时间短
(2) 终止时间开销少
(3) 切换快
(4) 通讯效率高

4、线程创建方法

线程的创建方法有两种:

(1)继承java.lang.Thread类

Thread类有一个run方法,子类应该重写这个方法,在这个run方法里写新建的线程要做的业务逻辑代码。

/**
 * Created by 鸣宇淳 on 2017/12/7.
 */
public class HelloThread extends Thread {

    @Override
    public void run() {
        //子线程,Thread.currentThread().getName(),获取当前线程为Thread-n
        for (int n = 0; n < 100; n++) {
            System.out.println("当前线程" + Thread.currentThread().getName() + "==>" + n);
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}


public class MyThread {

    public static void main(String[] args) {
        //main方法里是主线程
        //Thread.currentThread().getName() 是获取当前线程名称,为main
        System.out.println("当前线程" + Thread.currentThread().getName());

        //第一个线程
        HelloThread t1 = new HelloThread();

        //第二个线程
        HelloThread t2 = new HelloThread();

        //启动线程的方法是start,而不是run。
        //1、如果调用run(),相当于调用一个普通类的方法
        //2、如果调用start(),当做一个子线程来启动
        t1.start();
        t2.start();
        System.out.println("正在打印...");
    }
}

(2)实现接口java.lang.Runnable

实现了这个接口的类,调用run方法来创建一个线程。
java里类只能继承于一个父类,当子类需要继承于其他类时,使用继承java.lang.Thread类的方式来实现多线程就不方便了,可以使用实现java.lang.Runnable接口的方式来实现多线程。
实际上,Thread类也是继承了Runnable接口。
这种方式创建子线程更加的灵活,所以推荐使用这种方式。

实例:

/**
 * Created by 鸣宇淳 on 2017/12/7.
 */
public class HelloRunner implements Runnable {
    public void run() {
        //子线程,Thread.currentThread().getName(),获取当前线程为Thread-n
        for (int n = 0; n < 100; n++) {
            System.out.println("当前线程" + Thread.currentThread().getName() + "==>" + n);
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

public class MyThreadSecond {
    public static void main(String[] args) {

        //实例化一个线程执行的对象
        HelloRunner runner=new HelloRunner();

        //定义线程,指定线程要执行的对象,
        // 第二个参数是给线程起一个名称(可以省略)
        Thread t1=new Thread(runner,"T-1");
        Thread t2=new Thread(runner,"T-2");

        //启动子线程
        t1.start();
        t2.start();

        System.out.println("主线程" + Thread.currentThread().getName());
    }
}

5、Start方法做的事情

在线程对象上调用start方法之前,线程是处于新状态,新状态是指有了一个Thread对象,但是还有一个对应的线程。
当调用start方法后,线程就变为了可运行状态,请注意:可运行状态并不是实际运行,是否可以运行还需要JVM的调度。

6、线程的状态转换

这里写图片描述

扫描二维码关注公众号,回复: 8510969 查看本文章
新状态
线程对象已经创建,还没有调用start()方法。
可运行状态
当线程有资格运行,但是调度程序还没有将它选定为运行线程时所处于的状态。当start()方法调用后,线程就进入可以运行状态。
当线程在运行,或者从阻塞、等待、睡眠状态中,也可以返回可运行状态。
运行状态
线程调度程序从可运行池中选择一个线程运行,这个被选中的线程就变为了运行状态。
这个也是线程进入运行状态的唯一方式。
等待/阻塞/睡眠
线程没有条件运行,当满足条件后就可以返回可运行状态。
死亡状态
当线程的run()方法完成时就认为线程死亡了。线程死亡了,但是线程对象有可能还存在,线程死亡不能复生,如果在线程对象上调用start()方法,会抛出异常。

7、线程的优先级和让步

线程是有优先级的,JVM线程调度程序是基于优先级的抢占调用机制,优先级越大的线程,被选中的概率更大,但是不能保证是严格按照优先级的顺序来分别执行的。

线程的让步是暂停当前线程,让调度程序来确定执行其他线程。线程的让步是通过Thread.yield()来实现的。yield()方法是让当前运行线程回到可以运行状态,以让线程调度程序选择其他线程进行执行,但是实际上yield()无法保证达到让步的目的,因为执行yield()的线程还有可能被线程调度程序再次选中。

public class MyRunner implements Runnable {
    public void run() {

        for (int n = 0; n < 100; n++) {
            System.out.println(Thread.currentThread().getName() + ":" + n);

            //打印一次,就让步一次,可以模拟一个线程执行一次的效果
            //也不一定严格的一个线程执行一次
            Thread.yield();
        }

    }
}

public class MyYield {
    public static void main(String[] args) {

        MyRunner runner=new MyRunner();

        Thread t1=new Thread(runner,"线程1");
        Thread t2=new Thread(runner,"线程2");

        t1.start();
        t2.start();
    }
}

输出为


线程10
线程20
线程11
线程21
线程12
线程22
线程13
线程23
线程14
线程24
.......
.......

可以从结果来看,基本上是一个线程执行一次。
但是yield()方法让步可控性较差,一般不使用。

发布了74 篇原创文章 · 获赞 74 · 访问量 5万+

猜你喜欢

转载自blog.csdn.net/chybin500/article/details/78715647