首页 > 代码库 > java中的多线程入门

java中的多线程入门

本文主要是想学习下java中多线程的东西。

一、理解多线程

多线程是怎么样的机制?他是允许在程序中并发执行多个指令流,每个指令流都称为一个线程,彼此之间相互独立。

线程又称为轻量级进程,它和进程一样拥有独立的执行控制,有操作系统负责调度,区别在于线程诶有独立的存储空间,而是和所属进程中的其它线程共享一个存储空间,这使得线程间的通信远较进程简单。

多个线程的执行是并发的,也就是在逻辑上“同时”,而不管是否是物理上的“同时”,如果系统只有一个CPU,那么真正的“同时”是不可能的,但是由于CPU的速度非常快,用户感觉不到其中区别,所以不需要关心它。

二、java中多线程的实现

具体到java内存模型,由于java被设计为跨平台的语言,在内存管理上,显然也要有一个统一的模型。系统存在一个主内存,java中所有变量都存储在主存中,对所有线程都是共享的,每条线程都有自己的工作内存,工作内存中保存的是主存中某些变量的拷贝,线程对所有变量的操作都是在工作内存中进行的,线程之间无法相互直接访问,变量传递均需要通过主存完成。

作为一个完全面向对象的语言,java提供了类java.lang.Thread来方便多线程编程。这个类提供了大量方法来方便我们控制自己的各个线程。Tread类最重要的方法是run(),它为Thread类的方法start()所调用,提供我们的线程所要执行的代码。

方法一:继承Thread类,覆盖run()方法

我们在创建的Thread类的子类中重写run()方法,加入线程所要执行的代码即可。如下:

 1 public class MyThread extends Thread{ 2      3     int count = 1, number; 4     public MyThread(int num){ 5         number = num; 6         System.out.println("创建线程:" + number); 7     } 8      9     public void run(){10         while(true){11             System.out.println("线程" + number + ":计数  "+ count);12             if(++count == 6)13                 return ;14         }15     }16     17     public static void main(String[] args) {18         for(int i = 0; i < 5; i++){19             new MyThread(i+1).start();20         }21     }22 }

结果如下:

创建线程:1创建线程:2线程1:计数  1线程1:计数  2线程1:计数  3线程1:计数  4线程1:计数  5线程2:计数  1线程2:计数  2线程2:计数  3线程2:计数  4创建线程:3线程2:计数  5创建线程:4线程3:计数  1线程3:计数  2线程3:计数  3线程3:计数  4线程3:计数  5创建线程:5线程4:计数  1线程4:计数  2线程4:计数  3线程4:计数  4线程4:计数  5线程5:计数  1线程5:计数  2线程5:计数  3线程5:计数  4线程5:计数  5

 这里有一点需要注意,在main函数中,用到的是start函数,而不是run函数,大家可以试一下,用run函数得到的是顺序输出。为什么会用start而不是run?有人说在调用start函数的时候会首先进行与多线程相关的初始化,然后再调用run函数,也有人说通过start的源码

public synchronized void start() {        /**     * This method is not invoked for the main method thread or "system"     * group threads created/set up by the VM. Any new functionality added     * to this method in the future may have to also be added to the VM.     *     * A zero status value corresponds to state "NEW".         */        if (threadStatus != 0 || this != me)            throw new IllegalThreadStateException();        group.add(this);        start0();        if (stopBeforeStart) {        stop0(throwableFromStop);    }}private native void start0();

可以看出调用的是start0(),这个方法用了关键字native,这关键字表示调用本地操作系统的函数,因此需要用start()函数。因为多线程实现需要本地操作系统的支持。当调用run的时候系统并没有初始化多线程环境,还是在一个线程中。或者可以说,start是创建并启动一个线程,run只是运行其线程中代码。

这种方法简单明了,符合大家习惯,但是也有一个很大的缺点,那就是如果我们的类已经从一个类继承,则无法再继承Thread类,这时我们又不想建立一个新的类,那咋写?如下。

方法二:实现Runnable接口。

Runnable接口只有一个方法run(),我们声明自己的类实现Runnable接口,接口提供这一个方法,将我们的线程代码写入其中,就完成这一部分任务。

但是Runnable接口并没有任何对线程的支持,我们还必须创建Thread类的实例,这一点通过Thread类的构造函数public Thread(Runnable target)来实现。

下面是一个例子:

 1 public class MyThread implements Runnable{ 2     int count = 1, number; 3     public MyThread(int num){ 4         number = num; 5         System.out.println("创建线程"+number); 6     } 7     public void run(){ 8         while(true){ 9             System.out.println("线程"+number+":计数:"+count);10             if(++count==6)11                 return ;12         }13     }14     public static void main(String[] args){15         for(int i = 0; i<5; i++){16             new Thread(new MyThread(i+1)).start();17         }18     }19 }

结果如下:

创建线程1创建线程2线程1:计数:1线程1:计数:2线程1:计数:3创建线程3线程2:计数:1线程1:计数:4线程2:计数:2线程1:计数:5创建线程4线程2:计数:3线程3:计数:1线程3:计数:2线程2:计数:4线程3:计数:3创建线程5线程3:计数:4线程4:计数:1线程4:计数:2线程4:计数:3线程4:计数:4线程5:计数:1线程2:计数:5线程5:计数:2线程4:计数:5线程3:计数:5线程5:计数:3线程5:计数:4线程5:计数:5

严格的说,创建Thread子类的实例也是可行的,但是必须注意的是,该子类必须没有覆盖Thread类的run方法,否则该线程执行的将是子类的run方法而不是我们用以实现Runnable接口类的run方法。

使用Runnable接口来实现多线程使得我们能够在一个类中包容所有的代码,有利于封装,他的缺点在于,我们只能使用一套代码,若想创建多个线程并使各个线程执行不同的代码,则必须额外创建类,这样的话就不如直接用多个类分别继承Thread来的紧凑。

那么如何选择Runnable和Thread呢?

其实Thread也是实现Runnable的,Thread和Runnable都实现了run方法。

Thread和Runnable的区别

如果一个类继承Thread,则不适合资源共享。但是如果实现了Runable接口的话,则很容易的实现资源共享。  

 1 class hello extends Thread { 2     public void run() { 3         for (int i = 0; i < 7; i++) { 4             if (count > 0) { 5                 System.out.println("count= " + count--); 6             } 7         } 8     } 9  10     public static void main(String[] args) {11         hello h1 = new hello();12         hello h2 = new hello();13         hello h3 = new hello();14         h1.start();15         h2.start();16         h3.start();17     }18  19     private int count = 5;20 }

结果如下

count= 5count= 4count= 5count= 4count= 5count= 3count= 2count= 3count= 1count= 4count= 2count= 1count= 3count= 2count= 1

这里很明显看出并未实现资源(count)的共享,换位Runnable

 1 class MyThread implements Runnable{ 2   3     private int ticket = 5;  //5张票 4   5     public void run() { 6         for (int i=0; i<=20; i++) { 7             if (this.ticket > 0) { 8                 System.out.println(Thread.currentThread().getName()+ "正在卖票"+this.ticket--); 9             }10         }11     }12 }13 public class lzwCode {14      15     public static void main(String [] args) {16         MyThread my = new MyThread();17         new Thread(my, "1号窗口").start();18         new Thread(my, "2号窗口").start();19         new Thread(my, "3号窗口").start();20     }21 }

结果如下

2号窗口正在卖票52号窗口正在卖票22号窗口正在卖票13号窗口正在卖票41号窗口正在卖票3

总结如下:

实现Runnable接口比继承Thread接口有优势:

1.适合多个相同的程序代码的线程去处理同一个资源

2.可以避免java中的单继承的限制

3.增加程序的健壮性,代码可以被多个线程共享,代码和数据独立。

 

总之,对于多线程,虽然我用的不多,但是对于找工作或者啥的还是非常有用处的。这只是基本的多线程问题,以后还要深入研究多线程的细节东西。这算是入门吧。

 

java中的多线程入门