Java 线程

关于Java线程,应该说两种基本的创建线程的方法是知道的,现成的集中状态也是知道。对于简单的锁同步也清楚。

还有第三种创建线程的方式不清楚,关于线程池这个概念不清楚?  总结前面两种方式,学习后面两个问题。 还有一个wait和sleep区别。

 几个基本概念说清楚:

1:进程,线程

进程让操作系统并发成为可能,而线程让进程内部并发成为可能。

一个进程虽然包含多个线程,但是这些线程是共同享有进程占有的资源和地址空间。进程是操作系统进行资源分配的基本单位,而线程是操作系统进行调度的基本单位。

由于多个线程是共同占有所属进程的资源和地址空间,那么就会存在问题:所个线程同时访问进程的某个资源,怎么处理?

这个问题就是后序文章中要重点讲述的同步问题。

1、创建线程

 在java中创建线程一般有两种方法,1)继承Thread类, 2)实现Runnable接口

1.1继承Thread类

 1 package Lesson1218Thread;
 2 
 3 public class MyThread extends Thread {
 4     
 5     public static int num = 0;
 6      public MyThread(){
 7             num++;
 8         }
 9     
10     @Override
11     public void run() {
12         for(int i=0;i<10;i++){
13             System.out.println("Thread ID " + this.getId() );
14         }
15     }
16 
17 }
18 package Lesson1218Thread;
19 
20 public class ThreadDemo {
21 
22     public static void main(String[] args) {
23         
24         Thread thread1 = new MyThread();        
25         Thread thread2 = new MyThread();
26         
27         thread2.start();
28         thread1.start();    
29     }
30 }

看上面例子,新建一个MyThread类继承于Thread, 通过start将thread准备就绪,什么时候占有CPU资源就开始run().

上面例子建立了两个线程,会同时抢占CPU资源,不同步。

1.2.通过实现Runnable接口去创建线程

通过Runnable去创建线程,必须实现run()函数。

 1 package Lesson1218Thread;
 2 
 3 public class MyRunnable implements Runnable {
 4     
 5     @Override
 6     public void run() {
 7         
 8         //for(int i=0;i<10;i++){
 9             System.out.println("Thread name " + Thread.currentThread().getName());
10         //}
11     }
12 
13 }
14 
15 package Lesson1218Thread;
16 
17 public class ThreadDemo {
18 
19     public static void main(String[] args) {
20         
21         Thread thread1 = new MyThread();        
22         Thread thread2 = new MyThread();        
23         thread2.start();
24         thread1.start();
25         
26         MyRunnable myRunnable = new MyRunnable();        
27         Thread thread3 = new Thread(myRunnable);     //接口不可实例化,所以参数必须为实现接口的类或匿名类。下面有匿名类例子
28         Thread thread4 = new Thread(myRunnable);
29         
30         System.out.println(thread3.equals(thread4));  //false
31         
32         thread3.start();
33         thread4.start();        
34         
35         return;
36     }
37 }

运行结果:

false

Thread name Thread-2
Thread name Thread-3

Runnable的意思是“任务”,顾名思义,通过实现Runnable接口,我们定义了一个子任务,然后将子任务交由Thread去执行。注意这种方式必须将Runnable作为Thread类的参数来创建线程。然后通过Thread的start()方法来启动线程。

事实上查看Thread的源码,可以看到Thread也是实现了Runnable接口。 Thread有多个构造函数,可以传入以下参数,这个看具体要求。

从上面两种看。通过Runnable来创建线程是优于Thread,  类还可以去继承其他类。

1.3.通过匿名内部类来创建线程

上面两种方式是比较常见的创建线程的方法,但它们都有一个弊端,就是太麻烦,比如说项目里我就需要创建一个线程而已,难道还要创建一个类,然后再调用它吗,其实线程在项目中一般用下面这种简单的方式创建。

 1 package Lesson1218Thread;
 2 
 3 public class ThreadDemo {
 4 
 5     public static void main(String[] args) {
 6         new Thread(new Runnable(){
 7 
 8             @Override
 9             public void run() {
10                 System.out.println("通过内部类来创建Thread!! " + Thread.currentThread().getName());                
11             }            
12             
13         }).start();
14         
15         return;
16     }
17     
18 }

运行结果:

通过内部类来创建Thread!! Thread-4

匿名内部类还要去学习。

1.4通过Callable和FutureTask创建线程

1>创建Callable接口的实现类,并实现call()方法

2>创建Callable实现类的实例,使用FutureTask类来包装Callable对象,该FutureTask对象封装了该Callable对象的call()方法的返回值

3>使用FutureTask对象作为Thread对象的target创建并启动新的线程。

4>调用FutureTask对象的get()方法来获取线程执行结束后的返回值。

Callable是一个带有泛型的接口,里面只有一个抽象函数call(),带有返回值泛型。

1 public interface Callable<V> {
2     V call() throws Exception;
3 }

下面给出通过Callable和FutureTask来创建线程例子:

 1 package Lesson1218Thread;
 2 
 3 import java.util.concurrent.Callable;
 4 
 5 public class MyCallable implements Callable<Object> {
 6 
 7     @Override
 8     public Object call() throws Exception {
 9         System.out.println("hehe.....");
10         return 110;
11     }
12 }
13 
14 package Lesson1218Thread;
15 
16 import java.util.concurrent.ExecutionException;
17 import java.util.concurrent.FutureTask;
18 
19 public class ThreadDemo {
20 
21     public static void main(String[] args) {
22 
23         FutureTask<Object> ft = new FutureTask<>(new MyCallable());
24         new Thread(ft).start();
25         try {
26             System.out.println(ft.get());
27         } catch (InterruptedException | ExecutionException e) {
28             // TODO Auto-generated catch block
29             e.printStackTrace();
30         }         
32         return;
33     }     
35 }

运行接口输出:

false
hehe.....
110   //这个地方是返回值

使用接口实现线程的好处:

多个线程可共享一个target对象,所以非常适合多个相同线程来处理同一份资源的情况,从而将CPU、代码和数据分开,形成清晰的模型,较好地体现了面向对象的思想。

这个地方有一个问题,一直没有弄明白,如果哪位大牛清楚,希望帮忙解析下???

 就是上面的例子,如果通过ft多new几个Thread,结果发现那些后面新建的线程都没有运行, 如下:

 1 package Lesson1218Thread;
 2 
 3 import java.util.concurrent.ExecutionException;
 4 import java.util.concurrent.ExecutorService;
 5 import java.util.concurrent.Executors;
 6 import java.util.concurrent.FutureTask;
 7 
 8 public class ThreadDemo {
 9 
10     public static void main(String[] args) {
11         FutureTask<Object> ft = new FutureTask<>(new MyCallable());
12         new Thread(ft).start();   //new 两个Thread, 可是只有一个运行
13         new Thread(ft).start();
14         try {
15             System.out.println(ft.get());
16         } catch (InterruptedException | ExecutionException e) {
17             // TODO Auto-generated catch block
18             e.printStackTrace();
19         }
20         return;
21     }
22     
23 }

运行结果:

hehe.....
110
??????不知道为什么?????

再看到FutureTask的时候,发现接口居然可以多继承,以前一直以为Java就是单继承。出门看另一篇帖子:Java接口多继承。

 FutureTask最后还是实现了Runnable接口。

1 public class FutureTask<V> implements RunnableFuture<V> {}
2 
3 public interface RunnableFuture<V> extends Runnable, Future<V> {
4     /**
5      * Sets this Future to the result of its computation
6      * unless it has been cancelled.
7      */
8     void run();
9 }

Runnable和Callable接口的区别:
(1)Callable重写的方法是call(),Runnable重写的方法是run();
(2)Callable的任务执行后可返回值,而Runnable不能返回值;
(3)call方法可以抛出异常,run()不可以;
(4)运行Callable任务可以拿到一个future对象,表示异步计算的结果,它供检查计算是否完成的方法,以等待计算完成,并检索计算的结果。通过Future对象可以了解任务的执行情况,可取消任务的执行,还可以获取执行的结果。(对于这点不清楚,先复制在这)

1.5通过线程池来创建线程

对线程池确实不了解,我们先看一个例子

 1 package lesson.threadDemo;
 2 
 3 import java.util.concurrent.Callable;
 4 
 5 public class MyCallable implements Callable<String> {
 6 
 7     @Override
 8     public String call() throws Exception {
 9         System.out.println("hehe....."+Thread.currentThread().getName());
10 
11         return "110";
12     }    
13 
14 }
15 
16 package lesson.threadDemo;
17 
18 import java.util.ArrayList;
19 import java.util.List;
20 import java.util.concurrent.ExecutionException;
21 import java.util.concurrent.Executor;
22 import java.util.concurrent.ExecutorService;
23 import java.util.concurrent.Executors;
24 import java.util.concurrent.Future;
25 import java.util.concurrent.FutureTask;
26 
27 public class ThreadDemo {
28 
29     public static void main(String[] args) {
30         MyCallable myCallable = new MyCallable();
31         //不用线程池,用Callable和FutureTask创建函数
32         /*FutureTask ft = new FutureTask<>(myCallable);   
33         
34         new Thread(ft).start();
35         
36         try {
37             System.out.println(ft.get());
38         } catch (InterruptedException | ExecutionException e) {
39             // TODO Auto-generated catch block
40             e.printStackTrace();
41         }*/
42         
43         //用线程池创建函数
44         ExecutorService es = Executors.newCachedThreadPool();
45         List<Future<String>> fts = new ArrayList<Future<String>>();
46         for(int i=0;i<5;i++) {
47             fts.add(es.submit(myCallable));            
48         }
49         
50         for(Future<String> ft1:fts) {
51             try {
52                 System.out.println(ft1.get());
53             } catch (InterruptedException e) {
54                 // TODO Auto-generated catch block
55                 e.printStackTrace();
56             } catch (ExecutionException e) {
57                 // TODO Auto-generated catch block
58                 e.printStackTrace();
59             }
60             
61         }
62         
63     }
64 
65 }

运行结果:

hehe.....pool-1-thread-2
hehe.....pool-1-thread-1
hehe.....pool-1-thread-3
110
110
110
hehe.....pool-1-thread-3
hehe.....pool-1-thread-4
110
110

从上面例子看,看不出现成是在哪儿启动的,也不知道怎么运行的。对其中几个类Executors和EcecutorService两个类不了解。

 通过另一个例子看线程池如何创建线程:

 1 package lesson.threadDemo;
 2 
 3 import java.util.concurrent.ExecutorService;
 4 import java.util.concurrent.Executors;
 5 
 6 public class ThreadPool {
 7     
 8     private static int POOL_NUM = 10;
 9 
10     public static void main(String[] args) {
11         
12         ExecutorService es = Executors.newFixedThreadPool(6);
13         
14         for(int i =0;i<POOL_NUM;i++) {
15             
16             es.execute(new RunnableThread());            
17         }
18     }
19 }
20 
21 
22 class RunnableThread implements Runnable{
23 
24     @Override
25     public void run() {
26         System.out.println("hehe....." + Thread.currentThread().getName());
27     }
28     
29 }

运行结果:

hehe.....pool-1-thread-1
hehe.....pool-1-thread-4
hehe.....pool-1-thread-3
hehe.....pool-1-thread-6
hehe.....pool-1-thread-2
hehe.....pool-1-thread-6
hehe.....pool-1-thread-5
hehe.....pool-1-thread-1
hehe.....pool-1-thread-4
hehe.....pool-1-thread-3

下面再给出个线程池创建例子:

 1 package Lesson1218Thread;
 2 
 3 import java.util.concurrent.Callable;
 4 
 5 public class MyCallable implements Callable<Object> {
 6 
 7     @Override
 8     public Object call() throws Exception {
 9         System.out.println("MyCallable......" + Thread.currentThread().getName());
10         return 110;
11     }
12 }
13 package Lesson1218Thread;
14 
15 public class MyRunnable implements Runnable {
16     
17     @Override
18     public void run() {
19         
20         //for(int i=0;i<10;i++){
21             System.out.println("MyRunnable...... " + Thread.currentThread().getName());
22         }
23 }
24 
25 package Lesson1218Thread;
26 
27 import java.util.concurrent.Callable;
28 import java.util.concurrent.ExecutionException;
29 import java.util.concurrent.ExecutorService;
30 import java.util.concurrent.Executors;
31 import java.util.concurrent.Future;
32 import java.util.concurrent.FutureTask;
33 
34 public class ThreadDemo {
35 
36     public static void main(String[] args) {
37                 MyRunnable myRunnable = new MyRunnable();    
38                 //void execute(Runnable command);
39         ExecutorService es = Executors.newCachedThreadPool(); 
40         es.execute(myRunnable);
41         es.execute(myRunnable);  //每次都运行
42                 
43         MyCallable myCallable = new MyCallable();
44         FutureTask ft = new FutureTask<>(myCallable);
45         es.execute(ft);
46         //es.execute(ft); //重复不运行,?????                
47                 //Future<?> submit(Runnable task);
48         //<T> Future<T> submit(Callable<T> task);
49         Future ft0 = es.submit(ft); //没有运行,不知道为啥?????? 猜测是前面有es.execute(ft);
50         Future ft1 = es.submit(myCallable);    
51         
52         Future ft2 = es.submit(myRunnable);
53         
54         try {
55             System.out.println("ft0:"+ft0.get());
56             System.out.println("ft1:"+ft1.get());
57             System.out.println("ft2:"+ft2.get());
58         } catch (InterruptedException e) {
59             // TODO Auto-generated catch block
60             e.printStackTrace();
61         } catch (ExecutionException e) {
62             // TODO Auto-generated catch block
63             e.printStackTrace();
64         }
65         
66         return;
67     }
68     
69 }

运行结果:

MyRunnable...... pool-1-thread-1
MyCallable......pool-1-thread-1
MyRunnable...... pool-1-thread-2
MyRunnable...... pool-1-thread-5
ft0:null
MyCallable......pool-1-thread-4
ft1:110
ft2:null

分析上面的运行过程:

ft2由于调的是Runnable,所以肯定没有返回值,ft0也是调的Runnable,所以也不会有返回值。并且在程序中也没有运行(至于为啥没有运行,猜测与line45冲突)。

Exetucor提供的创建线程池方法:

 上述代码中Executors类,提供了一系列工厂方法用于创先线程池,返回的线程池都实现了ExecutorService接口。
public static ExecutorService newFixedThreadPool(int nThreads) 
创建固定数目线程的线程池。
public static ExecutorService newCachedThreadPool() 
创建一个可缓存的线程池,调用execute 将重用以前构造的线程(如果线程可用)。如果现有线程没有可用的,则创建一个新线程并添加到池中。终止并从缓存中移除那些已有 60 秒钟未被使用的线程。
public static ExecutorService newSingleThreadExecutor() 
创建一个单线程化的Executor。
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) 
创建一个支持定时及周期性的任务执行的线程池,多数情况下可用来替代Timer类。

线程池运行的两个方法:submit()和execute()
submit()方法,传递一个Callable,或Runnable,返回Future,如果传递的是Callable,Future里面才真正有值。如果Executor后台线程池还没有完成Callable的计算,这调用返回Future对象的get()方法,会阻塞直到计算完成。

execute()方法,传递Runnable,没有返回值。 如果是Callable也可以封装成FutureTask后传进去执行,但是没有返回值。

结论:

1:使用继承子Thread类的子类来创建线程类时,多个线程无法共享线程类的实例变量

2:采用Ruunable接口的方式创建多个线程可以共享线程类的实例变量,这是因为在这种方式下,程序创建的Runnable对象只是线程的target,而多个线程可以共享一个target,所以多个线程可以共享一个实例变量

2.Java中如何创建进程

 进程的三个特点:

1:独立性:进程是系统中独立存在的实体,它可以独立拥有资源,每一个进程都有自己独立的地址空间,没有进程本身的运行,用户进程不可以直接访问其他进程的地址空间。

2:动态性:进程和程序的区别在于进程是动态的,进程中有时间的概念,进程具有自己的生命周期和各种不同的状态

3:并发性:多个进程可以在单个处理器上并发执行,互不影响

 并发性和并行性是不同的概念:并行是指同一时刻,多个命令在多个处理器上同时执行;并发是指在同一时刻,只有一条命令是在处理器上执行的,但多个进程命令被快速轮换执行,使得在宏观上具有多个进程同时执行的效果

第一种方法:通过 Runtime 类的 exec() 方法来创建进程

public class Runtime
extends Object
①、表示当前进程所在的虚拟机实例,每个Java应用程序都有一个Runtime类的Runtime ,允许应用程序与运行应用程序的环境进行接口。
②、由于任何进程只会运行与一个虚拟机实例当中,即只会产生一个虚拟机实例(底层源码采用 单例模式)
③、当前运行时可以从getRuntime方法获得。
由上面源码可以看到,构造器私有化了,即外部我们不能 new 一个新的 Runtime 实例,而内部给了我们一个获取 Runtime 实例的方法 getRuntime() 。
通过 Runtime 类创建一个 记事本的 进程
 1 package Lesson1218Thread;
 2 
 3 import java.io.IOException;
 4 
 5 public class ProcessDemo {
 6 
 7     public static void main(String[] args) throws IOException {
 8         Runtime runt = Runtime.getRuntime();
 9         runt.exec("notepad");
10     }
11 }

运行后:弹出一个notepad框框.

第二种方法:通过 ProcessBuilder 创建进程

public final class ProcessBuilder extends Object<br>
①、此类用于创建操作系统进程。
②、每个ProcessBuilder实例管理进程属性的集合。 start()方法使用这些属性创建一个新的Process实例。 start()方法可以从同一实例重复调用,以创建具有相同或相关属性的新子进程。

 1 package Lesson1218Thread;
 2 
 3 import java.io.IOException;
 4 
 5 public class ProcessDemo {
 6 
 7     public static void main(String[] args) throws IOException {
 8         Runtime runt = Runtime.getRuntime();
 9         runt.exec("notepad");
10         
11         ProcessBuilder pb = new ProcessBuilder("notepad");
12         pb.start();
13     }
14 }

 参照的帖子有:

https://blog.csdn.net/weixin_41891854/article/details/81265772   有问题

https://blog.csdn.net/u012843873/article/details/51314572

https://www.cnblogs.com/WJ-163/p/6261835.html

https://blog.csdn.net/u012973218/article/details/51280044

https://blog.csdn.net/renyl_blog/article/details/78204590

https://www.cnblogs.com/ysocean/p/6883491.html

原文地址:https://www.cnblogs.com/beilou310/p/10136071.html