多线程------实际篇

  目录  

一.多线程的创建

1.如何实现多线程呢?

2.多线程程序实现的方式

方式1  (参考Thread类)

run()和start()方法哪个是启动线程的方法?线程能不能多次启动?为什么要重写run方法?

方式2  (实现Runnable接口)

方式3  (实现 Callable 接口)

二.多线程中常用到的方法

1.Thread类的基本获取和设置方法

2.线程中的方法(均由线程对象调用)

三.关于多线程的困扰问题

1.有的时候我们给线程设置了指定的优先级,但是该线程并不是按照优先级高的线程执行,那是为什么呢?

2.线程默认的优先级是多少呢?

3.礼让线程为什么不是理想状态下的一个线程执行一次?

4.用户线程和守护线程的区别以及守护线程的适用场景?


多线程小编已经在上一篇文章中详细介绍过了!接下来就看看多线程如何创建以及其中的方法等等!

一.多线程的创建

1.如何实现多线程呢?

由于线程是依赖进程而存在的,所以我们应该先创建一个进程(JVM)出来。而进程是由系统创建的,所以我们应该  去调用系统功能创建一个进程。  但是Java是不能直接调用系统的,所以我们没有办法直接实现多线程程序。但是, Java可以去调用C/C++写好的程序来实现多线程程序。由C/C++去调用系统功能创建进程,然后由Java去调用这样的东西,最后  提供一些类供我们使用,   就可以  实现多线程程序  了。

2.多线程程序实现的方式

方式1  (参考Thread类)

  • 定义一个类,继承 Thread类
  • 重写 Thread 类中的run方法
  • 创建你定义的这个类对象,调用start()方法启动线程

那么问题来啦!接招吧~~~~

run()和start()方法哪个是启动线程的方法?线程能不能多次启动?为什么要重写run方法?

        可不要觉得这都so easy!也会有朋友不懂这些问题的!首先启动线程使用的不是run方法,而是  start方法.  直接用线程.run()是无法成功启动线程的!咱们创建好线程后便调用start方法启动线程,随后由Java 虚拟机调用该线程的 run 方法. 还有针对  同一个线程对象不要重复调用 start().  注意是线程对象!同一个线程不同线程对象的话是可以多次启动的!那么为什么非要重写run方法?如果我们不重写Thread类中的run方法,run()则将执行Thread类自身原有的方法,并且由于Thread类run()的实现为空,因此不会获得任何输出。如果你想  run方法里面实现你想实现的功能,  拿当然就得重写啦!

public class MyThread extends Thread {     //继承Thread类
    //重写run方法
    @Override
    public void run() {
        System.out.println("具体需要实现的功能!");
    }
}


public class MyTest {
    public static void main(String[] args) {
        MyThread th = new MyThread();      //创建对象
        //th.run();     //这样不是开启了线程,你这是创建了一个对象,调用了run方法,没有新的线程开启。
        th.start();     //调用start()方法启动线程

        // th.start(); 同一个线程对象不要重复调用 start()
        MyThread th2 = new MyThread();     //新的线程对象
        th2.start();
    }
}

方式2  (实现Runnable接口)

  • 定义一个类,实现  Runnable 接口, 重写其中的run方法
  • 创建定义的这个类的对象
  • new Thread 把这个类的对象作为参数,传过来
  • 调用start()方法启动

这个方法的  优点在于扩展性强,  实现一个接口的同时还可以再去继承其他类. 可以避免由于Java单继承带来的局限性.

public class MyRunnable implements Runnable {     //实现Runnable 接口
    //重写run方法
    @Override
    public void run() {
            System.out.println("具体实现的功能!");
    }
}


public class MyTest {
    public static void main(String[] args) {
        MyRunnable myRunnable = new MyRunnable();    //创建对象
        Thread th = new Thread(myRunnable);          //new Thread 把这个类的对象作为参数,传过来
        th.start();                                  //调用start()方法启动

    }
}

方式3  (实现 Callable 接口)

  • 创建一个类实现Callable 接口
  • 创建一个FutureTask类将Callable接口的子类对象作为参数传进去
  • 创建Thread类,将FutureTask对象作为参数传进去
  • 开启线程

注意:  实现 Callable 接口相较于实现 Runnable 接口的方式,  方法可以有返回值,并且可以抛出异常.    执行 Callable 方式,  需要 FutureTask 实现类的支持,  用于接收运算结果.  FutureTask 是  Future 接口的实现类.并且Runnable 他的run方法,没有返回值,也不能抛出异常. 而Callable 他的call方法,有返回值,还可以抛出异常. 如果说咱们想拿到线程执行完后返回的结果,就可以用 Callable 任务.也就是用第三种方式来创建线程哈!

public class MyCallable implements Callable<Integer> {       //实现Callable接口
    private int num;

    public MyCallable(int num) {

        this.num = num;
    }

    //call方法也是线程将来执行的方法
    @Override
    public Integer call() throws Exception {
        int sum = 0;
        for (int i = 1; i <= num; i++) {
            sum += i;
        }
        return sum;
    }
}



public class MyTest {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
     
        MyCallable myCallable = new MyCallable(100);
        //可使用 FutureTask 包装 Callable 或 Runnable 对象。因为 FutureTask 实现了 Runnable,所以可将 FutureTask 提交给 Executor 执行。
        FutureTask<Integer> futureTask = new FutureTask<>(myCallable);        //创建一个FutureTask类将Callable接口的子类对象作为参数传进去

        Thread th = new Thread(futureTask);            //创建Thread类,将FutureTask对象作为参数传进去
        th.start();          //开启线程

        Integer integer = futureTask.get();
        System.out.println(integer);

        //获取子线程执行完之后的结果
        Integer integer1 = futureTask2.get();
        System.out.println(integer1);

    }
}

二.多线程中常用到的方法

1.Thread类的基本获取和设置方法

  • public final String getName()                                              //获取线程名称
  • public final void setName(String name)                            //设置线程名称(其实通过构造方法也可以给线程起名字)
  • public static Thread currentThread()                                 //获取当前执行的线程并返回对当前正在执行的线程对象的引用

2.线程中的方法(均由线程对象调用)

  • public final int getPriority()                                           //获取线程的优先级
  • public final void setPriority(int newPriority)               //设置线程的优先级
  • public static void sleep(long millis)                             //线程休眠,让当前线程处于休眠状态
  • public final void join()                                                    //加入线程,也就是等待该线程执行完毕了以后,其他线程才能再次执行.  注意: 在线程启动之后,在调用该方法.可以把多个线程并发执行,变为串行
  • public static void yield()                                                //礼让线程也就是暂停当前正在执行的线程对象,并执行其他线程。
  • public final void setDaemon(boolean on)                    //把该线程标记为守护线程.  该方法必须在启动线程前调用
  • public final void stop()                                                  //中断线程,停止线程的运行
  • public void interrupt():                                                  //当线程调用wait(),sleep(long time)方法的时候处于阻塞状态,可以通过这个方法清除阻塞
public class MyTest {
    public static void main(String[] args) throws InterruptedException {
        System.out.println("主线程中代码AAA");
        Thread thObj = Thread.currentThread();         //currentThread();获取当前执行的线程对象
        thObj.setName("主线程");                        //设置线程名称
        System.out.println(thObj.getName());           //获取线程名称
        //开启一个子线程
        MyThread th1 = new MyThread();
        th1.setName("范冰冰");

        th1.setPriority(Thread.MAX_PRIORITY);           //设置线程的优先级
        int priority = th1.getPriority();               //获取线程的优先级
        System.out.println("th1的优先级:" + priority);
        th1.start();

        //再开启一个子线程
        MyThread th2 = new MyThread();
        th2.setName("刘亦菲");
        th2.setPriority(Thread.MIN_PRIORITY);
        int priority1 = th2.getPriority();
        System.out.println("th2的优先级:" + priority1);
        th2.start();

        th2.stop();        //强制停止线程

        //让当前线程休眠
        Thread.sleep(1000 * 3);          //休眠之后的代码不再执行

}
    }


public class MyThread extends Thread {
    public MyThread() {

    }

    @Override
    public void run() {

        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName() + "-子线程执行的代码:" + i);
        }
    }
}

加入线程join()方法演示 

public class MyTest {
    public static void main(String[] args) throws InterruptedException {
        MyThread th1 = new MyThread("刘备");
        MyThread th2 = new MyThread("关羽");
        MyThread th3 = new MyThread("张飞");
        th1.start();
        th1.join();
        th2.start();
        th2.join();
        th3.start();
        th3.join();

        //join()在线程开启之后,调用,可以把多个线程并发执行,变为串行。

    }
}


public class MyThread extends Thread {
    public MyThread() {
    }
    public MyThread(String name) {
        super(name);
    }
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println(this.getName() + "-子线程执行的代码:" + i);
        }
    }
}


礼让线程yield()方法演示 

public class MyTest {
    public static void main(String[] args) throws InterruptedException {
        MyThread th1 = new MyThread("刘备");
        MyThread th2 = new MyThread("关羽");
        th1.start();
        th2.start();
    }
}


public class MyThread extends Thread {
    public MyThread() {
    }

    public MyThread(String name) {
        super(name);
    }

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println(this.getName() + "-子线程执行的代码:" + i);
            Thread.yield();     //线程礼让
        }
    }
}

设置守护线程案例演示 

public class MyTest {
    public static void main(String[] args) throws InterruptedException {
        Thread main = Thread.currentThread();
        main.setName("刘备");
        for (int i = 0; i < 10; i++) {
            System.out.println(main.getName() + "i");
        }
        MyThread th2 = new MyThread("关羽");
        MyThread th3 = new MyThread("张飞");
        //设置守护线程       当用户线程死亡后,守护线程也要立即死亡
        th2.setDaemon(true);
        th2.start();
        //设置守护线程
        th3.setDaemon(true);
        th3.start();

    }
}


public class MyThread extends Thread {
    public MyThread() {
    }

    public MyThread(String name) {
        super(name);
    }

    @Override
    public void run() {
        for (int i = 0; i < 1000; i++) {
            System.out.println(this.getName() + "-子线程执行的代码:" + i);
        }
    }
}

清楚阻塞状态interrupt()演示 

public class MyTest {
    public static void main(String[] args) throws InterruptedException {
        MyThread th = new MyThread("线程A");
        th.start();
        th.interrupt(); //清除线程阻塞的状态
    }
}



public class MyThread extends Thread {
    public MyThread() {
    }

    public MyThread(String name) {
        super(name);
    }

    @Override
    public void run() {
        try {
            Thread.sleep(5 * 1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        for (int i = 0; i < 1000; i++) {
            System.out.println(this.getName() + "-子线程执行的代码:" + i);
        }
    }
}

三.关于多线程的困扰问题

1.有的时候我们给线程设置了指定的优先级,但是该线程并不是按照优先级高的线程执行,那是为什么呢?

        因为线程的优先级的大小  仅仅表示这个线程被CPU执行的概率增大  了.但是我们都知道多线程  具有随机性,  所以有的时候一两次的运行说明不了问题的!

2.线程默认的优先级是多少呢?

        如果事先并没有给线程设置优先级,而java采用的又是抢占式调度模型,那么这个线程肯定存在一个默认的优先级.通过getPriority()方法获取线程的优先级.就可以发现线程的  默认优先级是5.  其次线程的  优先级范围是 1---10.  这就不用说啦,肯定是10 的优先级最大咯!

3.礼让线程为什么不是理想状态下的一个线程执行一次?

        按照我们的想法,这个礼让应该是一个线程执行一次,但是通过测试就发现该效果并不明显.礼让线程是要暂停当前正在执行的线程,这个  暂停的时间是相当短 的,如果在这个线程暂停完毕以后,其他的线程还没有抢占到CPU的执行权,那么这个时候这个线程就会再次和其他线程抢占CPU的执行权.

4.用户线程和守护线程的区别以及守护线程的适用场景?

        用户线程和守护线程都是线程,区别是Java虚拟机在所有用户线程dead后,程序就会结束.而不管是否还有守护线程还在运行,若守护线程还在运行,也会马上结束. 也就是说  当用户线程dead后,守护线程也要立即dead.  由两者的区别及dead时间点可知,守护线程不适合用于输入输出或计算等操作,因为用户线程执行完毕,程序就dead了,  适用于辅助用户线程的场景,  如JVM的垃圾回收,内存管理都是守护线程,还有就是在做数据库应用的时候,使用的数据库连接池,连接池本身也包含着很多后台线程,监听连接个数、超时时间、状态等.


(小编也在努力学习更多哟!以后再慢慢分享的啦!)

希望对友友们有所帮助!!!!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

naoguaziteng

谢谢友友的打赏!一起努力吧

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值