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

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

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

二.继承Thread类

来看一下thread类的源代码:安全

class Thread implements Runnable {

首先能够看出thread类也是实现Runable接口的run方法以下:多线程

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

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

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方法里面的一个线程this

从结果能够看出这两个线程的调用和建立的顺序是无关的,spa

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

能够看出如今的两个"线程"已是按照顺序执行的了,其实如今并非多线程,就是一个单线程按照流程来执行。scala

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

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来实现同步化,在后面还会深刻了解同步化。

相关文章
相关标签/搜索