【Java】多线程知识总结

一、多线程简介

        介绍多线程之前要介绍线程,介绍线程则离不开进程。

     进程 :是一个正在执行中的程序,每一个进程执行都有一个执行顺序,该顺序是一个执行路径,或者叫一个控制单元;

  线程:就是进程中的一个独立控制单元,线程在控制着进程的执行。一个进程中至少有一个进程。

       多线程:一个进程中不只有一个线程。

    为什么要用多线程?

    ①、为了更好的利用cpu的资源,如果只有一个线程,则第二个任务必须等到第一个任务结束后才能进行,如果使用多线程则在主线程执行任务的同时可以执行其他任务,而不需要等待;

    ②、进程之间不能共享数据,线程可以;

    ③、系统创建进程需要为该进程重新分配系统资源,创建线程代价比较小;

    ④、Java语言内置了多线程功能支持,简化了java多线程编程。

二、线程的分类

       Java线程可以分成两类:用户线程和守护线程,守护线程是为用户线程服务的.

       虚拟机必须确保用户线程执行完毕,不用等待守护线程执行完毕。

     

三、线程的创建

      方式一:继承 Thread  重写run()方法

                    输出结果不唯一  "一边听歌" 和 "一遍coding" 顺序可能每次执行结果都不是相同的

//继承Thread类
public class StartThread extends Thread{
    @Override
    //重写Thread类中的run方法
    public void run() {
        for (int i = 0; i < 20; i++) {
            System.out.println("一边听歌");
        }
    }

    public static void main(String[] args){
        StartThread st = new StartThread();
        //使用start启动
        st1.start();
        for (int i = 0; i < 20; i++) {
            System.out.println("一边coding");
        }
    }
}

注意:这里是st.start() 使用该方法后会让CPU去调用st重写的run()方法(不是立即让cpu执行,而是创建了一个线程交给cpu,在cpu空闲的时候去执行该线程),对于main()方法而言,不会等待st的run()方法结束,而是会继续向下执行。

         如果调用的是st.run()则仅仅相当于调用了run()方法而不会去创建一个新的线程让cpu执行,此时会等run()方法执行完后再去执行下面的for循环。

   总之就是调用run()方法就是简单的调用,调用start()会创建一个新的线程让cpu在空闲时刻执行,并不会影响底下的代码执行

如果代码变成了下面这样,会发生什么?

       会等待for循环结束后才去执行下面的代码,每次都是先输出10个"一遍coding"后再输出"一边听歌",所以使用多线程时需要注意代码的顺序,不同的顺序带来完全不同的结果!

    

   方式二、实现Runable接口(需要实现该接口的run()方法)

                需要注意的是该种启动方法需要 new Thread(传入对象).start();因为Runnable接口没有start方法!

public class Web12306 implements Runnable {
    //票数
    private int ticketNums = 99;
    @Override
    public void run() {
        while (true){
            if(ticketNums < 0){
                break;
            }
            System.out.println(Thread.currentThread().getName()+"-->"+ticketNums--);
        }
    }

    public static void main(String[] args) {
        //一份资源
        Web12306 web = new Web12306();

        //多个代理
        new Thread(web,"码农").start();
        new Thread(web,"码神").start();
        new Thread(web,"码皇").start();
    }
}

          对于以上两种创建线程方式,比较推荐实现接口的方式,因为Java中只能单继承,接口可以多实现,并且可以公用一份资源,如上面的代码创建三个线程共同使用了一份ticketNums资源,这在某些场景适用。

   方式三:实现Callable接口(以后补充) 

四、实现Runnable接口的Lambda写法

       使用Lambda表达式可以简化代码,在我看来lambda表达式就是再匿名内部类基础上进行更新,那么就先看看匿名内部类

new Thread(new Runnable(){
            @Override
            public void run() {
                System.out.println("匿名内部类");
            }
        }).start();

        来看看Lambda表达式

       () -> {}         箭头左边的括号为空代表着该接口参数为空的方法,箭头右边是实现该方法。

 new Thread(() -> {
           for(int i=0;i<10;i++){
               System.out.println("I am Lambda!");
           }
        }).start();

五、线程状态

  新生状态: Thread t = new Thread();

  就绪状态:    start();    (进入就绪队列,等待CPU调度进入运行状态)

                     阻塞事件解除,重新进入就绪态

                     调用yield()方法后

                     JVM将cpu从本地线程调用到其他线程 

  运行状态:    一定是从就绪态被CPU调度才会进入改状态 (阻塞状态只能直接到达就绪态

  阻塞状态:  调用sleep()方法  (占用资源,抱着资源睡觉)

                      调用wait()方法    (类似于过马路等待红灯,站在一边不占用资源)

                      调用join()方法   (相当于插队,需要等待该线程结束才能开始其他线程)

                      等待I/O操作

    死亡状态:    代码运行完,正常结束

                        线程调用 stop()方法、destory()方法线程即处于死亡状态。处于死亡状态的线程不具有继续运行的能力。

          不建议使用stop()、destory()方法的原因stop()会立即杀死线程,无法保证原子操作能够顺利完成,存在数据错                                                                                误的风险。无论线程执行到哪里、是否加锁,stop会立即停止线程运行,直接退出。

                                                                              destroy()-----强制终止线程,但该线程不会释放对象锁

           如何正确的停止线程:1.线程正常执行完毕

                                               2.外部干涉,假如flag标志位    

       下面的代码是设置标志位,当main方法中for循环i==5时将flag置为flase即可停止t线程

public class TerminateThread implements Runnable{
    //加入标识,标记线程是否可以运行
    private boolean flag = true;

    @Override
    public void run() {
        //关联标识,true-->运行   false-->终止
        while (flag){
            System.out.println("hello");
        }
    }
    public void terminate(){
        flag = false;
    }

    public static void main(String[] args) {
        TerminateThread t = new TerminateThread();
        new Thread(t).start();

        for(int i=0;i<10;i++){
            if(i==5){
                t.terminate();
            }
        }
    }
}

六、线程的具体方法使用

    1.slepp()方法   (从运行--->阻塞---->运行)

        sleep(时间)  指定当前线程阻塞的毫秒数;

        sleep()方法存在InterruptedException

        sleep时间打倒后线程进入就绪态,等待cpu调度

        每一个对象都有一个锁,sleep()方法不会释放锁

    2.yield()方法   (从运行--->就绪)

        礼让线程,让当前正在执行的线程暂停,记住不是阻塞线程,而是将线程从运行状态-->就绪态,让cpu重新调度

        避免一个资源占用cpu太久,让cpu重新调度

        礼让并不一定会成功,它仅仅是让cpu重新调度

    3.join()方法

        合并/插队  线程在那个线程体中调用该方法,就会在该线程体阻塞

        如下代码,在main方法中先启动一个线程,然后继续向下走进入for循环,当for循环的j==20时,会将t线程插队,只有在t线程执行结束后,main方法才继续向下执行.

public class BlockedJoin01 {

    public static void main(String[] args) {
        Thread t = new Thread(() -> {
            for (int i=0;i<100;i++){
                System.out.println("ttttttttt"+i);
            }
        });
        t.start();

        for(int j=0;j<100;j++){
            if(j==20){
                try {
                    t.join();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("main"+j);
        }

    }
}

      结果如下:

    

七、线程的优先级

        在Java中,每一个线程都有一个优先级,默认是一个线程继承它的父线程的优先级。一个线程的默认优先级为NORM_PRIORITY = 5,设置优先级在启动前,优先级范围是1 ~ 10

        高优先级的线程只是在一定概率上比低优先级线程先执行,并不是一定优先级高的先执行(类似于a买了10000张彩票,b买了1张彩票,显然a获奖概率大于b,但不一定就是a获奖)

        线程调用setPriority(数字)方法设置优先级

八、高级知识

        1.任务定时调度:通过Timer和Timetask,我们可以实现定时启动某个线程

                    java.util.Timer:类似于闹钟功能,本身实现的就是一个线程

                    java.util.TimerTask:一个抽象类,该类实现了Runnable接口

public class TimerTest01 {
    public static void main(String[] args) {
        Timer timer = new Timer();
        //1秒后开始执行,每隔200ms再执行一次
        timer.schedule(new MyTask(),1000,200);
    }
}

class MyTask extends TimerTask{
    @Override
    public void run() {
        for (int i=0;i<10;i++){
            System.out.println("Hello,world!");
        }
    }
}

猜你喜欢

转载自blog.csdn.net/weixin_41963657/article/details/89091334
今日推荐