精华内容
下载资源
问答
  • 线程创建的四种方式

    万次阅读 多人点赞 2018-03-30 14:34:14
    Java可以用四种方式创建线程,如下所示: 1)继承Thread类创建线程 2)实现Runnable接口创建线程 3)使用Callable和Future创建线程 4)使用线程池例如用Executor框架 下面让我们分别来看看这四种创建线程的...

    java中创建线程的四种方法以及区别

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

    1)继承Thread类创建线程

    2)实现Runnable接口创建线程

    3)使用Callable和Future创建线程

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

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

     

    ------------------------继承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();//创建并启动线程

      }

    }

    ------------------------实现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();

      }

    }

    ------------------------使用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();

       }

      }

    }

    ------------------------使用线程池例如用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()方法,该方法便会自动在一个线程上

     

     

    [java] view pl
     
    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、前三种的线程如果创建关闭频繁会消耗系统资源影响性能,而使用线程池可以不用线程的时候放回线程池,用的时候再从线程池取,项目开发中主要使用线程池

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

     

    展开全文
  • 学习笔记线程的创建方式

    万次阅读 2019-08-08 16:37:18
    线程创建方式 1.继承Thread类,重写run方法 public class a extends Thread{ public void run() { while(true) { try { sleep(50); } catch (InterruptedException e) { // TODO Auto-generated catch block e....

    学习笔记


    多线程创建方式

    1.继承Thread类,重写run方法

    public class a extends Thread{
    	public void run() {
    		while(true) {
    			try {
    				sleep(50);
    			} catch (InterruptedException e) {
    				// TODO Auto-generated catch block
    				e.printStackTrace();
    			}
    			System.out.println("旭旭520");
    		}
    	}`
    	
    
    	public static void main(String[] args) {
    		new a().start();
    		new b().start();
    		new c().start();
    	}
    	
    }
     class b extends Thread{
    	public void run() {
    		while(true) {
    			try {
    				sleep(50);
    			} catch (InterruptedException e) {
    				// TODO Auto-generated catch block
    				e.printStackTrace();
    			}
    			System.out.println("xx520");
    		}
    	}
    }
     class c extends Thread{
    	public void run() {
    		while(true) {
    			try {
    				sleep(50);
    			} catch (InterruptedException e) {
    				// TODO Auto-generated catch block
    				e.printStackTrace();
    			}
    			System.out.println("zz520");
    		}
    	}
    }
    

    执行结果:
    旭旭520
    zz520
    xx520
    旭旭520
    zz520
    xx520
    旭旭520
    zz520
    xx520
    旭旭520
    xx520
    zz520
    旭旭520
    xx520
    zz520
    旭旭520
    xx520
    zz520

    2.实现Runnable接口,重写run()方法,将类的实例作为Thread的参数

    public class a implements Runnable {
    	public void run() {
    		while (true) {
    			try {
    				Thread.sleep(50);
    			} catch (InterruptedException e) {
    				// TODO Auto-generated catch block
    				e.printStackTrace();
    			}
    			System.out.println("旭旭520");
    		}
    	}
    	public static void main(String[] args) {
    		new Thread(new a()).start();
    		new Thread(new b()).start();
    		new Thread(new c()).start();
    	}
    }
    
    class b implements Runnable {
    	public void run() {
    		while (true) {
    			try {
    				Thread.sleep(50);
    			} catch (InterruptedException e) {
    				// TODO Auto-generated catch block
    				e.printStackTrace();
    			}
    			System.out.println("xx520");
    		}
    	}
    }
    
    class c implements Runnable {
    	public void run() {
    		while (true) {
    			try {
    				Thread.sleep(50);
    			} catch (InterruptedException e) {
    				// TODO Auto-generated catch block
    				e.printStackTrace();
    			}
    			System.out.println("zz520");
    		}
    	}
    
    	
    }
    

    执行结果:
    旭旭520
    zz520
    xx520
    旭旭520
    zz520
    旭旭520
    xx520
    zz520
    旭旭520
    xx520
    zz520
    xx520
    旭旭520

    3.实现Callable接口,重写call方法将实现了Callable的类的一个实例作为FutureTask的参数,再将一个FutureTask的实例作为Thread的参数

    import java.util.concurrent.Callable;
    import java.util.concurrent.FutureTask;
    public class a implements Callable<String> {
    	public String call() {
    		while (true) {
    			try {
    				Thread.sleep(50);
    			} catch (InterruptedException e) {
    				// TODO Auto-generated catch block
    				e.printStackTrace();
    			}
    			System.out.println("zz520");
    		}
    	}
    	public static void main(String[] args) {
    		Callable<String> c1 = new a();
    		FutureTask<String> f1 = new FutureTask<String>(c1);
    		new Thread(f1).start();
    		Callable<String> c2 = new b();
    		FutureTask<String> f2 = new FutureTask<String>(c2);
    		new Thread(f2).start();
    		Callable<String> c3 = new c();
    		FutureTask<String> f3 = new FutureTask<String>(c3);
    		new Thread(f3).start();
    	}
    }
    class b implements Callable<String> {
    	public String call() {
    		while (true) {
    			try {
    				Thread.sleep(50);
    			} catch (InterruptedException e) {
    				// TODO Auto-generated catch block
    				e.printStackTrace();
    			}
    			System.out.println("旭旭520");
    		}
    	}
    }
    class c implements Callable<String> {
    	public String call() {
    		while (true) {
    			try {
    				Thread.sleep(50);
    			} catch (InterruptedException e) {
    				// TODO Auto-generated catch block
    				e.printStackTrace();
    			}
    			System.out.println("xx520");
    		}
    	}
    }
    

    执行结果:
    旭旭520
    xx520
    zz520
    旭旭520
    xx520
    旭旭520
    zz520
    xx520
    zz520
    旭旭520
    xx520

    4.使用线程池Executors类,使用ExecutorService e = Executors.newFixedThreadPool(5);再使用e.submit方法将实现了Runable接口的实例执行线程

    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    public class a implements Runnable {
    	public void run() {
    		while (true) {
    			try {
    				Thread.sleep(50);
    			} catch (InterruptedException e) {
    				// TODO Auto-generated catch block
    				e.printStackTrace();
    			}
    			System.out.println("xx520");
    		}
    	}
    	public static void main(String[] args) {
    		ExecutorService e = Executors.newFixedThreadPool(5);
    		e.submit(new a());
    		e.submit(new b());
    		e.submit(new c());
    	}
    }
    class b implements Runnable {
    	public void run() {
    		while (true) {
    			try {
    				Thread.sleep(50);
    			} catch (InterruptedException e) {
    				// TODO Auto-generated catch block
    				e.printStackTrace();
    			}
    			System.out.println("旭旭520");
    		}
    	}
    }
    class c implements Runnable {
    	public void run() {
    		while (true) {
    			try {
    				Thread.sleep(50);
    			} catch (InterruptedException e) {
    				// TODO Auto-generated catch block
    				e.printStackTrace();
    			}
    			System.out.println("zz520");
    		}
    	}
    }
    

    执行结果:
    zz520
    xx520
    旭旭520
    zz520
    xx520
    旭旭520
    xx520
    zz520
    zz520
    xx520
    旭旭520

    5、线程池

     ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(1, 10, 1000, TimeUnit.MILLISECONDS, new PriorityBlockingQueue<Runnable>(), Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy());
            threadPoolExecutor.execute(new AlertBug());
    
    展开全文
  • 下面详细介绍两种线程创建的方式,这个内容为必须掌握的。到最后会对比两种方式创建线程的优势和劣势。 1继承Thread类创建线程: 1,创建一个类,继承Thread类并覆盖Thread类的run方法。为什么要覆盖run方法?...




    创建线程有两种方式:第一种是继承Thread类,第二种是实现Runnable接口。下面详细介绍两种线程创建的方式,这个内容为必须掌握的。到最后会对比两种方式创建线程的优势和劣势。

    1继承Thread类创建线程:

    1,创建一个类,继承Thread类并覆盖Thread类的run方法。为什么要覆盖run方法?该run方法的方法体就是代表了线程要完成的任务,因此,run方法也称为线程执行体。

    2,创建Thread子类的实例,即创建了线程对象。

    3,用线程对象调用start方法启动线程。

    下面来看看代码是如何实现的:

    public class ThreadCreat
    {
    	public static void main(String args[]){
    		MyThread mt = new MyThread(); //创建Thread子类实例对象
    		mt.start(); //通过子类对象的start方法开启线程
    		for(int x=0;x<10;x++)
    			System.out.println(Thread.currentThread().getName()+"-->"+x);
    		
    	} 
    }
    class MyThread extends Thread  //新建一个类继承Thread
    {
    	public void run(){  //覆盖run方法,方体即线程体
    		for(int i=0;i<10;i++)
    			System.out.println(this.getName()+"--->"+i);
    	}
    }



    ---------- 运行java程序 ----------

    main-->0

    main-->1

    Thread-0--->0

    Thread-0--->1

    main-->2

    main-->3

    main-->4

    main-->5

    main-->6

    main-->7

    main-->8

    main-->9

    Thread-0--->2

    Thread-0--->3

    Thread-0--->4

    Thread-0--->5

    Thread-0--->6

    Thread-0--->7

    Thread-0--->8

    Thread-0--->9

    输出完成 (耗时 ) - 正常终止

    在打印时,我使用了Thread类中静态方法currentThread ,调用该方法可以返回当前正在执行的线程对象(Thread)。从输出可以看到,在本程序中,开启了两条线程,一条是主线程:

    main,另外一条是Thread-0线程。这两条线程是随机交替进行的。对于单核cpu在某一个时间点上只能执行一个命名,由于cpu切换的非常快,人是无法察觉它的切换,给人的感觉实在同时执行多个任务。知道了这一点,有利于我们理解多线程。

    下面再来看看为何会出现上面的输出结果:


    2线程创建的第二种方式:实现Runnable接口

    1,创建一个类实现Runnable接口,并覆盖run方法,run方法体就是线程体。

    2,新建一个Thread本类对象,在新建该对象时,使用的是其Thread(Runnable  target)构造方法,将Runnable 子类对象作为参数传递给该构造方法。

    3,调用Thread的start方法,启动线程。

    下面看看代码是如何实现的:

    public class ThreadCreat2
    {
    	public static void main(String args[]){
    		new Thread(new MyThread2()).start();//将实现了Runnable接口的类的实例对象作为                                         //参数传递给Thread类的实例对象
    		for(int i=0;i<10;i++)
    			System.out.println(Thread.currentThread().getName()+"-->"+i);
    	}
    }
    class MyThread2 implements Runnable //实现Runnable接口
    {
    	public void run(){
    		for(int i=0;i<10;i++)
    			System.out.println(Thread.currentThread().getName()+"-->"+i);
    	}
    }



    ---------- 运行java程序 ----------

    main-->0

    main-->1

    main-->2

    main-->3

    main-->4

    main-->5

    main-->6

    Thread-0-->0

    Thread-0-->1

    main-->7

    main-->8

    main-->9

    Thread-0-->2

    Thread-0-->3

    Thread-0-->4

    Thread-0-->5

    Thread-0-->6

    Thread-0-->7

    Thread-0-->8

    Thread-0-->9

    输出完成 (耗时 0 秒) - 正常终止

    从运行结果也可以看到,主线程(main)和Thread-0线程时随机交替执行的。

    看完两种创建线程的方式后,我们来对比下两种线程的优势和劣势:

    采用继承Thread类方式完成多线程建立:

    劣势是:因为线程类已经继承了Thread类,所以不能再继承其他父类。

    优势是:编写简单,如果需要访问线程,无需使用Thread.currentThread()方法,直接

    使用this即可获得当前线程。

    采用实现Runnable接口方式的多线程:

    优势是:线程类只是实现了Runnable接口,还可以继承其他类。在这种方式下,可以多个线程共享同一个target对象,所以非常适合多个相同线程来处理同一份资源的情况,较好的体现了面向对象的思想。

    劣势是:编程稍稍复杂,如果需要访问当前线程,必须使用Thread.currentThread()方法。

    推荐使用实现Runnable接口的方式创建线程对象。

    3,售票程序

    现在用多线程来模拟一个实际情况:

    现在有四个售票厅,同时在卖100张票,这100张票是他们共有的,售完即停。

    我们先来看下代码是如何实现的:

    public class SoldTickets
    {
    	public static void main(String args[]){
    		Tickets t1 = new Tickets();
    		Tickets t2 = new Tickets();
    		Tickets t3 = new Tickets();
    		Tickets t4 = new Tickets();//新建四个线程对象
    
    		t1.start(); //开启四个卖票线程
    		t2.start();
    	    t3.start();
    		t4.start();
    	}
    }
    class Tickets extends Thread
    {
    	public static int ticket = 100;  //这里加静态时为了保证卖的票是100张
        static Object obj = new Object();  //要保证锁的唯一性,就需要用static关键字修饰
    	public void run(){
    		while(true){
    			synchronized(obj){  //线程使用的是同一个锁
    					if(ticket>0){
    					System.out.println(this.getName()+"-->"+ticket);
    					ticket--;
    				}
    			}
    		}
    	}
    }



    上面是通过继承Thread类来完成卖票程序的。重点是要保证多个线程处理的是同一份数据,其二,关键代码要用同步代码块。在以上代码中是如何实现的了?

    我们来看高亮的注释:

    由于是继承Thread类,要开启多条线程,就会新建多个对象,为了保证卖的票是唯一的

    public static int ticket = 100; 不管我们新建多少对象,用static关键字修饰的成员变量,在内存中只有一份。至于另一个问题,同步代码块和锁的问题会留在下一节会详细讲解,并给出为何要同步的原因。


    再来看看实现Runnable接口的类是如何完成卖票程序的: 

    public class SoldTickets2
    {
    	public static void main(String args[]){
    		TicketsDemo td = new TicketsDemo();
    		Thread t1 = new Thread(td);
    		Thread t2 = new Thread(td);
    		Thread t3 = new Thread(td);
    		Thread t4 = new Thread(td);
    
    		t1.start();
    	    t2.start();
    		t3.start();
    		t4.start();
    	}
    }
    class TicketsDemo implements Runnable
    {
    	public int tickets = 100;   //注意这里并没有加static
    	Object obj = new Object(); //这里也没有加static
    	public void run(){
    		while(true){
    			synchronized(obj){  //加锁
    				if(tickets>0){
    					System.out.println(Thread.currentThread().getName()+"-->"+tickets);
    					tickets--;
    				}
    			}
    		}
    	}
    }



    我们来看看,这段代码是如何保证卖的是共同的100张票。

    由于我们是采用的是实现Runnable接口方式来创建线程的。我们只需要新建一个TicketsDemo类实例对象;在创建线程对象时通过新建Thread本类对象来完成的,同时将TicketsDemo作为参数传递给Thread的有参构造器,完成线程的建立。这样一来,我们可以用同一个TicketsDemo对象来新建多个线程对象。这就保证了票的唯一性,锁的唯一性也保证了。






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

    2018-11-12 20:05:07
    创建线程的方式线程常用方法: start和run方法的区别: 控制线程的结束: 创建线程创建线程的目的是为了开启一条执行路径,去运行指定的代码和其他代码实现同时运行。 而运行的指定代码就是这个执行路径...

    目录

     

    创建线程:

    创建线程的方式:

    线程常用方法:

    start和run方法的区别:

    控制线程的结束:


    创建线程:

    创建线程的目的是为了开启一条执行路径,去运行指定的代码和其他代码实现同时运行。 而运行的指定代码就是这个执行路径的任务。

    jvm创建的主线程的任务都定义在了主函数中。

    而自定义的线程它的任务在哪儿呢?

                 Thread类用于描述线程,线程是需要任务的。所以Thread类 也对任务的描述。这个任务就通过Thread类中的run方法来体现。也就是说,run方法就是封装自定义线程运行任务的函数.

                 run方法中定义就是线程要运行的任务代码。

                 开启线程是为了运行指定代码,所以只有继承Thread类,并复写run方法.将运行的代码定义在run方法中即可。

     

    主线程的名字就是main.


    创建线程的方式:

    <1>//继承方式,只能单继承

    继承Thread类,通过重写run()方法定义了一个新的线程类ThreadA,其中run()方法的方法体代表了线程需要完成的任务,称之为线程执行体。当创建此线程类对象时一个新的线程得以创建,并进入到线程新建状态。通过调用线程对象引用的start()方法,使得该线程进入到就绪状态,此时此线程并不一定会马上得以执行,这取决于CPU调度时机。

    class ThreadA extends Thread{
    
    //当run方法开始执行时,进入运行状态
    
        public void run() {
    
            String name = getName();//得到当前线程名称
    
            System.out.println("ThreadA执行了!线程为:"+name);
    
        }//当run方法执行结束时,进入死亡状态
    
    }

    <2>//接口实现方式(实现Runnable接口),可多继承

    1 定义类实现Runnable接口。

    2 覆盖接口中的run方法,将线程的任务代码封装到run方法中

    3通过Thread类创建线程对象,并将Runnable接口的子类对象作为Thread类的构造函数的参数进行传递。

    为什么?因为线程的任务都封装在Runnable接口子类对象的run方法中。所以要在线程对象创建时就必须明确要运行的任务

    调用线程对象的start方法开启线程。

    *注意:一个Runnable子类的实例,可以被Thread包装为多个线程,线程之间互补干扰,但它们操作同一个Runnable子类的实例中的属性变量

     

    实现Runnable接口,并重写该接口的run()方法,该run()方法同样是线程执行体,创建Runnable实现类的实例,并以此实例作为Thread类的target来创建 Thread对象,该Thread对象才是真正的线程对象。

    class ThreadB implements Runnable{
    
    
    
        @Override
    
        public void run() {
    
            String name=Thread.currentThread().getName();//获得当前线程的名字
    
            System.out.println("ThreadB执行了!线程为:"+name);方法
    
        }
    
    }

    <3>通过Callable和Future创建线程(有返回值)

      1.创建Callable接口的实现类,并实现call方法,该call方法将作为线程执行体,并有返回值(类似于run方法)

      2.创建Callable的实例,并用FutuerTask类来包装Callable对象,该FutuerTask封装了Callable对象call()方法的返回值

      3.实例化FutuerTask类,参数为FutuerTask接口实现类的对象来启动线程

      4.通过FutuerTask类的对象的get()方法来获取线程结束后的返回值(其为一个泛型)

     

    <4>TimerTask创建线程


    线程常用方法:

     

    start():使线程进入准备状态

    sleep(long millis):在指定毫秒内让当前正在执行的线程休眠(暂停执行)

    join(long millisec):指等待t线程终止(单位毫秒)

    yideld():停止当前正在执行的线程对象,并执行其他线程(静态方法)

    interrupt():中断某个线程(人为结束)这种结束方式比较粗暴如果t线程打开了某个资源还没来得及关闭也就是run方法还没有执行完就强制结束线程,会导致资源无法关闭

    Thread()

    isAilve()判断当前线程是否处于活动状态(线程已启动并未终止)

    Thread. currentThread()返回代码正在被那个线程所调用的信息

    Thread. currentThread() . getName ()返回对当前正在执行的线程对象的引用。

     

    start和run方法的区别:

    1) start:

      用start方法来启动线程,真正实现了多线程运行,这时无需等待run方法体代码执行完毕而直接继续执行下面的代码。通过调用Thread类的start()方法来启动一个线程,这时此线程处于就绪(可运行)状态,并没有运行,一旦得到cpu时间片,就开始执行run()方法,这里方法 run()称为线程体,它包含了要执行的这个线程的内容,Run方法运行结束,此线程随即终止。

    2) run:

      run()方法只是类的一个普通方法而已,如果直接调用Run方法,程序中依然只有主线程这一个线程,其程序执行路径还是只有一条,还是要顺序执行,还是要等待run方法体执行完毕后才可继续执行下面的代码,这样就没有达到写线程的目的。

    总结:调用start方法方可启动线程,而run方法只是thread的一个普通方法调用,还是在主线程里执行。这两个方法应该都比较熟悉,把需要并行处理的代码放在run()方法中,start()方法启动线程将自动调用 run()方法,这是由jvm的内存机制规定的。并且run()方法必须是public访问权限,返回值类型为void。

    控制线程的结束:

    1.stop()方法;

    2.run()方法结束:任务都会在循环结构中,控制住循环即可;(但如果被wait()了,没有被唤醒,便不能判断条件,便不能结束)

    3.interrupt():将线程强制从冻结状态强制恢复到运行状态来,让线程具备cpu的执行资格,但是强制动作会导致异常,要处理

     

     

     

    展开全文
  • Java多线程01_创建线程三种方式

    万次阅读 2020-08-26 21:23:39
    线程创建1:继承Thread类(不推荐,避免OOP单继承局限性) 创建线程:继承Thread类,重写run方法 public class TestThread1 extends Thread{ @Override public void run() { for (int i = 0; i
  • 创建线程的方式

    2019-03-07 10:36:23
    面试的过程中我们经常会遇到创建线程(对象)有多少种方式,具体如何实现等类似的问题,下面我们就来详细探讨下 首先明确创建线程主要有四种方式: 1、 通过实现Runnable接口...在你简述这四种方式创建线程的方式...
  • Java线程的创建方式

    2016-05-28 00:26:44
    线程创建方式 直接继承Thread 实现Runnable接口 实现Callable接口 方式一线程创建package com.jimory.thread.create;/** * 线程创建(一) * 1、继承Thread类 * 2、重写run方法 , run方法里是线程体 * * 线程...
  • 1.Java的线程初始化  以下介绍一种线程常见的创建方法: 1.1通过继承Thread类、... * 线程的创建方式1:继承Thread类 */ public class Demo_extends extends Thread { @Override public void run() { while ...
  • 第二种创建线程的方式是通过实现Runnable接口来创建一个新的线程。 下面总结一下两种创建线程方式的区别: 从面向对象编程的角度来看:通过继承Thread类创建线程使用的是基于继承的技术,而第二种方式是当然就是...
  • 其实,创建线程创建普通对象操作是一样,而线程就是Thread类或其子类实例对象。每个Thread对象描述了一个单独的线程。要产生一个线程,有两种方法: ◆需要从Java.lang.Thread类派生一个新的线程类,...
  • 线程创建方式 1.使用 Thread(Runnable target)这个构造方法创建Thread对象,传入一个实现Runnable接口的对象。实现Runnable接口要实现Run方法,所以这种方法创建的线程运行时,会执行这个Runnable接口上的Run方法。...
  • 创建线程的3种方式 第一种方式是将类声明为 Thread 子类。 定义Thread类子类,并重写该类run()方法,该run()方法方法体就代表了线程需要完成任务,因此把run()方法称为线程执行体。 创建Thread子类...
  • java 创建线程的方式

    千次阅读 2020-10-02 15:18:53
    Java创建线程的方式 1 官方API里面的说法 通过官方 API 可以发现创建线程的方式有两种,第一种是通过继承 Thread 类,重写 run 方法;第二种是通过实现 Runnable 接口,创建线程时传入该接口的子类。 2 我的观点 ...
  • 创建线程方式一:继承Thread类,重写run()方法,调用start开启线程 /** *创建线程方式一:继承Thread类,重写run()方法,调用start开启线程 * 总结:注意,线程开启不一定立即执行,由cpu调度执行 * * @author LCW...
  • 创建线程的四种方式

    千次阅读 多人点赞 2019-10-03 21:55:47
    创建线程的四种方式 继承Thread类 实现Runnable接口 使用Callable和Future创建线程 使用Executor框架创建线程池 继承Thread类 步骤 定义一个Thread类子类,重写run方法,将相关逻辑实现,run()方法就是线程要...
  • Windows 线程创建方式的比较

    千次阅读 2016-07-14 13:08:53
    进行 Windows 编程时,常需要涉及多线程编程,以下是 Windows 提供关于创建线程的3个API。
  • java创建线程的方式

    2019-04-15 12:36:26
    java创建线程的三种方式 1、继承Thread类创建线程类 定义Thread类子类,并重写该类run方法,该run方法方法体就代表了线程要完成任务。因此把run()方法称为执行体。 创建Thread子类实例,即创建线程...
  • java 创建线程的三种方式创建线程池四种方式

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

    千次阅读 2020-04-20 22:57:28
    线程的创建方式 方式一:继承Thread类 步骤: 1、定义类继承Thread类 2、覆盖Thread类中run方法 3、调用线程的start方法,该方法两个作用:启动线程、调用run方法 例子: class MyThreadDemo extends Thread { ...
  • 创建线程-采用Callable接口-普通创建callable实现实现类的方式 demo 创建线程-采用Callable接口-lambda表达式 demo 优缺点 采用实现Runnable、Callable接口方式创建线程 优点 1.线程只是实现了...
  • 线程对象创建后,即进入新建状态,如:Thread t = new MyThread(); 就绪状态(Runnable) 当调用线程对象start()方法时,线程即进入就绪状态。处于就绪状态的线程只是说明此线程已经做好准备,随时等待CPU调度...
  • 线程创建的方式:3种 实现runnable接口、继承Thread、实现Callable接口 实现Runnable接口 1、创建自定义类并实现Runable接口,并实现接口中的run方法 2、实例化自定的类 3、将自定义类的实例作为参数Thread类,创建...
  • 线程创建线程的两种方式

    万次阅读 多人点赞 2018-05-29 00:33:56
    彩蛋: ...言归正传,今晚准备详细记录一下线程创建的两种方式以及这两种方式的优缺点及适应的情况等等。 多进程:。。。 多线程:在一个应用程序中,有多个顺序流同时执行。(线程:顺序执行的代...
  • 线程的创建方式

    2014-01-27 21:53:56
    线程的创建方式一: 1. 继承Thread类; 2.覆写run方法。 /* 继承Thread类 */ class MyThread extends Thread { //覆写run方法 public void run() { System.out.println(Thread.currentThread().getName() + ".....
  • 线程及其创建的方式

    千次阅读 2015-07-23 20:07:09
    线程主要发挥作用时候:当一个线程所执行I/O被阻塞时候,同一进程中其他线程可以使用CPU来进行计算。这样,就提高了程序执行效率。 状态:运行、就绪(等待被调用)、阻塞(等待I/O资源) 两种创建...
  • 一、线程创建方式 Thread类继承、Runnable接口实现、Callable接口实现(Callable接口有返回值) 平时大家使用多可能就是Thread类继承和Runnable接口实现这两种,下面我们来看看实例 1、继承Thread类...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 26,759
精华内容 10,703
关键字:

线程创建的方式