zoukankan      html  css  js  c++  java
  • 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

  • 相关阅读:
    html URLRewriter生成静态页不能访问
    sql server 2008 不允许保存更改,您所做的更改要求删除并重新创建以下表
    IIS7.0 伪静态页配置
    hubbledotnet 定时更新索引
    今天开通了这个BLOG。
    ASP.NET公有六种验证控件 功能描叙
    Recommend of the Day:Orkut社区和明星推荐
    每日英语:Why You Need a Dictator in a Marriage
    每日英语:An Unhappy Middle in the Middle Kingdom
    每日英语:Web Browsers Are Reinvented
  • 原文地址:https://www.cnblogs.com/beilou310/p/10136071.html
Copyright © 2011-2022 走看看