线程与进程

进程:

是指一个内存中运行的应用程序,每个进程都有一个独立的内存空间,一个应用程序可以同时运行多个进程;进程也是程序的一次执行过程,是系统运行程序的基本单位;系统运行一个程序即是一个进程从创建、运行到消亡的过程。 

线程:

进程内部的一个独立执行单元;一个进程可以同时并发地运行多个线程,可以理解为一个进程便相当于一个单 CPU 操作系统,而线程便是这个系统中运行的多个任务。 

进程与线程的区别:

进程:有独立的内存空间,进程中的数据存放空间(堆空间和栈空间)是独立的,至少有一个线程。

线程:堆空间是共享的,栈空间是独立的,线程消耗的资源比进程小的多。 

注意:

  1. 因为一个进程中的多个线程是并发运行的,那么从微观角度看也是有先后顺序的,哪个线程执行完全取决于CPU 的调度,程序员是不能完全控制的(可以设置线程优先级)。而这也就造成的多线程的随机性。
  2. Java 程序的进程里面至少包含两个线程,主线程也就是 main()方法线程,另外一个是垃圾回收机制线程。每当使用 java 命令执行一个类时,实际上都会启动一个 JVM,每一个 JVM 实际上就是在操作系统中启动了一个线程,java 本身具备了垃圾的收集机制,所以在 Java 运行时至少会启动两个线程。
  3. 由于创建一个线程的开销比创建一个进程的开销小的多,那么我们在开发多任务运行的时候,通常考虑创建多线程,而不是创建多进程。 

多线程

多线程技术可以更好地利用系统资源。主要优势在于充分利用了CPU的空闲时间片,用尽可能少的时间来对用户的要求做出响应,使得进程的整体运行效率得到较大提高,同时增强了应用程序的灵活性。

更为重要的是,由于同一进程的所有线程共享同一内存,所以不需要特殊的数据传送机制,不需要建立共享存储区或共享文件,从而使得不同任务之间的协调操作与运行、数据的交互、资源的分配等问题更加易于解决。 

创建多线程的五种方式

1.继承Thread类

Thread类本质上是实现了Runnable接口的一个实例,代表一个线程的实例。

启动线程的唯一方法就是通过Thread类的start()实例方法。

start()方法是Java的native方法,它将启动一个新线程,并执行run()方法。

这种方法实现多线程比较简单,通过自己创建的类直接继承Thread,并重写run()方法,就可以启动新线程并执行自己定义的run()方法。

优点:代码比较简单

缺点:该类无法继承其他类

1 public class ThreadDemo1 {
 2 
 3     public static void main(String[] args) {
 4         ThreadDemo t1 = new ThreadDemo();
 5         t1.setName("线程1");
 6         t1.start();
 7         ThreadDemo t2 = new ThreadDemo();
 8         t2.setName("线程2");
 9         t2.start();
10     }
11 
12 }
13 
14 class ThreadDemo extends Thread{
15     @Override
16     public void run() {
17         for (int i = 1; i <= 10; i++) {
18             System.out.println(Thread.currentThread().getName()+":"+i);
19         }
20     }
21 }

 

2.实现Runnable接口

Java的继承属于单继承,如果一个类继承了其他的一个类就无法继承Thread类。但是一个类继承一个类的同时,可以实现多个接口。

优点:继承其他类,统一实现该接口的实例可以共享资源。

缺点:代码比较复杂。

1 public class ThreadDemo2 {
 2 
 3     public static void main(String[] args) {
 4         MyThread myThread= new MyThread();
 5         Thread t1 = new Thread(myThread, "线程1");
 6         t1.start();
 7         Thread t2 = new Thread(myThread, "线程2");
 8         t2.start();
 9     }
10 
11 }
12 
13 class MyThread implements Runnable{
14     @Override
15     public void run() {
16         for (int i = 1; i <= 10; i++) {
17             System.out.println(Thread.currentThread().getName()+":"+i);
18         }
19     }
20 }

 

3.实现Callable接口

实现Callable接口和实现Runnable接口的方法基本相同,不过Callable接口中的call()方法有返回值,Runnable接口中的call()方法没有返回值。

1 public class ThreadDemo3 {
 2 
 3     public static void main(String[] args) throws ExecutionException, InterruptedException {
 4         Callable<Object> myCallable = new MyCallable<Object>();
 5         //使用FutureTask类包装Callable对象,该FutureTask对象封装了Callable对象的call()方法的返回值
 6         FutureTask<Object> futureTask = new FutureTask<Object>(myCallable);
 7         Thread t1 = new Thread(futureTask, "线程1");
 8         t1.start();
 9         System.out.println(futureTask.get());
10     }
11 }
12 
13 
14 class MyCallable<String> implements Callable<Object>{
15 
16     @Override
17     public Object call() throws Exception {
18         for (int i = 1; i <= 10; i++) {
19             System.out.println(i);
20         }
21         return Thread.currentThread().getName()+"执行完毕";
22     }
23 }

 

4.线程池

线程池,其实就是一个容纳多个线程的容器,其中的线程可以重复使用,省去了频繁创建线程对象的操作,避免了频繁创建线程的资源消耗。

Executor类:提供了一系列工厂方法用于创建线程池,返回的线程池都实现了ExecutorService接口。

Java通过Executors提供四种线程池,分别为:

newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行。
newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。ExecutorService提供了sumbit()方法,传递一个Callable或Runnable,将线程放进线程池中,并执行。

优点:实现自动化装配,易于管理,循环利用资源。

1 public class ThreadDemo4 {
 2 
 3     public static void main(String[] args) {
 4         ExecutorService pool = Executors.newFixedThreadPool(2);
 5         //将线程放入线程池中并执行
 6         Future<?> submit = pool.submit(new MyRunnableThread());
 7         pool.submit(new MyRunnableThread());
 8         //关闭线程池
 9         pool.shutdown();
10     }
11 
12 }
13 
14 class MyRunnableThread implements Runnable {
15     @Override
16     public void run() {
17         for (int i = 1; i <=10 ; i++) {
18             System.out.println(Thread.currentThread().getName()+":"+i);
19         }
20     }
21 }

 

5.Timer类和TimerTask类

在这种实现方式中,Timer类实现的是类似定时任务的功能,也就是定时或者每隔一定时间触发一次线程。
Timer类本身实现的就是一个线程,只是这个线程是用来调用其他线程。
TimerTask类实现了Runnable接口,具备了多线程的能力。
在这种实现方式中,通过继承TimerTask使该类获得多线程的能力,重写run()方法,然后通过Timer类启动线程。
在实际使用时,一个Timer可以启动任意多个TimerTask实现的线程,但是多个线程之间会存在阻塞。所以如果多个线程之间如果需要完全独立运行的话,
最好还是一个Timer启动一个TimerTask实现。

1 public class ThreadDemo5 {
 2 
 3     public static void main(String[] args) {
 4         Timer timer = new Timer();
 5         MyTimerTask myTimerTask1 = new MyTimerTask("线程1");
 6         MyTimerTask myTimerTask2 = new MyTimerTask("线程2");
 7         //启动线程
 8         timer.schedule(myTimerTask1,0);
 9         timer.schedule(myTimerTask2,0);
10     }
11 
12 }
13 
14 class MyTimerTask extends TimerTask{
15     private String TimerTaskName;
16 
17     public MyTimerTask(String TimerTaskName) {
18         this.TimerTaskName=TimerTaskName;
19     }
20 
21     @Override
22     public void run() {
23         for (int i = 1; i <=10 ; i++) {
24             System.out.println(TimerTaskName+":"+i);
25         }
26     }
27 }