Java教程

多线程基础知识你都忘了吧,进来看看呗!

本文主要是介绍多线程基础知识你都忘了吧,进来看看呗!,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

好久没有写博客,今天捞一捞多线程

废话不多说,开始搞!

1.线程的三种方式

1.1.继承Thread

第一种方式是通过类继承Thread,实现run方法

然后创建对象调用start方法,会自动执行run方法里面的代码

代码例子如下:

public class ThreadTest {
    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        myThread.start();
    }
    static class MyThread extends Thread{
        @Override
        public void run(){
            for (int i = 0; i < 100; i++) {
                System.out.println("输出打印:"+i);
            }
        }
    }
}
复制代码

1.2.实现Runnable接口

第二种方式是通过实现Runnable接口

这种方式,需要我们创建两个对象, new Thread(), 还有实现 Runnable的 类

调用start方法,同样会自动调用run方法里面的代码

代码例子如下:

public class RunnableTest {
    public static void main(String[] args) {
        Thread thread = new Thread(new MyRunnable());
        thread.start();
    }
    static class MyRunnable implements Runnable{
        @Override
        public void run() {
            for (int i = 0; i < 100; i++) {
                System.out.println("输出打印:"+i);
            }
        }
    }
}
复制代码

1.3.实现Callable接口

第三种方式是实现Callable接口

这种比较厉害,能够获取线程执行完毕返回的结果.

使用的时候,我们需要创建一个 FutureTask对象来包装我们我们实现类,

然后把这个对象传到Thread的构造函数中,同样是调用start()方法

内部这次调用的是call方法

public class CallableTest {
    public static void main(String[] args) {
        //FutureTask包装我们的任务,FutureTask可以用于获取执行直接
        FutureTask<Integer> task = new FutureTask<>(new MyCallable());
        Thread thread = new Thread(task);
        thread.start();
        try {
            Integer result = task.get();
            System.out.println(result);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
    static class MyCallable implements Callable<Integer>{

        @Override
        public Integer call() throws Exception {
            int num = 0;
            for (int i = 0; i <= 100; i++) {
                  num +=i;
            }
            return num;
        }
    }
}
复制代码

2.用户线程和守护线程

用户线程执行完毕之后,守护线程就停止执行

设置代码:setDaemon()

下面我创建了两个线程(注意:我使用的是Lambad表达式)

第一个线程设置为守护线程,遍历0到100之间的数,每遍历一次睡眠200毫秒

第二线程默认为用户线程,遍历0到100之间的数,每遍历一次睡眠50毫秒

显然是第二个线程执行完毕先

通过执行代码可以发现第二个线程用户线程执行完毕之后,第一个线程守护线程就停止执行了

public class DaemonTest {
    public static void main(String[] args) {
        
        //守护线程
        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 100; i++) {
                try {
                    Thread.sleep(200);
                    System.out.println("t1:" + i);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        t1.setDaemon(true);  //设置守护线程
        t1.start();
        
        
        //用户线程
        Thread t2 = new Thread(() -> {
            for (int i = 0; i < 100; i++) {
                try {
                    Thread.sleep(50);
                    System.out.println("t2:" + i);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            }
        });
        t2.start();
        
    }
}
复制代码

3.线程的优先级

线程的切换是由线程调度控制的,我们无法通过代码来进行干涉

但是我们可以通过提高线程的优先级来最大程度的改善线程获取时间片的几率

一个CPU内核在同一个时间内,只能执行一个线程

所以各个线程都在抢夺CPU时间,谁抢到了时间,就执行谁

例如:

A线程抢到了时间2秒,就执行2秒,然后挂起再去抢夺

B线程抢到了时间3秒,就执行3秒,然后挂起再去抢夺

线程的优先级被划分为10级,值分别为1-10,10最高,线程中提供了3个常量表示最低,最高,以及默认的优先级

Thread.MIN_PRIORITY 1   // 最小等级
Thread.MAX-PRIORITY 10  // 最大等级
Thread.NORM_PRIOIRY 5   // 默认等级
void  setPrioriy(int prioriy )  // 设置线程等级
复制代码

创建两个线程

第一个线程等级设置为 1

第二个线程等级设置为 10

通过执行代码,我们发现第二个线程抢到时间的概率是比第一个线程的概率高的

public class DaemonTest {
    public static void main(String[] args) {
        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 10000; i++) {
                System.out.println("t1:"+i);
            }
        });
        t1.setPriority(1);//设置优先级为 1,那么抢夺到资源的概率就小
        t1.start();
        
        Thread t2 = new Thread(() -> {
            for (int i = 0; i < 10000; i++) {
                System.out.println("t2:"+i);
            }
        });
        t2.setPriority(10);  //抢夺资源的概率高
        t2.start();
    }
}
复制代码

注意:

如果没有设置等级的话,默认的线程等级为 5

4.线程中断

有两个重要的方法

isInterrupted,判断当前的线程是否已经中断了

interrupt中断线程

注意:

stop()中断线程的方法已经被Java抛弃了,它是通过抛异常的方式中断的,有比较大的误操作,导致事务回滚

示例代码创建一个线程

线程里面写了一个无限循环,每次循环,调用isInterrupted方法,判断线程是否已经中断

如果已经中断了,可以去执行其他的代码逻辑

执行线程,睡眠2秒之后,调用interrupt方法,中断线程

public class DaemonTest {
    public static void main(String[] args) {
        Thread t1 = new Thread(() -> {
            while (true){
                boolean flag = Thread.currentThread().isInterrupted();
                if (flag){
                    //如果线程中断了,你可以执行某些代码
                }else{
                    System.out.println("线程执行中.......");
                }
            }
        });
        t1.start();
        try {
            //2秒之后中断线程
            Thread.sleep(2000);
            t1.interrupt();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }
}
复制代码

5.线程的生命周期

线程的生命周期有

NEW 新建对象状态

RUNNABLE 就绪,运行时状态

BLOCKED 加锁等待状态,待其他线程释放锁了执行

WAITING 调用wait等待状态 ,通过notify唤醒

TIMED_WAITING 睡眠状态,睡眠时间到了唤醒

TERMIANTED 销毁状态

在运行过程中,各个线程都在抢夺CPU的时间,没有抢夺到CPU时间在就绪里面,随时准备执行

如果某个线程设置了加锁,或者设置wait,或者设置了sleep,

线程就会进入阻塞状态,等待被唤醒,被唤醒之后,重新进入就绪里面

6.join方法

`join()`可以理解为插队执行,例如我在A线程里面调用B线程的`join()`那么就会先执行B线程后再执行A线程

例如下面我创建了三个线程分别为t1,t2,t3

我的要求是他们的执行顺序必须要是先执行t1,再到t2,然后到t3

这时候就需要使用到了join方法

当t2线程抢夺到了CPU时间,我调用t1线程的join方法插队执行

当t3线程抢夺到了CPU时间,我调用t2线程的join方法插队执行

public class DaemonTest {
    public static void main(String[] args) {
        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                System.out.println("t1: "+ i);
            }
        });

        Thread t2 = new Thread(() -> {
            try {
                t1.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            for (int i = 0; i < 10; i++) {
                System.out.println("t2: "+ i);
            }
        });

        Thread t3 = new Thread(() -> {
            try {
                t2.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            for (int i = 0; i < 10; i++) {
                System.out.println("t3: "+ i);
            }
        });

        t1.start();
        t2.start();
        t3.start();

    }
}
复制代码

7.线程常用API

    start() // 启动线程
    getID() // 获取当前线程ID Thread-编号 (编号从0开启)
    getName() // 获取当前线程名称
    stop() // 停止线程(不建议使用)
    getPriority() // 返回线程的优先级
    isAlive() // 测试线程是否处于活动状态
    isDaemon() // 测试线程是否为守护线程
    isInterrupted() // 测试线程是否已经中断
    Thread.currentThread()  // 获取当前线程对象  
复制代码
这篇关于多线程基础知识你都忘了吧,进来看看呗!的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!