简单阐释进程和线程java
对于进程最直观的感觉应该就是“windows任务管理器”中的进程管理:编程
(计算机原理课上的记忆已经快要模糊了,简单理解一下):一个进程就是一个“执行中的程序”,是程序在计算机上的一次运行活动。程序要运行,系统就在内存中为该程序分配一块独立的内存空间,载入程序代码和资源进行执行。程序运行期间该内存空间不能被其余进程直接访问。系统以进程为基本单位进行系统资源的调度和分配。何为线程?线程是进程内一次具体的执行任务。程序的执行具体是经过线程来完成的,因此一个进程中至少有一个线程。回忆一下 HelloWrold 程序中main方法的执行,其实这时候,Java虚拟机会开启一个名为“main”的线程来执行程序代码。一个进程能够包含多个线程,这些线程共享数据空间和资源,但又分别拥有各自的执行堆栈和程序计数器。线程是CPU调度的基本单位。windows
多线程多线程
一个进程包含了多个线程,天然就叫作多线程。拥有多个线程就可让程序看起来能够“同时”处理多个任务,为何是看起来呢?由于CPU也分身乏术,只能让你这个线程执行一下子,好了你歇着,再让另外一个线程执行一下子,下次轮到你的时候你再继续执行。这里的“一下子”实际上时间很是短,感受上就是多个任务“同时”在执行。CPU就这样不停的切来切去…既然CPU一次也只能执行一个线程,为何要使用多线程呢?固然是为了充分利用CPU资源。一个线程执行过程当中不可能每时每刻都在占用CPU,CPU歇着的时候咱们就可让它切过来执行其余的线程。ide
好比QQ聊天的时候,跟一我的正聊着呢,另外一个消息过来了。若是是单线程,很差意思,等我跟这一个聊完说拜拜以后再去理你吧。多线程呢,消息窗口全打开,这个窗口说完话了,总得等人家回吧,趁这个空闲时候,处理另外一个窗口的消息。这样看起来不就是同时进行了么,每个窗口的另外一边都觉得你只在跟他一我的聊天…函数
Java中的多线程post
Java中启用多线程有两种方式:①继承Thread类;②实现Runnable接口。this
There are two ways to create a new thread of execution. One is to declare a class to be a subclass of
Thread
. This subclass should override therun
method of classThread
. An instance of the subclass can then be allocated and started.The other way to create a thread is to declare a class that implements theRunnable
interface. That class then implements therun
method. An instance of the class can then be allocated, passed as an argument when creatingThread
, and started. spa
继承Thread类操作系统
建立一个类,继承java.lang.Thread,并覆写Thread类的run()方法,该类的实例就能够做为一个线程对象被开启。
/** * Dog类,继承了Thread类 * @author lt */ class Dog extends Thread { /* * 覆写run()方法,定义该线程须要执行的代码 */ @Override public void run() { for (int i = 0; i < 10; i++) { System.out.println(i); } } }
线程建立好了,怎么让它做为程序的一个独立的线程被执行呢?建立一个该类的实例,并调用start()方法,将开启一个线程,并执行线程类中覆写的run()方法。
public class ThreadDemo { public static void main(String[] args) { Dog dog = new Dog(); dog.start(); } }
看不出什么端倪,若是咱们直接调用实例的run()方法,执行效果是彻底同样的,见上图。
public class ThreadDemo { public static void main(String[] args) { Dog dog = new Dog(); dog.run(); } }
若是一切正常,这时候程序中应该有两个线程:一个主线程main,一个新开启的线程。run()方法中的代码到底是哪一个线程执行的呢?Java程序中,一个线程开启会被分配一个线程名:Thread-x,x从0开始。咱们能够打印当前线程的线程名,来看看到底是谁在执行代码。
class Dog extends Thread { @Override public void run() { for (int i = 0; i < 10; i++) { System.out.println("当前线程:" + Thread.currentThread().getName() + "---" + i); } } } public class ThreadDemo { public static void main(String[] args) { System.out.println("当前线程:" + Thread.currentThread().getName()); Dog dog = new Dog(); dog.start(); } }
能够看到,确实开启了一个新的线程:Thread-0,main()方法的线程名就叫main。
同一个实例只能调用一次start()方法开启一次,屡次开启,将报java.lang.IllegalThreadStateException异常:
咱们再建立一个实例,开启第三个线程:
public class ThreadDemo { public static void main(String[] args) { System.out.println("当前线程:" + Thread.currentThread().getName()); Dog dog = new Dog(); Dog dog2 = new Dog(); dog.start(); dog2.start(); } }
这时候咱们已经可以看到多线程的底层实现原理:CPU切换处理、交替执行的效果了。
run和start
上面咱们直接调用run()方法和调用start()方法的结果同样,如今咱们在打印线程名的状况下再来看看:
public class ThreadDemo { public static void main(String[] args) { System.out.println("当前线程:" + Thread.currentThread().getName()); Dog dog = new Dog(); Dog dog2 = new Dog(); dog.run(); dog2.run(); } }
能够看到,这时候并无开启新的线程,main线程直接调用执行了run()方法中的代码。因此start()方法会开启新的线程并在新的线程中执行run()方法中的代码,而run()方法不会开启线程。查看start()的源代码,该方法调用了本地方法 private native void start0();即调用的是操做系统的底层函数:
public synchronized void start() { if (threadStatus != 0) throw new IllegalThreadStateException(); group.add(this); boolean started = false; try { start0(); started = true; } finally { try { if (!started) { group.threadStartFailed(this); } } catch (Throwable ignore) { /* do nothing. If start0 threw a Throwable then it will be passed up the call stack */ } } } private native void start0();
实现Runnable接口
第二种方式,实现Runnable接口,并覆写接口中的run()方法,这是推荐的也是最经常使用的方式。Runnable接口定义很是简单,就只有一个抽象的run()方法。
//Runnable接口源码 public interface Runnable { public abstract void run(); }
class Dog implements Runnable { @Override public void run() { for (int i = 0; i < 10; i++) { System.out.println("当前线程:" + Thread.currentThread().getName() + "---" + i); } } }
这时候的Dog类看起来跟线程什么的毫无关系,也没有了start()方法,怎么样开启一个新的线程呢?直接调用run()方法?想一想也不行。这时候咱们须要将一个Dog类的实例,做为Thread类的构造函数的参数传入,来建立一个Thread类的实例,并经过该Thread类的实例来调用start()方法从而开启线程。
public class ThreadDemo { public static void main(String[] args) { System.out.println("当前线程:" + Thread.currentThread().getName()); Dog dog = new Dog(); Thread thread = new Thread(dog); thread.start(); } }
这时候若是要开启第三个线程,须要建立一个新的Thread类的实例,同时传入刚才的Dog类的实例(固然也能够建立一个新的Dog实例)。这时候咱们就能够看到跟继承Thread类的方式的区别:多个线程能够共享同一个Dog类的实例。
public class ThreadDemo { public static void main(String[] args) { System.out.println("当前线程:" + Thread.currentThread().getName()); Dog dog = new Dog(); Thread thread = new Thread(dog); Thread thread2 = new Thread(dog); thread.start(); thread2.start(); } }
两种方式的比较
继承Thread类的方式有它固有的弊端,由于Java中继承的单一性,继承了Thread类就不能继承其余类了;同时也不符合继承的语义,Dog跟Thread没有直接的父子关系,继承Thread只是为了能拥有一些功能特性。而实现Runnable接口,①避免了单一继承的局限性,②同时更符合面向对象的编程方式,即将线程对象进行单独的封装,③并且实现接口的方式下降了线程对象(Dog)和线程任务(run方法中的代码)的耦合性,④如上面所述,可使用同一个Dog类的实例来建立并开启多个线程,很是方便的实现资源的共享。实际上Thread类也是实现了Runnable接口。实际开发中可能是使用实现Runnable接口的方式。