chapter11 多线程(一)

多线程的介绍

进程:一个正在运行的程序。
线程:是一个进程中的一个执行单元(执行路径)。
比如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 *");
            }
        }
    }
}

运行结果:
在这里插入图片描述
运行结果可能为前面全是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 *");
            }
        }
    }
}

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

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

多线程通过实现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);
            }
        }
    }
}

结果:
在这里插入图片描述
结果并不一定是上面那样,在运行几次之后,就会发现结果和当前的结果不同,这是正常的。

这里注意的问题和上面提到的注意问题一样,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();
                }
            }
        }
    }
}

结果:
在这里插入图片描述
当然结果并不一定是这样,但是肯定的是,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);
            }
        }

    }
}


结果:
在这里插入图片描述
运行的结果依旧是不一定的,但是多运行几次就会出现低优先级先执行的情况,从而证明我们的结论是正确的。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值