多线程1(线程的创建+生命周期+线程调度+线程休眠、让步、插队+后台线程)

一(进程)

定义:在操作系统中,每个独立执行的程序都可以称之为一个进程,也就是“正在运行的程序”。

二(线程的创建)

定义:在程序设计中,多线程是指一个应用程序中有多条并发执行的线索,每条线索都被称为一个线程,他们会交替执行,彼此间可以进行通信。

多线程程序的优点:
                          1.提高CPU的利用率
                          2.提高应用程序的响应
                          3.改善程序结构

何时需要多线程?
                     1.程序需要同时执行多个任务的时候,
                     2.需要一些后台程序运行时,
                     3.程序需要实现一些需要等待的任务时,

创建线程的3种方式:

1.Thread类实现多线程:创建一个类继承Threa线程类,重写run()方法(需要当前线程执行什么代码,就把相应的代码放入run方法中),再创建该类的实例对象,调用start()方法启动线程即可。

弊端:无法共享数据;由于java类的单一继承,当前类不可以再继承其他类。

package 多线程;

public class ThreadDemo1 {
    public static void main(String[] args) {
    A t1=new A("Thread1");
    t1.start();
    A t2=new A("Thread2");
    t2.start();
    }

}
class A extends Thread{
    int num=6;
    public A(String name) {
        super(name);
    }

    @Override
    public void run() {
       for (int i=0;i<num;i++){
           System.out.println(Thread.currentThread().getName()+":"+i);
       }
    }
}

currentThread()是Thread类的静态方法,可以获取当前线程对象,getName()获取线程名称。

从输出结果可看出,2个线程对象交替执行了各自重写的run()方法,并打印出各自的输出信息,而不是按照编程顺序先执行完第一个线程方法再执行第二个线程方法,这就说明程序实现了多线程功能。

2.Runable接口实现多线程:创建一个Runable接口的实现类,并重写run()方法;创建Runable接口实现类对象;使用Thread有参构造方法创建线程实例,并将Runable接口实现类的实例作为参数传入;调用start()方法启动线程即可。


实现Runnable接口比继承Thread所具有的优势:
     1.可以避免java中类的单一继承
     2.增强程序的强壮性,代码可以被多个线程共享
     3.线程池只能放实现Runnable接口的线程,不能直接放继承Thread类的线程 

package 多线程;

public class RunableDemo {
    public static void main(String[] args) {
        B b1=new B();//创建Runnable接口的实现类实例
        Thread t1=new Thread(b1);//使用Thread有参构造方法创建线程实例,并将Runable接口实现类的实例作为参数传入
        t1.start();//启动线程
        B b2=new B();
        Thread t2=new Thread(b2);
        t2.start();
    }
}
class B implements Runnable{

int num=6;
    @Override
    public void run() {
        for (int i = 0; i <num ; i++) {
            System.out.println(Thread.currentThread().getName()+":"+i);
        }
    }
}

从输出结果可看出,2个线程对象交替执行了各自重写的run()方法,并打印出各自的输出信息,这就说明通过实现Runnable接口实现了多线程。

3.Callable接口实现多线程:创建一个Callable接口的实现类,重写Callable接口的call()方法;创建Callable接口的实现类对象;通过FutureTask线程结果处理类的有参构造方法来封装Callable接口实现类对象;使用参数为FutureTask线程结果处理类的有参构造方法创建Thread线程实例;调用线程实例的start()方法启动线程。

FutureTask类实现自RunnableFuture接口,RunnableFuture接口继承自Runnable接口

实现Callable接口比Runnable方式的优点?
    1.Callable支持泛型
    2.重写的call()可以有返回值,如果需要返回值则通过FutureTask实例调用get()获取
    3.call()可以抛出异常,被外面的操作捕获,获取更多的异常信息 

package 多线程;

import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;

public class CallableDemo {
    public static void main(String[] args) {
        C c1=new C();
        FutureTask ft1=new FutureTask(c1);
        Thread t1=new Thread(ft1);
        t1.start();

        FutureTask ft2=new FutureTask(c1);
        Thread t2=new Thread(ft2);
        t2.start();
    }
}

class C implements Callable{

    @Override
    public Object call() throws Exception {
        int num=6;
        int i;
        for (i = 0; i <num ; i++) {
            System.out.println(Thread.currentThread().getName()+":"+i);
        }
        return i;
    }
}

事实上,实际开发中大部分的多线程应用都会采用实现Runnable接口或Callable接口的方式来实现多线程。

三(线程的生命周期)

线程具有生命周期,其中包括5中状态:分别是新建、 就绪、 运行、 暂停(包括休眠阻塞等待) 、死亡状态。

   新建状态
                 当使用new关键字创建线程实例后,该线程就处于就绪状态,但是不会执行
   就绪状态
                 当调用.start方法时,该线程处于就绪状态,表示可以执行,但是不一定会
                 立即执行,而是等待CPU分配时间片进程处理
                 就绪状态是不可以直接进入暂停和死亡
   运行状态
                 当cpu为该线程分配时间片,执行该线程的run方法时,就处于运行状态;只有
                 当线程运行起来后,才有资格进入死亡和暂停
   暂停状态
                 当线程调用sleep方法,主动放弃cpu资源,或者在线程运行中发出IO请求(如                             Scanner输入),线程就会进入暂停状态
   死亡状态
                 当线程run方法执行完毕,或者线程抛出未捕获异常或错误,线程就处于死亡状态

 注意:
       1.当前线程创建时,并不会立即执行,需要调用start,使其处于就绪状态。
       2.线程处于就绪状态,也不会立即执行,需要等待cpu分配时间片。
       3.当线程暂停时,会让出占有CPU,当阻塞结束时,线程会进入就绪状态,重新等待cpu,而不是直            接进入运行状态。

四(线程的调度)

程序中的多个线程是并发执行的,并不是同一时刻执行,某个线程若想被执行,必须得到CPU的使用权。Java虚拟机会按照特定的机制为程序中的每个线程分配CPU使用权,这种机制被称作“线程的调度”。

在计算机中,线程调度有分式调度模型和抢占式调度模型。分时调度模型是指让所有的线程轮流获得CPU的使用权,并平均分配每个线程占用的CPU时间片。抢占式调度是指让可运行池中所有就绪状态的线程抢占CPU的使用权,优先级高的线程获取CPU执行权的概率大于优先级低的线程。Java虚拟机默认使用抢占式调度模型。

优先级越高的线程获得CPU执行的机会就越大,但不一定会优先执行。线程的优先级用1-10之间的整数来表示,数字越大代表优先级越高,最小优先级为1,默认优先级为5,最大优先级为10。

setPriority(int new Priority):设置线程的优先级

getPriority():获取线程的优先级 

currentThread():获取当前线程对象

getName():获取线程的名字

getId(): 获取当前线程的唯一标识id 

package 多线程;

public class ThreadDemo2 {
    public static void main(String[] args) {
        Thread t1=new Thread(()->{
            for(int i=0;i<6;i++){
                System.out.println(Thread.currentThread().getName()+":"+i);
            }
        },"优先级为10的线程");
        Thread t2=new Thread(()->{
            for(int j=0;j<6;j++){
                System.out.println(Thread.currentThread().getName()+":"+j);
            }
        },"优先级为2的线程");
        t1.setPriority(10);
        t1.start();
        t2.setPriority(2);
        t2.start();

    }

}

五(线程休眠+线程让步+线程插队)

线程休眠:如果想要人为的控制线程执行顺序,使正在执行的线程暂停,将CPU使用权交给其他线程,这时可以使用静态方法sleep(long millis),该方法可以让当前正在执行的线程暂停millis的时间,进入休眠等待状态,这样其他的线程就可以得到执行的机会。sleep(long millis)方法会抛出InterruptedException异常,因此在调用该方法时应该捕获异常,或者声明抛出异常。

注意:线程类Thread提供了2种线程休眠方法:sleep(long millis)和sleep(long millis,int nanos),这2种方法都带有休眠时间参数,当其他线程终止后并不代表当前休眠的线程会立即执行,而是必须等当前休眠时间结束后,线程才会转换到就绪状态。

package 多线程;

public class SleepDemo {
    public static void main(String[] args) {
        Thread t1=new Thread(()->{
            for (int i = 0; i <6 ; i++) {
                System.out.println(Thread.currentThread().getName()+":"+i);
                if (i==2){
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        });

        Thread t2=new Thread(()->{
            for (int i = 0; i <6 ; i++) {
                System.out.println(Thread.currentThread().getName()+":"+i);
            }
        });
        t1.start();
        t2.start();
    }
}

线程让步:通过yield()来实现,该方法和sleep(long millis)方法类似,都可以让当前正在运行的线程暂停,区别在于yield()方法不会阻塞该线程,它只是将线程转换为就绪状态,让系统的调度器重新调度一次。当某个线程调用yield()方法之后,与当前线程优先级相同或者更高的线程可以获得执行的机会。

通过yield()方法可以实现线程让步,让当前正在运行的线程失去CPU使用权,让系统的调度器重新调度一次,由于java虚拟机默认采用抢占式调度,让所有线程再次争夺CPU使用权,所以在执行线程让步后不能保证立即执行其他线程。

package 多线程;

public class YieldDemo {
    public static void main(String[] args) {
        Thread t1=new D();
        t1.start();
        Thread t2=new D();
        t2.start();
    }
}
class D extends Thread{
    public void run(){
        for (int i = 0; i <6 ; i++) {
            System.out.println(Thread.currentThread().getName()+":"+i);
            if (i ==2) {
                System.out.print("线程让步:");
                Thread.yield();
            }
        }
    }
}

创建2个线程t1和t2,它们的优先级相同。2个线程在循环变量i=2时,都会调用Thread的yield()方法,使当前线程暂停,让2个线程再次争夺CPU使用权,从运行结果可看出,当t1线程输出2时,会做出让步,线程t2获得执行权,同样,线程t2输出2时,也会做出让步,线程t1获得执行权。 

线程插队:当在某个线程中调用其他线程的join()方法时,调用的线程将被阻塞,直到被join()方法加入的线程执行完毕后才能继续运行。

Thread类中除了提供一个无参数的线程插队join()方法外,还提供了带有时间参数的线程插队方法join(long millis)。当执行带有时间参数的join(long millis)进行线程插队时,必须等待插入的线程指定时间过后才能继续执行其他线程。

package 多线程;

public class JoinDemo {
    public static void main(String[] args) throws InterruptedException {
         Thread t=new Thread(new E());
         t.start();
        for (int i = 0; i <6 ; i++) {
            System.out.println(Thread.currentThread().getName()+":"+i);
            if (i == 2) {
                t.join();
            }
        }
    }
}

class E implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i <6 ; i++) {
            System.out.println(Thread.currentThread().getName()+":"+i);
        }
    }
}

在main线程中开启了一个线程t,这2个线程会互相争夺CPU使用权输出语句。当main线程中的循环变量为2时,调用t线程的join()方法,这时,t线程就会“插队”优先执行,并且整个线程执行完毕后才会执行其他线程。从运行结果可以看出,当main线程输出2以后,t线程就开始执行,直到执行完毕才继续执行main线程。

六(后台线程)

对Java程序而言,只要还有一个前台程序在运行,这个进程就不会结束,如果一个进程只有后台程序在运行,这个进程就会结束。新创建的线程默认都是前台线程,如果某个线程对象在启动之前调用了setDaemon(true)方法,该线程就变成后台线程,又称“守护线程”。

若要将某个线程设置为后台线程,则必须在该线程启动之前调用setDaemon(true)方法,否则后台线程设置无效。

package 多线程;

public class DaemonDemo {
    public static void main(String[] args) {
        Thread ros=new Thread(){
            @Override
            public void run() {
                for (int i=0;i<5;i++){
                    System.out.println("ros:jack,我要跳啦!");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println("ros:jack,来守护我吧!");
                System.out.println("ros:扑通!");
            }
        };
        Thread jack=new Thread(){
            @Override
            public void run(){
                while (true){
                    System.out.println("jack:我会永远守护你!");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        };
        ros.start();
        /*
        先设置为守护线程,再启动
         */
        jack.setDaemon(true);
        jack.start();
    }
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值