一.使用多线程的两种方法java
来看一下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方法来调用线程也是无序的
因为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来实现同步化,在后面还会深刻了解同步化。