什么是线程,Java多线程编程(小白必读)
- 资源得到更合理的利用;
- 程序设计更加简洁;
- 程序响应更快,运行效率更高。
任何一门技术都没有绝对的好与坏,有其优点就必有其缺点,多线程也存在一些缺点:
- 需要更多的内存空间来支持多线程;
- 多线程并行访问的情况可能会影响数据的准确性;
- 数据被多线程共享,可能会出现死锁的情况。
我们在实际开发的过程中,应该将程序设计得更加合理有效,避免多线程的缺点,将其优点发挥出来,从而提高程序的性能。
进程与线程
说到线程,就必须先提出进程的概念,什么是进程呢?简单来理解,进程就是计算机正在运行的一个独立的应用程序,例如打开 Eclipse 编写 Java 程序就是一个进程,打开浏览器查找学习资料就是一个进程等。一个应用程序至少有一个进程。
那什么是线程呢?进程和线程之间的关系是什么呢?
线程是组成进程的基本单位,可以完成特定的功能,一个进程是由一个或多个线程组成的。进程和线程是应用程序在执行过程中的概念,如果应用程序没有执行,比如 Eclipse 工具没有运行起来,那么就不存在进程和线程的概念。应用程序是静态的概念,进程和线程是动态概念,有创建就有销毁,存在也是暂时的,不是永久性的。
进程与线程的区别在于,进程在运行时拥有独立的内存空间,即每个进程所占用的内存都是独立的,互不干扰。而多个线程是共享内存空间的,但是每个线程的执行是相互独立的,同时线程必须依赖于进程才能执行,单独的线程是无法执行的,由进程来控制多个线程的执行。
了解完进程与线程的区别,接下来说说什么是多线程。
我们通常所说的多线程是指在一个进程中,多个线程同时执行。这里说的同时执行不是真正意义上的同时执行,系统会自动为每个线程分配 CPU 资源,在某个具体时间段内 CPU 的资源被一个线程占用,在不同的时间段内由不同的线程来占用 CPU 资源,所以多个线程还是在交替执行,只不过因为 CPU 运行速度太快,感觉上是在同时执行。
我们之前写的 Java 程序都是单线程的,Java 程序运行起来就是一个进程,该进程中只有一个线程在运行,比如:
public class Test { public static void main(String[] args) { System.out.println("Thread"); } }
程序运行起来之后只有一个线程在执行,就是 main 方法,所以 main 方法也叫作程序的主线程,main 方法中无论调用多少个其他类的方法,也都只是一个线程,比如:
public class MyTest { public void test() { for (int i = 0; i < 5; i++) { System.out.println("--------------MyTest"); } } } public class Test { public static void main(String[] args) { for(int i = 0; i < 5; i++) { System.out.println("++++++++++++++Test"); } MyTest myTest = new MyTest(); myTest.test(); } }运行结果为:
++++++++++++++Test
++++++++++++++Test
++++++++++++++Test
++++++++++++++Test
++++++++++++++Test
--------------MyTest
--------------MyTest
--------------MyTest
--------------MyTest
--------------MyTest
那什么是多线程呢?两个循环逻辑不是顺序执行,而是同时执行,比如输出结果为:
++++++++++++++Test
++++++++++++++Test
--------------MyTest
++++++++++++++Test
--------------MyTest
......
我们如何来判断程序是单线程还是多线程呢?只需要分析程序有几条分支即可,例如:
图 1 单线程
整个程序的执行是一条回路,所以程序只有一个线程。再例如:
图 2 多线程
程序有两条回路,同时向下运行,这种情况就是多线程,两个线程同时执行。
Java中线程的使用
Java 中实现多线程的常用方式有两种,分别是继承 Thread 类和实现 Runnable 接口。1) 继承Thread类
继承 Thread 类的实现分为两步:- 创建自定义类并继承 Thread;
- 重写 Thread 的 run() 方法,并编写该线程的业务逻辑代码。
具体实现如下面的代码所示:
public class MyThread extends Thread{ @Override public void run() { // TODO Auto-generated method stub for (int i = 0; i < 5; i++) { System.out.println("--------------MyThread"); } } }
线程创建好之后,如何调用呢?具体实现为:
public class Test { public static void main(String[] args) { MyThread myThread = new MyThread(); myThread.start(); for(int i = 0; i < 5; i++) { System.out.println("++++++++++++++Test"); } } }运行结果为:
--------------MyTest
--------------MyTest
++++++++++++++Test
++++++++++++++Test
++++++++++++++Test
--------------MyTest
--------------MyTest
++++++++++++++Test
--------------MyTest
++++++++++++++Test
这里需要注意,开启子线程是通过调用线程对象的 start() 方法来完成的,一定不能调用 run() 方法,调用 run() 方法是普通的方法调用,相当于在主线程中顺序执行了两个循环,并没有开启一个可以和主线程抢占 CPU 资源的子线程。所以调用 run() 方法并不是多线程,还是一个主线程在执行,比如:
public class Test { public static void main(String[] args) { MyThread myThread = new MyThread(); myThread.run(); for(int i = 0; i < 5; i++) { System.out.println("++++++++++++++Test"); } } }运行结果为:
--------------MyThread
--------------MyThread
--------------MyThread
--------------MyThread
--------------MyThread
++++++++++++++Test
++++++++++++++Test
++++++++++++++Test
++++++++++++++Test
++++++++++++++Test
2) 实现Runnable接口
Java 中创建多线程的另外一种方式是通过 Runnable 接口的实现来完成,具体分为两步:- 创建自定义类并实现 Runnable 接口;
- 实现 run() 方法,编写该线程的业务逻辑代码。
具体实现代码如下:
public class MyRunnable implements Runnable{ @Override public void run() { // TODO Auto-generated method stub for (int i = 0; i < 5; i++) { System.out.println("--------------MyRunnable"); } } }
调用 MyRunnable 线程的具体实现如下:
public class Test { public static void main(String[] args) { MyRunnable myRunnable = new MyRunnable(); Thread thread = new Thread(myRunnable); thread.start(); for(int i = 0; i < 5; i++) { System.out.println("++++++++++++++Test"); } } }MyRunnable 的使用与 MyThread 略有不同,MyRunnable 相当于定义了线程业务逻辑,它本身并不是线程对象,所以还需要实例化 Thread 对象,然后将 MyRunnable 对象赋给 Thread 对象。这样 Thread 对象就知道了它要完成的业务逻辑,再通过调用 Thread 对象的 start() 方法来启动该线程,运行结果为:
++++++++++++++Test
++++++++++++++Test
++++++++++++++Test
--------------MyRunnable
--------------MyRunnable
--------------MyRunnable
++++++++++++++Test
++++++++++++++Test
--------------MyRunnable
--------------MyRunnable