欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

chapter11 多线程(一)

程序员文章站 2022-07-14 21:07:11
...

多线程的介绍

进程:一个正在运行的程序。
线程:是一个进程中的一个执行单元(执行路径)。
比如360安全卫士,我点击了360安全卫士,那么这就是一个正在运行的程序(即进程)。然后我点击清理垃圾,那么清理垃圾就是这个正在运行的程序中的一个执行单元(线程),我再点击病毒扫描、电脑加速,那么这个正在运行的程序就不止一个执行单元。
因此我们可以得出:一个进程中至少有一个线程

多线程的创建方式

多线程通过继承实现

基本步骤:
①新建一个类ThreadTest,使得这个类继承于Thread这个类
②在这个ThreadTest类中,重写Thread的run方法,从而设置线程的执行的内容
③在主函数中新建ThreadTest对象为test
调用test这个对象的start方法,从而启动当前的线程,然后会调用当前线程的run方法

代码实例:

//步骤一:新建一个类,从而使这个类继承于Thread这个类
class ThreadTest extends  Thread{
    @Override
    //步骤二:重写方法run,从而设置线程的方法体
    public void run(){
        for(int i = 0; i<10; i++){
            if(i % 2 == 0){
                //调用Thread中的cuttentThread()方法中的getName方法,可以获得当前线程的名字
                System.out.println(Thread.currentThread().getName()+": "+i);
            }
        }
    }
}
public class DemoThread2 {
    public static void main(String[] args){
        //步骤三:新建这个子类的对象
        ThreadTest test = new ThreadTest();
//步骤四:调用子类的start方法,从而这个方法会调用Thread方法的run方法,执行线程的方法体
//注意start方法的作用有两个:1)启动当前这个对象的线程 2)调用当前线程的run方法
        test.start();
      /**
       *如果要再开启一个线程,那么就不可以再用test这个对象再调用start方
       *法,而是再新建一个对象,然后这个新对象再调用start方法,从而开启另
       *一个线程,否则会抛出IllegalThreadStateException异常
       */
        ThreadTest test1 = new ThreadTest();
        test1.start();
        /**
        *如果直接执行run方法,那么就没有启动线程,那么当前只有一个线程,
        *就是主线程,可以通过Thread中的getName更好的判断
        */
        // test.run();
        //这个步骤是main线程中的
        for(int i = 0; i<10; i++){
            if(i%2 ==0){
                System.out.println(Thread.currentThread().getName()+": "+ i+" * main *");
            }
        }
    }
}

运行结果:
chapter11 多线程(一)
运行结果可能为前面全是main线程,然后才是Thread-1线程,最后是Thread-0线程或者只要在运行几次,就会发现运行结果和之前几次的结果是不一样的,这些是正常的。

需要注意的问题有几个:
问题一:如果在步骤④中没有调用start方法,而是直接调用run方法,那么并没有启动当前这个线程(即test那一条线程),在这个代码中只有一个线程,即为主线程

实例代码:

//步骤一:新建一个类,从而使这个类继承于Thread这个类
class ThreadTest extends  Thread{
    @Override
    //步骤二:重写方法run,从而设置线程的方法体
    public void run(){
        for(int i = 0; i<10; i++){
            if(i % 2 == 0){
                //调用Thread中的cuttentThread()方法中的getName方法,可以获得当前线程的名字
                System.out.println(Thread.currentThread().getName()+": "+i);
            }
        }
    }
}
public class DemoThread2 {
    public static void main(String[] args){
        //步骤三:新建这个子类的对象
        ThreadTest test = new ThreadTest();
        /**
        *直接执行run方法,那么就没有启动线程,那么当前只有一个线程,
        *就是主线程,可以通过Thread中的getName更好的判断
        */
        test.run();
        //这个步骤是main线程中的
        for(int i = 0; i<10; i++){
            if(i%2 ==0){
                System.out.println(Thread.currentThread().getName()+": "+ i+" * main *");
            }
        }
    }
}

结果为:
chapter11 多线程(一)
从结果可以直到,如果直接调用方法run,那么在整个代码中只有一个主线程,并没有启动另一条线程。因此必须要通过调用start方法,从而启动当前线程,然后再调用run方法

问题二:如果要再启动另外一条线程,那么就需要再新建ThreadTest这个子类对象,然后再调用start方法启动线程。如果依旧用上面的test这个子类对象再一次调用方法start,那么就会发生报错,抛出IlegalThreadStateException。例子如下:
chapter11 多线程(一)

多线程通过实现Runnable接口来实现

基本步骤:
①新建一个子类ThreadTest,使得这个子类实现Runnable接口。
②在这个子类中,实现接口的run方法,从而设置了线程的执行的内容
③在主函数中,新建这个子类ThreadTest的对象为test
新建Thread类的对象thread,并且它的构造方法是以test这个对象作为参数。即Thread thread = new Thread(Runnable p),本来参数为Runnable接口,但是以它的子类test作为参数也是没有问题的,因为发生了向上转型嘛。
⑤通过调用thread对象的start方法,从而启动线程,调用run方法。

代码实例:

//步骤1、新建一个类,作为Runnable接口的子类
class ThreadTest implements Runnable{

    @Override
    //步骤2、在这个子类中重写Runnable接口的run方法,作为线程的方法体
    public void run() {
        for(int i = 0; i<10; i++){
            if(i % 2 == 0){
                //Thread.currentThread().getName()表示当前线程的名字
                System.out.println(Thread.currentThread().getName()+": "+i);
            }
        }
    }
}
public class DemoThread {
    public static void main(String[] args){
        //步骤3、新建上面Runnable子类对象
        ThreadTest test = new ThreadTest();
        //步骤4、新建Thread类对象,并且它的构造方法的参数是Runnable子类对象,即test
        Thread thread = new Thread(test);
//步骤5、通过thread这个Thread类对象,调用start方法,启动线程,从而调用方法run,执行线程
        thread.start();
      /**
//如果要在启动另一个线程,那么就要在新建Thread类对象,而不用在新建Runnable子类对象,向上面所说的
        Thread thread1 = new Thread(test);
        thread1.setName("线程2");//设置线程名
        thread1.start();
       */
        for(int i = 0; i<10; i++){
            if(i % 2 != 0){
                System.out.println("main " + i);
            }
        }
    }
}

结果:
chapter11 多线程(一)
结果并不一定是上面那样,在运行几次之后,就会发现结果和当前的结果不同,这是正常的。

这里注意的问题和上面提到的注意问题一样,Thread类对象不可以直接调用run方法,否则就没有办法启动线程,整个代码中只有一个主线程
其次,如果要再一次启动线程,那么就不可以再通过当前的thread对象进行调用start方法,而是新建Thread类对象,再调用start方法,否则会发生报错,抛出IllegalThreadStateException错误

线程的常用方法

①start():导致该线程开始执行;java虚拟机调用这个线程的 run方法

②run():在多线程的两种创建方式中,都进行了run方法的重写,从而设置了线程的执行内容。

③setName():设置当前线程的名字。当然也可以通过构造方法进行设置,不过这一种构造方式用在通过继承实现多线程的方法中,因为在通过接口的方式实现多线程,它的参数是接口子类。
线程通过构造方法命名当前线程名字,要在子类中设置带参的构造方法,否则会发生报错

//ThreadTest这个子类继承于Thread类的构造方法
public  ThreadTest(String string){
        super(string);
    }

④getName():获取线程的名字。

⑤currentThread():静态方法,返回当前代码执行的线程。

⑥stop():已过时。强制结束当前这个线程。

⑦sleep(long milis):方法体 :
public static void sleep(long millis) throws InterruptedException
当前正在执行的线程休眠(暂停执行)为指定的毫秒数,根据精度和系统定时器和调度的准确性。线程不失去任何监视器的所有权。
参数
millis -睡在毫秒的时间长度,单位是毫秒
注意后面还声明了异常,那么在使用的时候,一定要用try/catch捕获并处理异常,否则会有错误提示,提示我们surround with try/catch

⑧join():在线程a中调用线程b的join方法时,那么线程a会被阻塞,直到线程b执行完毕之后,线程a才结束阻塞,继续执行线程a。注意后面还声明了异常,那么在使用的时候,一定要用try/catch捕获并处理异常,否则会有错误提示,提示我们surround with try/catch
代码:

public final void join() throws InterruptedException {//这里声明了异常
        join(0);
    }

实例:

class ThreadTest2 extends Thread{
    @Override
    public void run(){
        for(int i = 0; i<10; i++){
            if(i%2 == 0){
/**
 *Thread.currentThread().getName()输出当前线程的名字,如果时通过继承实现
 *线程,那么这里可以写getName(),这样写就相当于this.getName(),但是如果通
 *过接口实现的话,必须是Thread.currentThread().getName()这样写
 */
                System.out.println(Thread.currentThread().getName()+" " +i);
            }
        }
    }
}

public class DemoThread3 {
    public static void main(String[] args){
        ThreadTest2 test2 = new ThreadTest2();
        test2.setName("线程一");//设置线程名
        test2.start();//启动线程,然后调用Thread中的run方法
        Thread.currentThread().setName("主线程");//设置主线程的名字
        for(int i = 0; i<10; i++){
            if(i % 2 == 0){
                //getName()获取当前线程得名字
                System.out.println(Thread.currentThread().getName()+" " + i);
            }
            if(i == 4){
        //调用方法join时会抛出异常,因此需要用try/catch进行捕获处理异常
                try {
         /**
          *在主线程中调用线程2的join方法,从而阻塞主线程,只有线程2执行完
          *毕之后,主线程才会阻塞结束,才会继续执行主线程
          */
                    test2.join();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

结果:
chapter11 多线程(一)
当然结果并不一定是这样,但是肯定的是,i = 4时,主线程阻塞,之后必然时线程一,线程一结束之后,才会继续执行主线程。可能这里的数字10太小,可以设置得大点,看的更加清楚。

⑨isAlive():判断线程是否还活着,如果返回的是false,表明这个线程死了,也就是已经结束了,否则还活着,一直在运行着。

多线程的优先级

线程的优先级:
MAX_PRIORITY = 10;
MIN_PRIORITY = 1;
NORM_PRIORITY = 5;
方法:
1)getPriority():获取当前线程的优先级
2)setPriority():设置当前线程的优先级

注意高优先级的线程会抢占低优先级cpu的执行权(即高优先级的线程有更大的机率会先执行,但不是说高优先级的线程执行完毕之后低优先级线程才会执行),如果没有设置优先级,那么就默认为优先级为5,那么此时随机进行线程
如下面的代码:

class ThreadTest4 extends  Thread{
    @Override
    public void run(){
        for(int i = 0; i<10; i++){
            if(i%2 == 0){
                System.out.println("thread "+i);
            }
        }
    }
}

public class DemoThread4 {
    public static void main(String[] args){
        ThreadTest4 test4 = new ThreadTest4();
        test4.setPriority(1);//设置优先级
        test4.start();
        Thread.currentThread().setPriority(10);
        for(int i = 0; i<10; i++){
            if(i % 2 != 0){
                System.out.println("main "+i);
            }
        }

    }
}


结果:
chapter11 多线程(一)
运行的结果依旧是不一定的,但是多运行几次就会出现低优先级先执行的情况,从而证明我们的结论是正确的。

相关标签: java基础的总结