精华内容
下载资源
问答
  • Java使用Thread类代表线程,所有的线程对象都必须是Thread类或其子类的实例。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建立的线程,下面就以上四个方法做个比较

     

     

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

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

    转载于:https://www.cnblogs.com/chenxiaoxian/p/10423151.html

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

    2020-02-23 21:24:48
    2、实现Runnable接口创建线程 3、实现callable接口创建线程 4、通过线程池创建线程 实现接口继承Thread类比较 1、接口更适合多个相同程序代码线程去共享同一个资源。 2、接口可以避免java中单个继承局限性...

    1、继承Thread类创建线程
    2、实现Runnable接口创建线程
    3、实现callable接口创建线程
    4、通过线程池创建线程
    实现接口和继承Thread类比较

    1、接口更适合多个相同的程序代码的线程去共享同一个资源。
    2、接口可以避免java中的单个继承的局限性
    3、接口代码可以被多个线程共享,代码和线程独立。
    4、线程池只能实现Runable或callable接口的线程,不能直接放入继承Thread的类。

    扩充:
    在java中,每次程序运行至少启动2个线程。一个是main线程,一个是垃圾收集线程。
    Runable 和Callable接口 比较
    相同点:
    两者都是接口。
    两者都可以用来编写多线程程序。
    两者都需要调用Thread.start()启动线程。

    不同点
    实现callable接口的线程能返回执行的结果,而实现runable接口的线程不能返回结果

    callable接口的call()方法允许抛出异常,而runable接口不允许抛出异常。
    实现callable接口的线程可以调用future.cancel取消执行,而实现runable接口的线程不能
    

    注意点:
    Callable接口支持返回结果,此时需要调用Future.get()方法实现,此方法会阻塞主线程知道获取到“将来”结果,当不调用此方法时,主线程不会阻塞。
    线程的生命周期:
    新建—就绪–运行–阻塞–死亡

    新建:new关键字创建一个线程之后,该线程就处于新建状态。jvm为线程分配内存,初始化成员变量

    就绪:当线程对象调用了start()该线程就处于就绪状态。jvm为线程创建方法栈和程序计数器,等待程序调度器调度
    运行:就绪状态的线程获得cpu的资源,开始运行run()方法,该线程就处于运行状态。

    阻塞:当发生如下情况时,线程将会进入阻塞状态,线程调用sleep()方法线程主动放弃所占用的处理器资源,线程调用了一个阻塞式IO方法,在该方法返回之前,该线程被阻塞,线程试图获得一个同步锁,但该同步 锁正被其他线程所持有,线程在等待某个通知,程序调用了线程的suspend()方法将该线程挂起,但这个方法容易导致死锁,所以应该尽量避免使用该方法

    死亡:run或call方法执行完成,线程正常结束,线程抛出一个未捕获的exception或error,调用该线程stop方法来结束该线程,该方法容易导致死锁,不推荐使用。

    展开全文
  • 创建线程的方式及优缺点关系图创建线程的3种方式一、 Thread类创建线程1. 定义Thread类的子类,并重写该类的run方法,该run方法的方法体就代表了线程要完成的任务。因此把run()方法称为执行体。2. 创建Thread子类...

    a0dac12efcc51a340116bf510fa8ef18.png

    创建线程的方式及优缺点和关系图

    创建线程的3种方式

    一、 Thread类创建线程

    1. 定义Thread类的子类,并重写该类的run方法,该run方法的方法体就代表了线程要完成的任务。因此把run()方法称为执行体。

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

    3. 调用线程对象的start()方法来启动该线程。

    8c111cfa18a3c13f16f68858e6a5f560.png

    二、通过Runnable接口创建线程

    1. 定义runnable接口的实现类,并重写该接口的run()方法,该run()方法的方法体同样是该线程的线程执行体。

    2. 创建 Runnable实现类的实例,并依此实例作为Thread的target来创建Thread对象,该Thread对象才是真正的线程对象。

    3. 调用线程对象的start()方法来启动该线程。

    f321c936ab5141099dd83f66aef79ce1.png

    三、通过Callable和Future创建线程

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

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

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

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

    83dcb5fea35b47085b77c933893e068e.png

    优缺点

    使用继承Thread类的方式创建多线程时优势是:

    编写简单,如果需要访问当前线程,则无需使用Thread.currentThread()方法,直接使用this即可获得当前线程。

    劣势是:

    线程类已经继承了Thread类,所以不能再继承其他父类。

    使用实现Runnable、Callable接口的方式创见多线程时

    优势是:线程类只是实现了Runnable接口或Callable接口,还可以继承其他类。

    在这种方式下,多个线程可以共享同一个target对象,所以非常适合多个相同线程来处理同一份资源的情况,从而可以将CPU、代码和数据分开,形成清晰的模型,较好地体现了面向对象的思想。Callable接口方式创建线程还可以通过返回值来判断线程执行情况。

    劣势是:

    编程稍微复杂,如果要访问当前线程,则必须使用Thread.currentThread()方法。

    关系图

    Thread关系图

    599096743362772c43d773e866e67aab.png

    Runnable关系图

    dc5236fe035e02ca4372220e4286e0cd.png

    Callable关系图

    1607e3c1eb7ba25856f17856de9183c8.png

    Callable创建线程关系图

    6a90b85fdd7f927eb3345123c14e1cb8.png
    展开全文
  • 线程的生命周期: 1.新建:使用new创建对象,  2.准备就绪:调用了start()方法,但还没有抢占到CPU资源  3.运行:调用了start()方法并抢占到了cpu资源,开始执行  4.阻塞:调用 sleep()/wait()方法,使线程...

    线程的生命周期:

    1.新建:使用new创建对象, 
    2.准备就绪:调用了start()方法,但还没有抢占到CPU资源 
    3.运行:调用了start()方法并抢占到了cpu资源,开始执行 
    4.阻塞:调用 sleep()/wait()方法,使线程让出CPU资源并进入休眠状态,可调用notity()方法使线程进入到准备就绪状态 
    5.销毁:run方法执行结束或调用 stop()方法后,线程已经不在使用,成为了垃圾线程,将GC回收

    ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

    两种创建线程的方法在使用同步锁的对比

    1. 实现了Runnable接口的类创建线程: 
      因在创建多个线程时,实现Runnable接口的类只须要创建一个,所以它的锁对象默认为 this
    2. 继承了Thread类的类,在创建线程时,如果使用了同步锁,那么它默认锁对象默认为 类名.class,原因在于继承了Thread的类在创建线程时,每创建一个线程创建了一个新的对象,要保证它的锁是固定的,所以它的默认锁对象就是 类名.class
    3.  
    ------------------------------------------------------------------继承Thread的实体类Apples------------------------------------------------------------------
    
    package com.Ckinghan.thread;
    
    /**
     * @author Ckinghan
     *  @描述:如果类继承了Thread类,那么它在使用同步锁时,锁的对象及共享数据必须是静态的(即类对象,不管这个类被new几个,类对象只有一个)
     *              当锁加在方法上时,它的默认对象是类名.class,如Apples类在方法上加锁时,它的默认锁对象是Apples.class
     */
    public class Apples extends Thread{
    
        public Apples(String threadName){
            super(threadName);
        }
    
        /**
         * 共享数据变量,必须使用static修饰,否则,在创建线程时每一个线程都会有一个apples,且初始值都是100,就有了加锁的必要,也与
         * 最初的设计不符
         */
        private static Integer apples = 100;
    
        /**
         * 锁的对象,必须是被static修饰的且不管类被new多少个,它始终只能有一个且是不变的,否则,同步锁将失效
         */
        public static Integer lockObject = new Integer(0);
    
        @Override
        public void run() {
            while (true) {
                if(apples % 3 == 0){
                    /**
                     * 同步锁块的锁对象,建议使用 类名.class(在本类中即:Apples.class)
                     */
                    synchronized (Apples.class) {
                        if(apples > 0){
                            /**
                             * 使线程休眠30毫秒,增加出现并发的机率
                             */
                            try {
                                Thread.sleep(30);
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                            System.out.println(this.getName()+"抢到了编号为"+apples--+"的苹果");
                        }else{
                            System.out.println("苹果已经被抢完了");
                            break;
                        }
                    }
                }else if(apples % 3 ==1){
                    /**
                     * 同步锁块的锁对象,建议使用 类名.class(在本类中即:Apples.class)
                     */
                    synchronized (Apples.class) {
                        if(apples > 0){
                            /**
                             * 使线程休眠30毫秒,增加出现并发的机率
                             */
                            try {
                                Thread.sleep(30);
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                            System.out.println(this.getName()+"抢到了编号为"+apples--+"的苹果");
                        }else{
                            System.out.println("苹果已经被抢完了");
                            break;
                        }
                    }           
                }else{
                    //抢苹果
                    lockFunction();
                }//if end
            }//while end    
        }
    
        public synchronized static void lockFunction(){
            if(apples > 0){
                System.out.println(Thread.currentThread().getName()+"抢到了编号为"+apples--+"的苹果");
                return;
            }
            System.out.println("苹果已经被抢完了");
        }
    }
    
    
    ------------------------------------------------------------------实现了Runnable接口的实体类Apple------------------------------------------------------------------
    
    package com.Ckinghan.thread;
    
    /**
     * 创建线程的第二种方式 :实现Runnable
     * 在使用同步锁时,如果同步锁加在方法上,它默认的锁对象是this
     * 如果用在同步锁块上,它的锁对象可以自己定义,建议使用this,
     * @author Ckinghan
     *  @描述:
     */
    public class Apple implements Runnable {
    
        private Integer apples = 100;
    
        @Override
        public void run() {
            while(true){
                if(apples % 4 == 0){
                    synchronized (this) {
                        if(apples > 0){
                            try {
                                Thread.sleep(30);
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                            System.out.println(Thread.currentThread().getName()+"抢到了编号为"+apples--+"的苹果");
                        }else{
                            System.out.println("苹果已经被抢完了");
                            break;
                        }
                    }
                }else {
                    //调用同步锁的方法抢苹果
                    lockFunction();
                }
    
    
            }
    
        }
    
        /**
         * @描述:实现Runnable接口的类,在方法上加同步锁时,默认的锁对象为this
         * @创建时间:
         */
        private synchronized void  lockFunction(){
            if(apples > 0){
                System.out.println(Thread.currentThread().getName()+"抢到了编号为"+apples--+"的苹果");
                return;
            }
            System.out.println("苹果已经被抢完了");
        }
    
    }
    
    
    ------------------------------------------------------------------测试Demo------------------------------------------------------------------
    
    package com.Ckinghan.thread;
    
    public class ApplesDemo {
    
        public static void main(String[] args) {
            /**
             *   * @描述:几个小朋友抢苹果,为防止一个编号的苹果被多个人同时抢中,加同步锁
             *              因为apple实现了Runnable接口,鉴于它的线程创建方式,在加锁时,
             *              锁的对象建议使用this,因为在方法上加同步锁,它的默认锁对象是this
             */
    //      threadRunnableApple();
    
    
    
            /**
             * @描述:几个小朋友抢苹果,为防止一个编号的苹果被多个人同时抢中,加同步锁
             *              因为apples继承了Thread类,鉴于它的线程创建方式,在加锁时,
             *              锁的对象建议使用 类名.class,因为在方法上加同步锁,它的默认锁对象是 类名.clss(如Apples.class)
             */
            extendsThreadApple();
    
        }
    
        /**
         * @描述:几个小朋友抢苹果,为防止一个编号的苹果被多个人同时抢中,加同步锁
         *              因为apples继承了Thread类,鉴于它的线程创建方式,在加锁时,
         *              锁的对象建议使用 类名.class,因为在方法上加同步锁,它的默认锁对象是 类名.clss(如Apples.class)
         * @创建时间:
         */
        public static void extendsThreadApple(){
            /**
             * 创建四个线程
             */
            Apples persion1 = new Apples("小明");
            Apples persion2 = new Apples("小二");
            Apples persion3 = new Apples("小黄");
            Apples persion4 = new Apples("小三");
            /**
             * 启动线程
             */
            persion1.start();
            persion2.start();
            persion3.start();
            persion4.start();
        }
    
        /**
         * @描述:几个小朋友抢苹果,为防止一个编号的苹果被多个人同时抢中,加同步锁
         *              因为apple实现了Runnable接口,鉴于它的线程创建方式,在加锁时,
         *              锁的对象建议使用this,因为在方法上加同步锁,它的默认锁对象是this
         * @创建时间:
         */
        public static void threadRunnableApple(){
            /**
             * 注意,实现了Runnable接口的apple类对象创建线程时,因为apple类只实例化了一次,所以不用静态的锁对象就可以,因它多个线程使用
             * 的对象只有apple一个
             */
            Apple apple = new Apple();
            /**
             * 创建四个线程
             */
            Thread persion1 = new Thread(apple,"小明");
            Thread persion2 = new Thread(apple,"小二");
            Thread persion3 = new Thread(apple,"小黄");
            Thread persion4 = new Thread(apple,"小三");
            /**
             * 启动线程
             */
            persion1.start();
            persion2.start();
            persion3.start();
            persion4.start();
    
        }
    
    }
    
    ------------------------------------------------------------------执行结果:------------------------------------------------------------------
    
    小明抢到了编号为100的苹果
    小三抢到了编号为99的苹果
    小黄抢到了编号为98的苹果
    小黄抢到了编号为97的苹果
    小黄抢到了编号为96的苹果
    小黄抢到了编号为95的苹果
    小二抢到了编号为94的苹果
    小二抢到了编号为93的苹果
    小二抢到了编号为92的苹果
    小黄抢到了编号为91的苹果
    小三抢到了编号为90的苹果
    小三抢到了编号为89的苹果
    小明抢到了编号为88的苹果
    小明抢到了编号为87的苹果
    小明抢到了编号为86的苹果
    小三抢到了编号为85的苹果
    小黄抢到了编号为84的苹果
    小二抢到了编号为83的苹果
    小黄抢到了编号为82的苹果
    小三抢到了编号为81的苹果
    小明抢到了编号为80的苹果
    小明抢到了编号为79的苹果
    小三抢到了编号为78的苹果
    小三抢到了编号为77的苹果
    小二抢到了编号为76的苹果
    小二抢到了编号为75的苹果
    小二抢到了编号为74的苹果
    小黄抢到了编号为73的苹果
    小二抢到了编号为72的苹果
    小二抢到了编号为71的苹果
    小二抢到了编号为70的苹果
    小二抢到了编号为69的苹果
    小二抢到了编号为68的苹果
    小明抢到了编号为67的苹果
    小三抢到了编号为66的苹果
    小三抢到了编号为65的苹果
    小明抢到了编号为64的苹果
    小二抢到了编号为63的苹果
    小二抢到了编号为62的苹果
    小黄抢到了编号为61的苹果
    小黄抢到了编号为60的苹果
    小黄抢到了编号为59的苹果
    小二抢到了编号为58的苹果
    小明抢到了编号为57的苹果
    小明抢到了编号为56的苹果
    小三抢到了编号为55的苹果
    小三抢到了编号为54的苹果
    小三抢到了编号为53的苹果
    小明抢到了编号为52的苹果
    小二抢到了编号为51的苹果
    小二抢到了编号为50的苹果
    小黄抢到了编号为49的苹果
    小黄抢到了编号为48的苹果
    小黄抢到了编号为47的苹果
    小二抢到了编号为46的苹果
    小二抢到了编号为45的苹果
    小二抢到了编号为44的苹果
    小明抢到了编号为43的苹果
    小明抢到了编号为42的苹果
    小明抢到了编号为41的苹果
    小三抢到了编号为40的苹果
    小三抢到了编号为39的苹果
    小三抢到了编号为38的苹果
    小明抢到了编号为37的苹果
    小明抢到了编号为36的苹果
    小明抢到了编号为35的苹果
    小二抢到了编号为34的苹果
    小二抢到了编号为33的苹果
    小黄抢到了编号为32的苹果
    小二抢到了编号为31的苹果
    小黄抢到了编号为30的苹果
    小黄抢到了编号为29的苹果
    小黄抢到了编号为28的苹果
    小黄抢到了编号为27的苹果
    小黄抢到了编号为26的苹果
    小黄抢到了编号为25的苹果
    小黄抢到了编号为24的苹果
    小黄抢到了编号为23的苹果
    小明抢到了编号为22的苹果
    小三抢到了编号为21的苹果
    小三抢到了编号为20的苹果
    小明抢到了编号为19的苹果
    小黄抢到了编号为18的苹果
    小黄抢到了编号为17的苹果
    小二抢到了编号为16的苹果
    小二抢到了编号为15的苹果
    小二抢到了编号为14的苹果
    小黄抢到了编号为13的苹果
    小黄抢到了编号为12的苹果
    小黄抢到了编号为11的苹果
    小明抢到了编号为10的苹果
    小三抢到了编号为9的苹果
    小明抢到了编号为8的苹果
    小明抢到了编号为7的苹果
    小黄抢到了编号为6的苹果
    小二抢到了编号为5的苹果
    小二抢到了编号为4的苹果
    小黄抢到了编号为3的苹果
    小黄抢到了编号为2的苹果
    小黄抢到了编号为1的苹果
    苹果已经被抢完了
    苹果已经被抢完了
    苹果已经被抢完了
    苹果已经被抢完了
    苹果已经被抢完了

    出处:”https://blog.csdn.net/ckinghan58/article/details/72458217 

    展开全文
  • 1、线程和进程联系与区别
  • 创建线程共有四种方式: 1.继承Thread类 2.实现Runnable接口 3.实现Callable接口 4.使用Executors工具类创建线程池 具体实现方法如下: 1.继承Thread类:定义一个MyThread子类,重写run方法,继承Thread类,调用子类...
  • 创建线程的方式

    2020-07-29 17:54:05
    创建线程的方式有: 继承 Thread 类创建线程 实现 Runnable 接口创建线程 使用 Callable Future 创建线程 使用线程池 本文主要介绍一下前三种方法。理解创建线程,需要注意两个概念:线程体、线程对象。 1. ...
  • 本文主要介绍Java线程的三种创建方式和线程池的四种实现方式。 1.进程和线程 A:进程概念 进程:进程指正在运行的程序。确切的来说,当一个程序进入内存运行,即变成一个进程,进程是处于运行过程中的程序,...
  • 创建一个线程有四种方式: ...创建线程的步骤: 定义Thread类的子类,并重写该类的 run() 方法,该方法的方法体就是县城需要完成的任务,run() 方法也成为线程的执行体 创建Thread子类的实例,也就是创建了...
  • 文章目录典型错误观点二: 通过CallableFutureTask创建线程是一种新建线程的方式典型错误观点三: 无返回值 是实现Runnable接口, 有返回值是实现callable接口 ,所以callable是新的实现线程的方式 典型错误观点二: ...
  • 操作系统实现线程的几种模式 java创建线程的3个方式 这是两个概念 在操作系统中,线程可以实现在用户模式下,也可以实现在内核模式下,也可以两者结合实现。 1、实现线程的三种方式: (1)继承thread 类 ...
  • 对于多线程,大家并不陌生,对于如何创建线程也是轻车熟路,对于使用new thread和实现runable接口的方式,不再多说。这篇博文我们介绍第三种:实现Callable接口。
  • 相信大多数学过多线程的同学都知道创建线程常见的有三种方式,一种是继承Thread类,一种是实现Runnable接口,最后一种就是Callable,今天主要是对最后不常见的Callable方式进行介绍。一、为什么要Callable接口既然有...
  • 1)创建Callable 接口的实现类,并实现call方法, 该call()方法作为线程执行体,并且有返回值。 2)创建Call 实现实例, 使用FutureTask 类来包装Callable对象,该FutureTask对象封装了该Callable 对象call() 方法...
  • (1)问题分析:面试官考核线程创建方式及其优缺点(2)核心答案讲解:1)继承Thread类2)实现Runnable接口3)实现Callable接口4)线程池方式优缺点:1)继承Thread类优点:代码简单 。 缺点:该类无法集成别类。2)实现...
  • 我们使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题:如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程就会大大降低系统的效率,因为频繁...
  • 2、创建线程实现方式有几种,请列举步骤 两种: 1)继承关系 自定义一个类,继承自Thread类 重写Thread类中的run方法 在main线程中,创建该类对象,启动线程(start()) 2)实现Runnable接口的方式 自定义类...
  • java创建线程的方式

    2019-09-24 01:50:03
    Java创建线程的方法有如下3种。 1)继承Thread类创建线程 extends Thread 2)实现Runnable接口创建线程 implements Runnable 3)实现Callable接口Future创建线程 implements Callable 他们之间的关系如下: 继承...
  • 方式可以用来创建线程1)继承Thread类2)实现Runnable接口3)应用程序可以适用Executor框架来创建线程池实现了Runnable接口这种方式更受欢迎,因为这不需要继承Thread类。在应用设计中已经继承了别对象情况下,这...
  • Java中创建线程的方式

    2020-04-01 20:32:41
    目录 并发并行 进程与线程 线程调度的方式 主线程、单线程 单线程程序执行原理 ...通过创建Thread类的子类创建多线程 ...通过匿名内部类的方式创建线程 并发并行 并发:同一时间段发生 并行...
  • 源码中说的创建线程有两种方式类thread接口runable;网上第三种:实现Callable接口,结合FutureTask创建线程,这种方式我们查看源码也可以看出它实际上也属于实现Runable接口方式:public class FutureTask<V&...
  • 二、创建线程的方式 2.1 继承Thread类 2.2 实现Runnable接口 三、wait()、sleep()、notify()yield()等方法介绍 一、线程状态 为了方便理解,线程运行状态大致可分为:初始状态、就绪状态、运行状态、阻塞...
  • //方式1: 匿名内部类 Thread t1 = new Thread(){ public void run(){ for(int i=0;i;i++){ System.out.println
  • 第二种方式,创建匿名子类的方式 测试thread的常用方法 俩种命名方法 线程的调度/优先级 例题:卖票 创建多线程2:实现runnable接口 同上卖票例题 一个对象放到三个构造器中,三个线程用的同一个...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 5,057
精华内容 2,022
关键字:

创建线程的方式和实现