Java多线程编程

1.什么是线程/进程

        线程(Thread)是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。

        进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。在早期面向进程设计的计算机结构中,进程是程序的基本执行实体;在当代面向线程设计的计算机结构中,进程是线程的容器。程序是指令、数据及其组织形式的描述,进程是程序的实体。

        简单来说,进程就是一个运行的程序,而线程就是这个程序中执行不同任务的运作单元。就好像吃饭这个行为相当于程序,具体的吃米饭、吃菜、喝汤则是其中不同的线程。
​        本篇主要介绍线程。

2.如何创建线程

        Java中提供了三种创建方式 1.实现Runnable接口

                                                     2.继承Thread类

                                                     3.通过Callable和Future(很少使用)

        *要注意的是,Java中能够创建线程的其实只有Thread类对象,其他方法也只是基于Thread类对象来创建的。

2.1 实现Runnable接口

        要实现Runnable,我们要先清楚它的调用过程。其中有几个重要的元素和方法:

//可以被重写,在其中调用其他方法、使用其他类、声明变量
public void run();

//创建一个线程的实例化对象,其中threadOb是实现Runnable接口的类实例,threadName是新线程的名称
Thread(Runnable threadOb,String threadName);

//start()是真正启动这条线程的方法
void start();

        第一步要明确该线程想要进行的操作,也就是run()方法内部的内容。run()会先等待start()来调用线程,待到线程被调用以后,才会执行run()中的操作。它并不会主动去执行!

public void run() {
    //线程开始运行
    System.out.println("开始运行:" +  threadName );
    try {
        for(int i = 4; i > 0; i--) {
            System.out.println("当前输出:" + threadName + ", 结果为:" + i);
            //进程睡眠时间:50ms
            Thread.sleep(50);
        }
    }catch (InterruptedException e) {
        System.out.println("线程:" +  threadName + "异常");
    }
    System.out.println("线程:" +  threadName + "运行结束");
}

         第二步则是编写真正启动该线程的部分——start()方法。在创建好线程以后,调用该线程的start()即可自动运行run()中的内容。此处我们在重写的start()中,根据当前类(包含了run())创建了一个新的线程,最后调用Thread中的start()方法启动线程。

public void start () {
    //线程准备就绪,准备运行
    System.out.println("准备运行:" +  threadName );
    if (thread == null) {
        //创建新线程,this意为将该对象放入线程中
        thread = new Thread(this, threadName);
        //线程开始运行
        thread.start();
    }
}

         最后一步就是将Runnable接口实现类实例化,创建新线程并且启动,总而执行run()中的内容。

class RunnableDemo implements Runnable{   
    //Runnable实现类的有参构造器,传入一个name参数来作为threadName进程名
    RunnableDemo(String name) {
        threadName = name;
        //正在创建线程
        System.out.println("正在创建:" +  threadName );
    }
}

//创建实例化对象——创建了新的Thread类“线程1”
RunnableDemo R1 = new RunnableDemo("线程1");
//调用start()方法启动线程
R1.start();

        再强调一下!Runnable接口的实现类并不是线程,它只是重写了run()方法。必须将它的实例对象放入一个新线程new Thread(R1),才可以调用start()方法启动线程!

        最后整体演示一下Runnable接口实现多线程的过程: 

class RunnableDemo implements Runnable {
    //通过实现Runnable接口创建多线程

    //创建一个线程Thread类
    private Thread thread;
    private String threadName;

    //类的有参构造器,传入一个name参数来作为threadName进程名
    RunnableDemo( String name) {
        threadName = name;
        //正在创建线程
        System.out.println("正在创建:" +  threadName );
    }

    public void run() {
        //线程开始运行
        System.out.println("开始运行:" +  threadName );
        try {
            for(int i = 4; i > 0; i--) {
                System.out.println("当前输出:" + threadName + ", 结果为:" + i);
                //进程睡眠时间:50ms
                Thread.sleep(50);
            }
        }catch (InterruptedException e) {
            System.out.println("线程:" +  threadName + "异常");
        }
        System.out.println("线程:" +  threadName + "运行结束");
    }

    public void start () {
        //线程准备就绪,准备运行
        System.out.println("准备运行:" +  threadName );
        if (thread == null) {
            //创建新线程
            thread = new Thread (this, threadName);
            //线程开始运行
            thread.start ();
        }
    }
}

public class TestThread {

    public static void main(String args[]) {
        //创建线程1
        RunnableDemo R1 = new RunnableDemo( "线程1");
        //线程启动
        R1.start();
        
        RunnableDemo R2 = new RunnableDemo( "线程2");
        R2.start();
    }
}

         运行结果如下:

正在创建:线程1
准备运行:线程1
正在创建:线程2
准备运行:线程2
开始运行:线程1
开始运行:线程2
当前输出:线程2, 结果为:4
当前输出:线程1, 结果为:4
当前输出:线程1, 结果为:3
当前输出:线程2, 结果为:3
当前输出:线程1, 结果为:2
当前输出:线程2, 结果为:2
当前输出:线程1, 结果为:1
当前输出:线程2, 结果为:1
线程:线程1运行结束
线程:线程2运行结束

2.2 继承Thread类

        通过创建一个类来继承Thread类,来创建新的线程。这种方法和实现Runnable接口基本上是相同的,本质上就是实现Runnable接口的一个实例。

        直接上一个例子,大伙看看就好。

class ThreadDemo extends Thread {
    //通过实现继承Thread创建多线程

    //创建一个线程Thread类
    private Thread thread;
    private String threadName;

    //类的有参构造器,传入一个name参数来作为threadName进程名
    ThreadDemo(String name) {
        threadName = name;
        //正在创建线程
        System.out.println("正在创建:" +  threadName );
    }

    public void run() {
        //线程开始运行
        System.out.println("开始运行:" +  threadName );
        try {
            for(int i = 4; i > 0; i--) {
                System.out.println("当前输出:" + threadName + ", 结果为:" + i);
                //进程睡眠时间:50ms
                Thread.sleep(50);
            }
        }catch (InterruptedException e) {
            System.out.println("线程:" +  threadName + "异常");
        }
        System.out.println("线程:" +  threadName + "运行结束");
    }

    public void start () {
        //线程准备就绪,准备运行
        System.out.println("准备运行:" +  threadName );
        if (thread == null) {
            //创建新线程
            thread = new Thread (this, threadName);
            //线程开始运行
            thread.start();
        }
    }
}

public class TestThread {

    public static void main(String args[]) {
        //创建线程1
        ThreadDemo T1 = new ThreadDemo( "线程1");
        //线程启动
        T1.start();

        ThreadDemo T2 = new ThreadDemo( "线程2");
        T2.start();
    }
}

3.线程中的方法

        除了我们上面用到过的start()和run(),Thread类其实还包含很多方法。

//改变线程名称,使之与参数 name 相同
public final void setName(String name);

//更改线程的优先级
public final void setPriority(int priority);

//将该线程标记为守护线程或用户线程
public final void setDaemon(boolean on);

//等待该线程终止的时间最长为 millis 毫秒
public final void join(long millisec);

//中断线程
public void interrupt();

//测试线程是否处于活动状态
public final boolean isAlive();

         还有一些静态方法,其中就有我们最常用的休眠方法sleep()。

//暂停当前正在执行的线程对象,并执行其他线程
public static void yield();

//在指定的毫秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度程序精度和准确性的影响
public static void sleep(long millisec);

//当且仅当当前线程在指定的对象上保持监视器锁时,才返回 true
public static boolean holdsLock(Object x);

//返回对当前正在执行的线程对象的引用
public static Thread currentThread();

//将当前线程的堆栈跟踪打印至标准错误流
public static void dumpStack();

        需要特别提一下的是,setDaemon()方法中提到了一个概念叫做“守护线程”与“用户线程”,这里我们详细解释一下。

        只要有一个线程没有退出(执行完毕),JVM进程就不会退出,因此JVM的退出条件就是所有线程都结束。但在具体业务逻辑中,会有一些必须要无限循环的线程,例如每运行一小时就提示一次“您已使用一小时,请注意护眼”。

while(true){
    System.out.println("您已使用一小时,请注意护眼");
    try{
        //休眠时间为一小时
        Thread.sleep(3600000);
    } catch(InterruptedException e){
        break;
    }
}

        这个线程是无法自行结束的,那JVM岂不是永远无法退出了?这时就要运用到“守护线程”了。在不使用setDaemon()方法来指定线程属性时,默认为false“用户线程”,在用户线程没有结束以前,JVM都不会退出。但如果指定该线程为“守护线程”,所有非守护线程执行完毕后,不管JVM进程中还有没有运行中的守护线程,都会直接结束。

        所以对于一些类似于心跳检测、事件监听、GC(垃圾回收)等独立于用户线程之外的线程,我们会将它设置为守护线程。

Thread t = new Thread();
//设置true为守护进程,false为用户进程,默认为false
t.setDaemon(true);
t.start();
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值