Java自学者论坛

 找回密码
 立即注册

手机号码,快捷登录

恭喜Java自学者论坛(https://www.javazxz.com)已经为数万Java学习者服务超过8年了!积累会员资料超过10000G+
成为本站VIP会员,下载本站10000G+会员资源,会员资料板块,购买链接:点击进入购买VIP会员

JAVA高级面试进阶训练营视频教程

Java架构师系统进阶VIP课程

分布式高可用全栈开发微服务教程Go语言视频零基础入门到精通Java架构师3期(课件+源码)
Java开发全终端实战租房项目视频教程SpringBoot2.X入门到高级使用教程大数据培训第六期全套视频教程深度学习(CNN RNN GAN)算法原理Java亿级流量电商系统视频教程
互联网架构师视频教程年薪50万Spark2.0从入门到精通年薪50万!人工智能学习路线教程年薪50万大数据入门到精通学习路线年薪50万机器学习入门到精通教程
仿小米商城类app和小程序视频教程深度学习数据分析基础到实战最新黑马javaEE2.1就业课程从 0到JVM实战高手教程MySQL入门到精通教程
查看: 554|回复: 0

创建Java多线程的两种方式和线程异常

[复制链接]
  • TA的每日心情
    奋斗
    2024-11-24 15:47
  • 签到天数: 804 天

    [LV.10]以坛为家III

    2053

    主题

    2111

    帖子

    72万

    积分

    管理员

    Rank: 9Rank: 9Rank: 9

    积分
    726782
    发表于 2021-9-7 10:49:52 | 显示全部楼层 |阅读模式

    一.使用多线程的两种方法

     使用多线程的两种方法有:继承Thread类和实现runable接口。

    二.继承Thread类

    来看一下thread类的源代码: 

    class Thread implements Runnable {

     

    首先可以看出thread类也是实现Runable接口的run方法如下:

        public void run() {
            if (target != null) {
                target.run();
            }
        }

     

     下面就是一个创建继承Thread的类的列子:

    public class ExThreadText extends Thread {
    
        @Override
        public void run(){
            for(int i=0;i<20;i++){
                System.out.println("我自己创建的线程");
            }
        }
    
        public static void main(String[] args) {
            new ExThreadText().start();
            System.out.println("程序结束!");
        }
    }

    结果如下

    首先我们需要明白在这个程序里面有多少个线程?应该是两个线程一个是main方法的线程一个是我run方法里面的一个线程

    从结果可以看出这两个线程的调用和创建的顺序是无关的,

    在这个代码实例里面我们重写了run方法,并使用start方法来调用,那为什么不用run方法来调用呢?我们来看看run方法调用会有什么结果:

     可以看出现在的两个"线程"已经是按照顺序执行的了,其实现在并不是多线程,就是一个单线程按照流程来执行。

    总结:1.多线程的调用是无序的

       2.多线程需要使用start方法来调用而不是run方法,同样start方法来调用线程也是无序的

    三.使用runable接口来实现多线程

    由于Java不提供多继承,所以当我们继承了Thread类的时候我们就不能继承其它的父类了,为了解决这一个问题,我建议实现runable接口。

    首先继承runable接口必须重写他的run方法,代码如下:

    public class ImRunableText implements Runnable {
        @Override
        public void run(){
            //重写run方法
        }
    }

     那怎么使用这个runable接口的子类呢?

    我们来看看Thread类的构造方法:

    可以看出Thread类的构造器可以接受实现Runable接口的子类对象(不要忘了thread类也是实现runable接口的),同时他还重载了一个构造器可以为线程命名。

    那么我们就可以使用这个runable的实现子类了,代码如下:

    public class ImRunableText implements Runnable {
        @Override
        public void run(){
            //重写run方法
            for(int i=0;i<10;i++){
                System.out.println("run方法");
            }
        }
    
        public static void main(String[] args) {
            Thread thread =new Thread(new ImRunableText(),"线程");
            thread.start();
            System.out.println("结束了");
        }
    }

     结果如下:

    四.线程中的数据共享和线程安全

    4.1数据不共享

    数据不共享那就是一个线程一个数据,单独执行互不影响。代码如下:

    这是一个线程类,他有自己的字段num为10。

    public class DataNShare extends Thread{
        private int num =10;
        private String name;
        public DataNShare(String name){
            this.name=name;
        }
        @Override
        public  void run(){
            for(;num>0;){
                System.out.println("当前线程为:"+name);
                System.out.println("num值为"+num);
                num--;
            }
        }
    }

     

    在设置一个测试类,创建三个对象,各自进行测试代码如下:

    public class Text {
    
        public static void main(String[] args) {
            DataNShare d1=new DataNShare("线程1");
            DataNShare d2=new DataNShare("线程2");
            DataNShare d3=new DataNShare("线程3");
            d1.start();
            d2.start();
            d3.start();
        }
    }

     

    测试结果:可以看出一开始是没有问题的,但是在后面出现了乱序的情况。

    那么出现了乱序的情况是不是就一定证明了程序出错了呢?

    我们来改进一下这个DataNShare类中的Run方法

    public  void run(){
            for(int i =1;num>=0;i++){
                System.out.println("当前线程为:"+name);
                System.out.println("num值为"+num);
                num--;
                if(num==0){
                    System.out.println("*************i的值为"+i+"*************");
                }
            }
        }

     

    那么也就是说当我的i值输出每一次输出10那么就是代表每一条线程都是执行互不影响的。

    *3,虽然还是存在乱序的情况,但是至少保证我们的每一条线程执行都是10次没有问题的。那么出现乱序的情况就是输出语句的问题。

    4.2数据共享

    数据共享就是多个线程可以访问一个数据,代码如下:

     放有共享数据的线程类:

    public class DataShare extends Thread {
        private int num=3;//共享数据
        @Override
        public void run(){
            num--;//共享数据减一
            System.out.println("当前线程为:"+this.currentThread().getName()+"num值为:"+num);
        }
    }

     

     处理类:

    public class Text {
    
        public static void main(String[] args) {
            //将共享数据放入3个线程里进行处理
            DataShare d=new DataShare();
            Thread t1=new Thread(d,"t1");
            Thread t2=new Thread(d,"t2");
            Thread t3=new Thread(d,"t3");
            t1.start();
            t2.start();
            t3.start();
        }
    }

     

     结果如下:

     

     在这里出现的这种情况就是线程不安全状态。那么怎么解决这个问题呢?

     使用关键字synchronized(同步化的)来解决。

     当一个方法使用该关键字那么在多个线程执行这个方法时,每一个线程获得执行该方法的执行权就会把这个方法上锁结束后开锁,只有等到这个方法没有被上锁时才可以被其他线程运行。

    看看改进后的代码:

    public class DataShare extends Thread {
        private int num=3;//共享数据
        @Override
        synchronized public void run(){
            num--;//共享数据减一
            System.out.println("当前线程为:"+this.currentThread().getName()+"num值为:"+num);
        }
    }

     结果:

    总结:当多个线程在共享一个数据时,可能会造成线程异常,应该使用关键字synchronized来实现同步化,在后面还会深入了解同步化。

     

    哎...今天够累的,签到来了1...
    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    QQ|手机版|小黑屋|Java自学者论坛 ( 声明:本站文章及资料整理自互联网,用于Java自学者交流学习使用,对资料版权不负任何法律责任,若有侵权请及时联系客服屏蔽删除 )

    GMT+8, 2024-12-22 13:08 , Processed in 0.062628 second(s), 30 queries .

    Powered by Discuz! X3.4

    Copyright © 2001-2021, Tencent Cloud.

    快速回复 返回顶部 返回列表