精华内容
下载资源
问答
  • 创建线程的方式有哪些
    千次阅读
    2022-03-22 15:26:58

    创建线程的几种方式: 


    方式1:通过继承Thread类创建线程
    步骤:1.定义Thread类的子类,并重写该类的run方法,该方法的方法体就是线程需要执行的任务,因此run()方法也被称为线程执行体
    2.创建Thread子类的实例,也就是创建了线程对象
    3.启动线程,即调用线程的start()方法

    方式2:通过实现Runnable接口创建线程
    步骤:1.定义Runnable接口的实现类,一样要重写run()方法,和第一种方式一样,这里的run()方法也是线程的执行体
    2.创建Runnable实现类的实例,并用这个实例作为Thread的target来创建Thread对象,这个Thread类才是真正的线程对象
    3.依然是通过调用线程对象的start方法来启动线程

    方式3:使用Callable和Future来创建线程
    注意:和Runnable接口不一样,Callable接口提供了一个call()方法来作为线程的执行体,call()方法比run()方法功能要更加强大,call()方法可以有返回值,call()方法可以声明抛出异常(前两种如果要抛异常只能通过try,catch来实现)。
    步骤:1.创建Callable接口的实现类,并实现call()方法,然后创建该类的实例
    2.使用Future Task类来包装Callable对象。该FutureTask对象封装了Callable对象的call()方法的返回值
    3.使用FutureTask对象作为Thread对象的target创建并启动线程(因为FutureTask实现了Runnable接口并在重写的run方法中执行call方法)
    4.调用FutureTask对象的get方法来获取线程执行结束后的返回值

    方法4:通过线程池来创建线程
    注意:ExecutorService es = Executors.newFixedThreadPool(30);
    ExecutorService es = Executors.newCachedThreadPool();
    FixedThreadPool创建的线程池-》用户可以指定线程池大小,但指定了就不可变
    CachedThreadPool创建的线程池-》线程池大小可变

    几种创建线程方式的对比:
    实现Runnable和实现Callable接口方式基本相同,不过是后者执行call方法并且有返回值,而run方法无任何返回值,因此可以把这两种方式归为一种方式与继承Thread类的方式进行对比,差别如下(以实现接口方式为主):
    1.线程只是实现Runnable接口或Callable接口,还可以继承其他类(有点像接口和抽象类的区别,java是单继承的,但可以实现多个接口)
    2.实现接口的方式多个线程可以共享一个target对象,非常适合多线程处理同一份资源的情形
    3.如果需要访问当前线程,必须调用Thread.currentThread方法
    4.继承Thread类的线程类不能再继承其他父类(java单继承决定)
    因此,一般推荐采用实现接口的方式来创建线程。

    更多相关内容
  • 创建线程有哪些方式

    千次阅读 2019-05-13 16:23:36
    创建线程有哪些方式? 继承Thread类 实现Runnable接口 实现Callable接口,配合FutureTask 线程池 import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util....

    创建线程有哪些方式?

    • 继承Thread类
    • 实现Runnable接口
    • 实现Callable接口,配合FutureTask
    • 线程池
    import java.util.concurrent.Callable;
    import java.util.concurrent.ExecutionException;
    import java.util.concurrent.FutureTask;
    
    public class TestStratThread {
    	
    	public static void main(String[] args) {
    		testStrartSubThread();
    		testImplementsRunable();
    		testImplementsCallable();
    	}
    	
    	//测试继承Thread类的方式创建、启动线程
    	public static void testStrartSubThread() {
    		Thread t1 = new CustomThread();
    		t1.setName("CustomThread");
    		t1.start();
    	}
    	
    	//测试实现Runnable接口的方式创建、启动线程
    	public static void testImplementsRunable() {
    		Thread t2 = new Thread(new CustomRunnable());
    		t2.setName("CustomRunnable");
    		t2.start();
    	}
    	
    	//测试实现Callable接口的方式创建、启动线程
    	public static void testImplementsCallable() {
    		Callable<String> callable = new CustomCallable();
    		FutureTask<String> futureTask = new FutureTask<String>(callable);
    		Thread t3 = new Thread(futureTask);
    		t3.setName("CustomCallable");
    		t3.start();
    		try {
    			System.out.println(futureTask.get());
    		} catch (InterruptedException e) {
    			e.printStackTrace();
    		} catch (ExecutionException e) {
    			e.printStackTrace();
    		}
    	}
    
    }
    
    //继承自Thread,重写run方法
    class CustomThread extends Thread {
    
    	@Override
    	public void run() {
    		System.out.println(Thread.currentThread().getName());
    	}
    	
    }
    
    //实现Runnable接口,重写run方法
    class CustomRunnable implements Runnable {
    
    	public void run() {
    		System.out.println(Thread.currentThread().getName());
    	}
    	
    }
    
    //实现Callable接口,重写call方法
    class CustomCallable implements Callable<String> {
    
    	public String call() throws Exception {
    		System.out.println(Thread.currentThread().getName());
    		return "Callable Result";
    	}
    	
    }

     


    【Java面试题与答案】整理推荐

     

    展开全文
  • 线程创建常用的四种方式

    千次阅读 2021-03-24 16:28:17
    Java可以用四种方式创建线程,如下所示: 1)继承Thread类创建线程 2)实现Runnable接口创建线程 3)使用Callable和Future创建线程 4)使用线程池例如用Executor框架 下面让我们分别来看看这四种创建线程的...

    java中创建线程的四种方法以及区别
    Java使用Thread类代表线程,所有的线程对象都必须是Thread类或其子类的实例。Java可以用四种方式来创建线程,如下所示:

    1)继承Thread类创建线程

    2)实现Runnable接口创建线程

    3)使用Callable和Future创建线程

    4)使用线程池例如用Executor框架

    下面让我们分别来看看这四种创建线程的方法。

     

    1.继承Thread类创建线程

     

    通过继承Thread类来创建并启动多线程的一般步骤如下

    1】d定义Thread类的子类,并重写该类的run()方法,该方法的方法体就是线程需要完成的任务,run()方法也称为线程执行体。

    2】创建Thread子类的实例,也就是创建了线程对象

    3】启动线程,即调用线程的start()方法

    代码实例

    public class MyThread extends Thread{//继承Thread类
    
      public void run(){
      //重写run方法
    
      }
    
    }
    
    public class Main {
      public static void main(String[] args){
        new MyThread().start();//创建并启动线程
    
      }
    
    }

     

    2.实现Runnable接口创建线程

    通过实现Runnable接口创建并启动线程一般步骤如下:

    1】定义Runnable接口的实现类,一样要重写run()方法,这个run()方法和Thread中的run()方法一样是线程的执行体

    2】创建Runnable实现类的实例,并用这个实例作为Thread的target来创建Thread对象,这个Thread对象才是真正的线程对象

    3】第三部依然是通过调用线程对象的start()方法来启动线程

    代码实例:
     

    public class MyThread2 implements Runnable {//实现Runnable接口
    
      public void run(){
      //重写run方法
    
      }
    
    }
    
    public class Main {
      public static void main(String[] args){
        //创建并启动线程
    
        MyThread2 myThread=new MyThread2();
    
        Thread thread=new Thread(myThread);
    
        thread().start();
    
        //或者    new Thread(new MyThread2()).start();
    
      }
    
    }

     

     

    3.使用Callable和Future创建线程

    和Runnable接口不一样,Callable接口提供了一个call()方法作为线程执行体,call()方法比run()方法功能要强大。

    》call()方法可以有返回值

    》call()方法可以声明抛出异常

    Java5提供了Future接口来代表Callable接口里call()方法的返回值,并且为Future接口提供了一个实现类FutureTask,这个实现类既实现了Future接口,还实现了Runnable接口,因此可以作为Thread类的target。在Future接口里定义了几个公共方法来控制它关联的Callable任务。

    >boolean cancel(boolean mayInterruptIfRunning):视图取消该Future里面关联的Callable任务

    >V get():返回Callable里call()方法的返回值,调用这个方法会导致程序阻塞,必须等到子线程结束后才会得到返回值

    >V get(long timeout,TimeUnit unit):返回Callable里call()方法的返回值,最多阻塞timeout时间,经过指定时间没有返回抛出TimeoutException

    >boolean isDone():若Callable任务完成,返回True

    >boolean isCancelled():如果在Callable任务正常完成前被取消,返回True

    介绍了相关的概念之后,创建并启动有返回值的线程的步骤如下:

    1】创建Callable接口的实现类,并实现call()方法,然后创建该实现类的实例(从java8开始可以直接使用Lambda表达式创建Callable对象)。

    2】使用FutureTask类来包装Callable对象,该FutureTask对象封装了Callable对象的call()方法的返回值

    3】使用FutureTask对象作为Thread对象的target创建并启动线程(因为FutureTask实现了Runnable接口)

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

    代码实例:
     

    public class Main {
      public static void main(String[] args){
       MyThread3 th=new MyThread3();
    
       //使用Lambda表达式创建Callable对象
    
         //使用FutureTask类来包装Callable对象
    
       FutureTask<Integer> future=new FutureTask<Integer>(
    
        (Callable<Integer>)()->{
          return 5;
    
        }
    
        );
    
       new Thread(task,"有返回值的线程").start();//实质上还是以Callable对象来创建并启动线程
    
        try{
        System.out.println("子线程的返回值:"+future.get());//get()方法会阻塞,直到子线程执行结束才返回
    
        }catch(Exception e){
        ex.printStackTrace();
    
       }
    
      }
    
    }

     

    4.使用线程池例如用Executor框架

    1.5后引入的Executor框架的最大优点是把任务的提交和执行解耦。要执行任务的人只需把Task描述清楚,然后提交即可。这个Task是怎么被执行的,被谁执行的,什么时候执行的,提交的人就不用关心了。具体点讲,提交一个Callable对象给ExecutorService(如最常用的线程池ThreadPoolExecutor),将得到一个Future对象,调用Future对象的get方法等待执行结果就好了。Executor框架的内部使用了线程池机制,它在java.util.cocurrent 包下,通过该框架来控制线程的启动、执行和关闭,可以简化并发编程的操作。因此,在Java 5之后,通过Executor来启动线程比使用Thread的start方法更好,除了更易管理,效率更好(用线程池实现,节约开销)外,还有关键的一点:有助于避免this逃逸问题——如果我们在构造器中启动一个线程,因为另一个任务可能会在构造器结束之前开始执行,此时可能会访问到初始化了一半的对象用Executor在构造器中。

     

        Executor框架包括:线程池,Executor,Executors,ExecutorService,CompletionService,Future,Callable等。

     

        Executor接口中之定义了一个方法execute(Runnable command),该方法接收一个Runable实例,它用来执行一个任务,任务即一个实现了Runnable接口的类。ExecutorService接口继承自Executor接口,它提供了更丰富的实现多线程的方法,比如,ExecutorService提供了关闭自己的方法,以及可为跟踪一个或多个异步任务执行状况而生成 Future 的方法。 可以调用ExecutorService的shutdown()方法来平滑地关闭 ExecutorService,调用该方法后,将导致ExecutorService停止接受任何新的任务且等待已经提交的任务执行完成(已经提交的任务会分两类:一类是已经在执行的,另一类是还没有开始执行的),当所有已经提交的任务执行完毕后将会关闭ExecutorService。因此我们一般用该接口来实现和管理多线程。

     

        ExecutorService的生命周期包括三种状态:运行、关闭、终止。创建后便进入运行状态,当调用了shutdown()方法时,便进入关闭状态,此时意味着ExecutorService不再接受新的任务,但它还在执行已经提交了的任务,当素有已经提交了的任务执行完后,便到达终止状态。如果不调用shutdown()方法,ExecutorService会一直处在运行状态,不断接收新的任务,执行新的任务,服务器端一般不需要关闭它,保持一直运行即可。

     

     

        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类。

     

        这四种方法都是用的Executors中的ThreadFactory建立的线程,下面就以上四个方法做个比较

     

     


    newCachedThreadPool()                                                                                                                                             
    -缓存型池子,先查看池中有没有以前建立的线程,如果有,就 reuse.如果没有,就建一个新的线程加入池中
    -缓存型池子通常用于执行一些生存期很短的异步型任务
     因此在一些面向连接的daemon型SERVER中用得不多。但对于生存期短的异步任务,它是Executor的首选。
    -能reuse的线程,必须是timeout IDLE内的池中线程,缺省     timeout是60s,超过这个IDLE时长,线程实例将被终止及移出池。
      注意,放入CachedThreadPool的线程不必担心其结束,超过TIMEOUT不活动,其会自动被终止。
     


    newFixedThreadPool(int)                                                          
    -newFixedThreadPool与cacheThreadPool差不多,也是能reuse就用,但不能随时建新的线程
    -其独特之处:任意时间点,最多只能有固定数目的活动线程存在,此时如果有新的线程要建立,只能放在另外的队列中等待,直到当前的线程中某个线程终止直接被移出池子
    -和cacheThreadPool不同,FixedThreadPool没有IDLE机制(可能也有,但既然文档没提,肯定非常长,类似依赖上层的TCP或UDP IDLE机制之类的),所以FixedThreadPool多数针对一些很稳定很固定的正规并发线程,多用于服务器
    -从方法的源代码看,cache池和fixed 池调用的是同一个底层 池,只不过参数不同:
    fixed池线程数固定,并且是0秒IDLE(无IDLE)    
    cache池线程数支持0-Integer.MAX_VALUE(显然完全没考虑主机的资源承受能力),60秒IDLE  
     
    newScheduledThreadPool(int)    
    -调度型线程池
    -这个池子里的线程可以按schedule依次delay执行,或周期执行
     
    SingleThreadExecutor()    
    -单例线程,任意时间池中只能有一个线程
    -用的是和cache池和fixed池相同的底层池,但线程数目是1-1,0秒IDLE(无IDLE)
     
     

     

        一般来说,CachedTheadPool在程序执行过程中通常会创建与所需数量相同的线程,然后在它回收旧线程时停止创建新线程,因此它是合理的Executor的首选,只有当这种方式会引发问题时(比如需要大量长时间面向连接的线程时),才需要考虑用FixedThreadPool。(该段话摘自《Thinking in Java》第四版)

     

                             

    Executor执行Runnable任务
        通过Executors的以上四个静态工厂方法获得 ExecutorService实例,而后调用该实例的execute(Runnable command)方法即可。一旦Runnable任务传递到execute()方法,该方法便会自动在一个线程上

     

     

     
    import java.util.concurrent.ExecutorService;   
    import java.util.concurrent.Executors;   
      
    public class TestCachedThreadPool{   
        public static void main(String[] args){   
            ExecutorService executorService = Executors.newCachedThreadPool();   
    //      ExecutorService executorService = Executors.newFixedThreadPool(5);  
    //      ExecutorService executorService = Executors.newSingleThreadExecutor();  
            for (int i = 0; i < 5; i++){   
                executorService.execute(new TestRunnable());   
                System.out.println("************* a" + i + " *************");   
            }   
            executorService.shutdown();   
        }   
    }   
      
    class TestRunnable implements Runnable{   
        public void run(){   
            System.out.println(Thread.currentThread().getName() + "线程被调用了。");   
        }   
    }  
    


     

     

     

     

       某次执行后的结果如下:

     

     

       从结果中可以看出,pool-1-thread-1和pool-1-thread-2均被调用了两次,这是随机的,execute会首先在线程池中选择一个已有空闲线程来执行任务,如果线程池中没有空闲线程,它便会创建一个新的线程来执行任务。

     

     

    Executor执行Callable任务
        在Java 5之后,任务分两类:一类是实现了Runnable接口的类,一类是实现了Callable接口的类。两者都可以被ExecutorService执行,但是Runnable任务没有返回值,而Callable任务有返回值。并且Callable的call()方法只能通过ExecutorService的submit(Callable<T> task) 方法来执行,并且返回一个 <T>Future<T>,是表示任务等待完成的 Future。

     

        Callable接口类似于Runnable,两者都是为那些其实例可能被另一个线程执行的类设计的。但是 Runnable 不会返回结果,并且无法抛出经过检查的异常而Callable又返回结果,而且当获取返回结果时可能会抛出异常。Callable中的call()方法类似Runnable的run()方法,区别同样是有返回值,后者没有。

     

        当将一个Callable的对象传递给ExecutorService的submit方法,则该call方法自动在一个线程上执行,并且会返回执行结果Future对象。同样,将Runnable的对象传递给ExecutorService的submit方法,则该run方法自动在一个线程上执行,并且会返回执行结果Future对象,但是在该Future对象上调用get方法,将返回null。

     

        下面给出一个Executor执行Callable任务的示例代码:

     

    
    import java.util.ArrayList;   
    import java.util.List;   
    import java.util.concurrent.*;   
      
    public class CallableDemo{   
        public static void main(String[] args){   
            ExecutorService executorService = Executors.newCachedThreadPool();   
            List<Future<String>> resultList = new ArrayList<Future<String>>();   
      
            //创建10个任务并执行   
            for (int i = 0; i < 10; i++){   
                //使用ExecutorService执行Callable类型的任务,并将结果保存在future变量中   
                Future<String> future = executorService.submit(new TaskWithResult(i));   
                //将任务执行结果存储到List中   
                resultList.add(future);   
            }   
      
            //遍历任务的结果   
            for (Future<String> fs : resultList){   
                    try{   
                        while(!fs.isDone);//Future返回如果没有完成,则一直循环等待,直到Future返回完成  
                        System.out.println(fs.get());     //打印各个线程(任务)执行的结果   
                    }catch(InterruptedException e){   
                        e.printStackTrace();   
                    }catch(ExecutionException e){   
                        e.printStackTrace();   
                    }finally{   
                        //启动一次顺序关闭,执行以前提交的任务,但不接受新任务  
                        executorService.shutdown();   
                    }   
            }   
        }   
    }   
      
      
    class TaskWithResult implements Callable<String>{   
        private int id;   
      
        public TaskWithResult(int id){   
            this.id = id;   
        }   
      
        /**  
         * 任务的具体过程,一旦任务传给ExecutorService的submit方法, 
         * 则该方法自动在一个线程上执行 
         */   
        public String call() throws Exception {  
            System.out.println("call()方法被自动调用!!!    " + Thread.currentThread().getName());   
            //该返回结果将被Future的get方法得到  
            return "call()方法被自动调用,任务返回的结果是:" + id + "    " + Thread.currentThread().getName();   
        }   
    }  


     

     

     

        某次执行结果如下:

       

     

        从结果中可以同样可以看出,submit也是首先选择空闲线程来执行任务,如果没有,才会创建新的线程来执行任务。另外,需要注意:如果Future的返回尚未完成,则get()方法会阻塞等待,直到Future完成返回,可以通过调用isDone()方法判断Future是否完成了返回。

     

     

     

    自定义线程池
        自定义线程池,可以用ThreadPoolExecutor类创建,它有多个构造方法来创建线程池,用该类很容易实现自定义的线程池,这里先贴上示例程序:

    import java.util.concurrent.ArrayBlockingQueue;   
    import java.util.concurrent.BlockingQueue;   
    import java.util.concurrent.ThreadPoolExecutor;   
    import java.util.concurrent.TimeUnit;   
      
    public class ThreadPoolTest{   
        public static void main(String[] args){   
            //创建等待队列   
            BlockingQueue<Runnable> bqueue = new ArrayBlockingQueue<Runnable>(20);   
            //创建线程池,池中保存的线程数为3,允许的最大线程数为5  
            ThreadPoolExecutor pool = new ThreadPoolExecutor(3,5,50,TimeUnit.MILLISECONDS,bqueue);   
            //创建七个任务   
            Runnable t1 = new MyThread();   
            Runnable t2 = new MyThread();   
            Runnable t3 = new MyThread();   
            Runnable t4 = new MyThread();   
            Runnable t5 = new MyThread();   
            Runnable t6 = new MyThread();   
            Runnable t7 = new MyThread();   
            //每个任务会在一个线程上执行  
            pool.execute(t1);   
            pool.execute(t2);   
            pool.execute(t3);   
            pool.execute(t4);   
            pool.execute(t5);   
            pool.execute(t6);   
            pool.execute(t7);   
            //关闭线程池   
            pool.shutdown();   
        }   
    }   
      
    class MyThread implements Runnable{   
        @Override   
        public void run(){   
            System.out.println(Thread.currentThread().getName() + "正在执行。。。");   
            try{   
                Thread.sleep(100);   
            }catch(InterruptedException e){   
                e.printStackTrace();   
            }   
        }   
    }  


      运行结果如下:

     

        从结果中可以看出,七个任务是在线程池的三个线程上执行的。这里简要说明下用到的ThreadPoolExecuror类的构造方法中各个参数的含义。   

     

    public ThreadPoolExecutor (int corePoolSize, int maximumPoolSize, long         keepAliveTime, TimeUnit unit,BlockingQueue<Runnable> workQueue)

    corePoolSize:线程池中所保存的线程数,包括空闲线程。

    maximumPoolSize:池中允许的最大线程数。

    keepAliveTime:当线程数大于核心数时,该参数为所有的任务终止前,多余的空闲线程等待新任务的最长时间。

    unit:等待时间的单位。

    workQueue:任务执行前保存任务的队列,仅保存由execute方法提交的Runnable任务。

     

    总结 四种创建线程方法对比

    实现Runnable和实现Callable接口的方式基本相同,不过是后者执行call()方法有返回值,后者线程执行体run()方法无返回值,因此可以把这两种方式归为一种这种方式与继承Thread类的方法之间的差别如下:

    1、线程只是实现Runnable或实现Callable接口,还可以继承其他类。

    2、这种方式下,多个线程可以共享一个target对象,非常适合多线程处理同一份资源的情形。

    3、但是编程稍微复杂,如果需要访问当前线程,必须调用Thread.currentThread()方法。

    4、继承Thread类的线程类不能再继承其他父类(Java单继承决定)。

    5、前三种的线程如果创建关闭频繁会消耗系统资源影响性能,而使用线程池可以不用线程的时候放回线程池,用的时候再从线程池取,项目开发中主要使用线程池

    注:在前三种中一般推荐采用实现接口的方式来创建多线程
     


     

    展开全文
  • java创建线程的四种方式

    千次阅读 2022-03-14 11:52:41
    4.使用线程池创建线程 1.直接初始化Thead类,实现Runnable接口 查看Thread类源码可以发现,下面这么一个构造参数,target是线程启动的时候要调用的方法(Runnable接口中个run方法),如果为空,那这个类的运行...

    目录

    1.直接初始化Thead类,实现Runnable接口

    2.继承Thread类

    3.实现callable接口

    4.使用线程池创建线程


    1.直接初始化Thead类,实现Runnable接口

    查看Thread类源码可以发现,有下面这么一个构造参数,target是线程启动的时候要调用的方法(Runnable接口中有个run方法),如果为空,那这个类的运行方法什么都不做

    那我们是否可以直接初始化线程,然后调用start方法启动呢

    答案是可以的

        public Thread(Runnable target) {
            init(null, target, "Thread-" + nextThreadNum(), 0);
        }

    测试代码如下

        public static void main(String args[]){
            new Thread(new Runnable(){
    
                @Override
                public void run() {
                    System.out.println("test create thread by Runable");
                }
            }).start();
        }
        
    Connected to the target VM, address: '127.0.0.1:57009', transport: 'socket'
    test create thread by Runable
    Disconnected from the target VM, address: '127.0.0.1:57009', transport: 'socket'

    当然我们也可以写一个类去实现Runnable接口

    测试代码如下

    class Demo implements Runnable{
    
        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName());
        }
    }
    
    public static void main(String args[]) {
        Demo d = new Demo();
        Thread thread1 = new Thread(d,"first thread");
        Thread thread2 = new Thread(d,"second thread");
        thread1.start();
        thread2.start();
    }
        
    Connected to the target VM, address: '127.0.0.1:65445', transport: 'socket'
    second thread
    first thread
    Disconnected from the target VM, address: '127.0.0.1:65445', transport: 'socket'

    上面这种方式只new了一个Demo,所以thread1和thread2会共享类中的局部变量

    我们测试下这种方式共享局部变量的效果,可以仔细看看count这个变量的变化,每个线程不是10->1这么变化的,说明count这个变量被2个线程共享了

    class Demo implements Runnable{
    
        private int count =10;
        @Override
        public void run() {
            for(int i=0;i<10;i++){
                System.out.println(Thread.currentThread().getName()+count--);
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
    
        }
    }
    
    public static void main(String args[]) {
        Demo d = new Demo();
        Thread thread1 = new Thread(d,"first thread");
        Thread thread2 = new Thread(d,"second thread");
        thread1.start();
        thread2.start();
    }
    
    Connected to the target VM, address: '127.0.0.1:51427', transport: 'socket'
    first thread10
    second thread9
    first thread8
    second thread7
    second thread6
    first thread5
    second thread4
    first thread3
    first thread2
    second thread2
    first thread1
    second thread1
    second thread0
    first thread-1
    second thread-2
    first thread-3
    second thread-4
    first thread-5
    second thread-6
    first thread-7
    Disconnected from the target VM, address: '127.0.0.1:51427', transport: 'socket'
    
    Process finished with exit code 0
    

    如果我们不想共享类中的变量,那只能初始化2个Demo类,代码如下

    public static void main(String args[]) {
        Demo d1 = new Demo();
        Demo d2 = new Demo();
        Thread thread1 = new Thread(d1,"first thread");
        Thread thread2 = new Thread(d2,"second thread");
        thread1.start();
        thread2.start();
    }
    
    Connected to the target VM, address: '127.0.0.1:65346', transport: 'socket'
    first thread10
    second thread10
    first thread9
    second thread9
    second thread8
    first thread8
    first thread7
    second thread7
    first thread6
    second thread6
    first thread5
    second thread5
    first thread4
    second thread4
    first thread3
    second thread3
    first thread2
    second thread2
    first thread1
    second thread1
    Disconnected from the target VM, address: '127.0.0.1:65346', transport: 'socket'

    2.继承Thread类

    前面我们通过初始化Thread类的方式创建线程,那我们是否可以新建一个类继承Thread类,然后创建线程呢,那肯定是可以的

    测试代码如下

    class Demo extends Thread{
    
        public void run() {
            System.out.println(Thread.currentThread().getName());
    
        }
    }
    
    public static void main(String args[]) {
        Demo d = new Demo();
        Thread thread1 = new Thread(d,"create thread by extends Thread");
        thread1.start();
    }
    
    Connected to the target VM, address: '127.0.0.1:64623', transport: 'socket'
    create thread by extends Thread
    Disconnected from the target VM, address: '127.0.0.1:64623', transport: 'socket'

    3.实现callable接口

    A task that returns a result and may throw an exception. Implementors define a single method with no arguments called call. The Callable interface is similar to Runnable, in that both are designed for classes whose instances are potentially executed by another thread. A Runnable, however, does not return a result and cannot throw a checked exception.(返回结果并可能抛出异常的任务。实现者定义了一个不带参数的方法,称为call。 Callable接口与Runnable接口类似,两者都是为其实例可能由另一个线程执行的类设计的。然而,Runnable不返回结果,也不能抛出检查异常。)

    我们查看jdk中callable类的注释,可以知道几个关键的信息

    1.Callable接口和Runnable接口类似,都是为其他线程执行实例而设计的

    2.Callable可以接受结果

    3.Callable可以检查异常

    通过jdk的FutureTask类,我们可以看到其实现了RunableFuture接口,然后这个接口是继承的Runable

    我们来看一下他的源码

    我们可以通过这个方法来实例化FutureTask

    Creates a FutureTask that will, upon running, execute the given Callable. Params: callable – the callable task Throws: NullPointerException – if the callable is null

    public FutureTask(Callable<V> callable) {
        if (callable == null)
            throw new NullPointerException();
        this.callable = callable;
        this.state = NEW;       // ensure visibility of callable
    }

    然后通过get方法获取返回结果

    public V get() throws InterruptedException, ExecutionException {
        int s = state;
        if (s <= COMPLETING)
            s = awaitDone(false, 0L);
        return report(s);
    }

    Returns result or throws exception for completed task. Params: s – completed state value

    private V report(int s) throws ExecutionException {
        Object x = outcome;
        if (s == NORMAL)
            return (V)x;
        if (s >= CANCELLED)
            throw new CancellationException();
        throw new ExecutionException((Throwable)x);
    }

    测试代码如下

    class Demo implements Callable{
    
        @Override
        public Object call() throws Exception {
            int sum=0;
            for(int i=0;i<10;i++){
                sum=sum+i;
                Thread.sleep(300);
            }
            System.out.println("sum:"+sum);
            return sum;
        }
    }
    
    
    public static void main(String args[]) throws Exception{
    
        System.out.println("time1:"+new Date());
        Demo d = new Demo();
        FutureTask futureTask = new FutureTask(d);
        Thread thread = new Thread(futureTask);
        thread.start();
        System.out.println("time2:"+new Date());
        Object sum = futureTask.get(5,TimeUnit.SECONDS);
        System.out.println("time3:"+new Date());
        System.out.println(sum);
    
    }
    
    Connected to the target VM, address: '127.0.0.1:60755', transport: 'socket'
    time1:Fri Mar 11 17:54:19 CST 2022
    time2:Fri Mar 11 17:54:19 CST 2022
    sum:45
    time3:Fri Mar 11 17:54:22 CST 2022
    45
    Disconnected from the target VM, address: '127.0.0.1:60755', transport: 'socket'

    我们可以发现time2和time3之间间隔了3s,然后获得了结果

    4.使用线程池创建线程

    线程池创建线程的优点

    Thread pools address two different problems: they usually provide improved performance when executing large numbers of asynchronous tasks, due to reduced per-task invocation overhead, and they provide a means of bounding and managing the resources, including threads, consumed when executing a collection of tasks. Each ThreadPoolExecutor also maintains some basic statistics, such as the number of completed tasks.(线程池解决两个不同的问题:它们通常在执行大量异步任务时提高性能,因为减少了每个任务的调用开销,并且它们提供了一种方法来限制和管理执行任务集合时消耗的资源(包括线程)。每个ThreadPoolExecutor还维护一些基本的统计信息,比如完成任务的数量。)

    1.使用ThreadPoolExecutor创建线程.这种方式比较灵活

    Params:

    • corePoolSize – the number of threads to keep in the pool, even if they are idle, unless allowCoreThreadTimeOut is set
    • maximumPoolSize – the maximum number of threads to allow in the pool
    • keepAliveTime – when the number of threads is greater than the core, this is the maximum time that excess idle threads will wait for new tasks before terminating.
    • unit – the time unit for the keepAliveTime argument
    • workQueue – the queue to use for holding tasks before they are executed. This queue will hold only the Runnable tasks submitted by the execute method.
    • threadFactory – the factory to use when the executor creates a new thread
    • handler – the handler to use when execution is blocked because the thread bounds and queue capacities are reached
    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {
        if (corePoolSize < 0 ||
            maximumPoolSize <= 0 ||
            maximumPoolSize < corePoolSize ||
            keepAliveTime < 0)
            throw new IllegalArgumentException();
        if (workQueue == null || threadFactory == null || handler == null)
            throw new NullPointerException();
        this.acc = System.getSecurityManager() == null ?
                null :
                AccessController.getContext();
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }

    下面我们来测试一下,这里使用callable,当然也可以使用runnable

    class Demo implements Callable{
    
        @Override
        public Object call() throws Exception {
            int sum=0;
            for(int i=0;i<10;i++){
                sum=sum+i;
                Thread.sleep(300);
            }
            System.out.println("sum:"+sum);
            return sum;
        }
    }
    
        public static void main(String args[]) throws Exception{
    
            ThreadPoolExecutor pool = new ThreadPoolExecutor(10,20,60L,TimeUnit.SECONDS,new SynchronousQueue<>(), new ThreadFactoryBuilder().setNameFormat("test thread pool").build(),new ThreadPoolExecutor.AbortPolicy());
            Demo d = new Demo();
            Future f = pool.submit(d);
            System.out.println("当前时间:"+new Date());
            Object result = f.get(5L,TimeUnit.SECONDS);
            System.out.println("当前时间:"+new Date()+"==结果:"+result);
        }
        
    当前时间:Mon Mar 14 11:28:42 CST 2022
    sum:45
    当前时间:Mon Mar 14 11:28:45 CST 2022==结果:45

    2.使用Executors创建线程池

    这种方法比较简单,其实就是对上面的方法进行了一些封装,建议使用上面的方法,因为可以看到具体的参数

    Executorst提供了几个常见构造方法

    • newFixedThreadPool--
    • newSingleThreadExecutor
    • newCachedThreadPool
    • newScheduledThreadPool

    我们来找一个这个类的构造参数看一下

    Creates a thread pool that reuses a fixed number of threads operating off a shared unbounded queue. At any point, at most nThreads threads will be active processing tasks. If additional tasks are submitted when all threads are active, they will wait in the queue until a thread is available. If any thread terminates due to a failure during execution prior to shutdown, a new one will take its place if needed to execute subsequent tasks. The threads in the pool will exist until it is explicitly shutdown.
    Params:
    nThreads – the number of threads in the pool
    Returns:
    the newly created thread pool
    Throws:
    IllegalArgumentException – if nThreads <= 0

    public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }

    测试一下

    class Demo implements Runnable{
    
        @Override
        public void run() {
            int sum=0;
            for(int i=0;i<10;i++){
                sum=sum+i;
                try {
                    Thread.sleep(300);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("sum:"+sum);
    
        }
    }
    
        public static void main(String args[]) throws Exception{
            ExecutorService executorService = Executors.newFixedThreadPool(10);
            Demo d = new Demo();
            executorService.submit(d);
        }
        
    Connected to the target VM, address: '127.0.0.1:55962', transport: 'socket'
    sum:45
    Disconnected from the target VM, address: '127.0.0.1:55962', transport: 'socket'

    展开全文
  • 创建线程几种方式

    千次阅读 2019-04-12 12:07:59
    创建线程的几种方式方式1:通过继承Thread类创建线程 步骤:1.定义Thread类的子类,并重写该类的run方法,该方法的方法体就是线程需要执行的任务,因此run()方法也被称为线程执行体 2.创建Thread子类的实例,...
  • 创建线程的四种方式

    千次阅读 2019-11-21 00:29:29
    5.线程池创建线程 1. 线程的创建四种方式,分别为: 继承Thread类 实现Runnable接口 实现Callable接口 使用Executor框架创建线程池 编写多线程程序是为了实现多任务的并发执行,从而能够更好地与用户交互。...
  • Java创建线程

    千次阅读 多人点赞 2022-04-05 22:49:44
    既然说到线程,在这里就给大家普及一下线程线程(Thread): 是操作系统能够进行运算调度的最小单位。 它被包含在进程之中,是进程中的实际运作单位。 一个线程指的是进程中一个单一顺序的控制流。 ...
  • ======创建线程方式====== 继承Thead类,重写run()方法 实现Runnable类,重写run()方法 使用lombda 实现Callable类,重写call()方法 使用线程池 ======停止线程的方式====== 使用stop()方法 使用suspend()和resume()...
  • 创建线程有哪几种方式?3种

    千次阅读 2020-07-20 08:17:10
    实现Runnable接口 public class RunnableDemoTest implements Runnable{ public void run() { System.out.println("实现Runnable开启线程!"); } public static void main(String[] args) { Thread thread = new ...
  • java创建线程(Thread)的5种方式

    千次阅读 2021-01-16 17:07:50
    java创建线程(Thread)的4种方式方式一:继承于Thread类方式二:实现Runnable接口方式三:实现Callable接口方式四:使用线程池 方式一:继承于Thread类 步骤: 1.创建一个继承于Thread类的子类 2.重写Thread类的...
  • 线程创建方式和区别 1.继承Thread类 (1)创建Thread类的子类,并重写该类的run方法,该run方法的方法体就代表了线程要完成的任务。因此把run()方法称为执行体。 (2)创建了Thread子类的实例,即创建线程对象。...
  • 最近读完了深入理解Java虚拟机大部分理论章节,感觉对JVM内部执行豁然开朗,并且发现并发编程和虚拟机工作也密不可分,现在再系统读一读多线程、并发这块的书籍,以前也学过多线程,不过没有系统看书,图书馆选了一...
  • 线程创建的四种方式

    万次阅读 多人点赞 2018-03-30 14:34:14
    Java可以用四种方式创建线程,如下所示: 1)继承Thread类创建线程 2)实现Runnable接口创建线程 3)使用Callable和Future创建线程 4)使用线程池例如用Executor框架 下面让我们分别来看看这四种创建线程的...
  • 众所周知,我们在创建线程四种方法可以用,分别是: 1、继承Thread类创建线程 2、实现Runnable接口创建线程 3、使用Callable和Future创建线程 4、使用线程池创建(使用java.util.concurrent.Executor接口) 其中...
  • C++多线程,多线程创建,std::thread,普通函数/成员函数/仿函数/匿名函数
  • java 创建线程的三种方式、创建线程池的四种方式

    万次阅读 多人点赞 2019-02-23 21:01:44
    java创建线程的三种方式: 继承Thread类创建线程类 实现Runnable接口 通过Callable和Future创建线程 java创建线程池的四种方式: newCachedThreadPool 创建一个可缓存的线程池,如果线程池长度超过处理...
  • 如何创建线程创建线程有几种方法?怎么实现的?
  • 线程的三种创建方式以及区别

    千次阅读 2021-03-07 18:10:39
    这里写目录标题一:创建线程的三种方式1.继承类Thread的方式2.继承Runnable接口方式3.使用Callable接口创建线程4.这三种创建线程的区别 一:创建线程的三种方式 1.继承类Thread的方式 注意:这种创建线程方式不能再...
  • Lambda方式创建线程

    千次阅读 2021-05-24 16:06:00
    Lamdba创建线程,是对匿名内部类创建多线程的简化 由三部分组成: a、一些参数 b、一个箭头 c、一段代码 格式: (参数列表)->{一些重写方法的代码} 解释说明: ():接口中抽象方法的参数列表,...
  • 进程(process):是程序的一次执行过程或是正在运行的一段程序、是一个动态的过程:他自身的产生、存在和消亡过程。 线程(thread):进程可进一步细化为线程,是一个程序内部的一条执行路径。若一个进程中同一...
  • 创建线程的两种方式 二.启动线程start和run的分析   一.创建线程的两种方式  java提供两种创建线程方式: 1.继承Thread private void createThread(){  new MyThread().start(); } private class...
  • 关于时间,创建线程使用是直接向系统申请资源的,这里调用系统函数进行分配资源的话耗时不好说。 关于资源,Java线程的线程栈所占用的内存是在Java堆外的,所以是不受java程序控制的,只受系统资源限制,默认一个...
  • 多线程(一):创建线程和线程的常用方法

    万次阅读 多人点赞 2018-09-01 19:14:23
    一:为什么要学多线程 应付面试 :多线程几乎是面试中必问的题,所以掌握一定的基础知识是必须的。 了解并发编程:实际工作中很少写多线程的代码,这部分代码一般都被人封装起来了,在业务中使用多线程的机会也...
  • 创建线程的三种方法

    千次阅读 2021-12-13 13:42:57
    第一种方法 使用Thread的方法创建线程 1.写一个Thread的子类去继承Thread 2.复写run方法,方法内写线程的执行代码 3.创建Thread的子类对象 4.调用start()方法,开启线程 第二种方法 实现Runnable接口的方法 1.写...
  • Java创建线程的四种方式

    万次阅读 多人点赞 2018-07-28 22:10:16
    1.继承Thread类实现多线程  run()为线程类的核心方法,相当于主线程的main方法,是每个线程的入口  a.一个线程调用 两次start()方法将会抛出线程状态异常,也就是的start()只可以被调用一次   b.native生明的...
  • java创建线程的三种方式: 继承Thread类创建线程类 通过Runable接口创建线程类 通过Callable和FutureTask创建线程 总结下: 实现Runnable和实现Callable接口的方式基本相同,不过是后者执行call()方法返回值。 1...
  • C++11创建线程的三种方式

    千次阅读 2019-04-03 08:30:06
    通过类对象创建线程 class CObject { public: void operator ()() { cout << "线程开始运行" << endl; cout << "线程结束运行" << endl; } }; int main() { CObject obj; std::...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,757,309
精华内容 702,923
关键字:

创建线程的方式有哪些