首页 > 试题广场 >

创建线程有几种不同的方式?你喜欢哪一种?为什么?

[问答题]
创建线程有几种不同的方式?你喜欢哪一种?为什么?
我来给大家举个例子
第一种:继承Thread类
第二种:实现Runnable接口
第三种:实现Callable接口
第四种:Executor框架来创建线程池

继承Thread类
/**
 * @Author qgfzzzzzz
 * @Date 2019/8/19
 * @Version 1.0
 * <p>
 *  通过继承Thread类创建线程
 * </p>
 */
public class MyThreadOne extends Thread {
    @Override
    public void run() {
        for(int i = 0; i < 100; i++){
            System.out.println(Thread.currentThread().getName() + " " + i);
        }
    }
    public static void main(String[] args){
        new MyThreadOne().start();
        new MyThreadOne().start();
    }
}

实现Runnable接口
/**
 * @Author qgfzzzzzz
 * @Date 2019/8/19
 * @Version 1.0
 * <p>
 * 通过实现Runnable接口创建线程
 * </p>
 */
public class MyThreadTwo implements Runnable{
    @Override
    public void run() {
        for(int i = 0; i < 100; i++){
            System.out.println(Thread.currentThread().getName() + " " + i);
        }
    }
    public static void main(String[] args){
        MyThreadTwo myThreadTwo = new MyThreadTwo();
        Thread th1 = new Thread(myThreadTwo, "thread-1");
        Thread th2 = new Thread(myThreadTwo, "thread-2");
        th1.start();
        th2.start();
    }
}

实现Callable接口
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

/**
 * @Author qgfzzzzzz
 * @Date 2019/8/19
 * @Version 1.0
 * <p>
 * 通过实现Callable接口创建线程
 *
 * 实现Callable接口
 * 1】创建Callable接口的实现类,并实现call()方法,然后创建该实现类的实例
 * 2】使用FutureTask类来包装Callable对象,该FutureTask对象封装了Callable对象的call()方法的返回值
 * 3】使用FutureTask对象作为Thread对象的target创建并启动线程(因为FutureTask实现了Runnable接口)
 * 4】调用FutureTask对象的get()方法来获得子线程执行结束后的返回值
 * </p>
 */
public class MyThreadThree implements Callable<Integer> {
    public static void main(String[] args) {
        MyThreadThree myThreadThree = new MyThreadThree();
        FutureTask<Integer> ft = new FutureTask<>(myThreadThree);
        FutureTask<Integer> ft1 = new FutureTask<>(myThreadThree);
        System.out.println(Thread.currentThread().getName() + "===== ");

        new Thread(ft, "thread-1").start();
        new Thread(ft1, "thread-2").start();
        try {
            System.out.println("return thread-1'result :" + ft.get());
            System.out.println("return thread-2'result :" + ft1.get());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }

    @Override
    public Integer call() throws Exception {
        for (int i = 0; i < 10; i++) {
            System.out.println(Thread.currentThread().getName() + " " + i);
        }
        return 20;
    }
}

Executors执行Runnable任务
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * @Author qgfzzzzzz
 * @Date 2019/8/19
 * @Version 1.0
 * <p>
 * Executors执行Runnable任务
 * </p>
 */
public class MyThreadFour implements Runnable {
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + " is called");
    }
    public static void main(String[] args){
        ExecutorService executorService = Executors.newCachedThreadPool();
        for(int i = 0; i < 5; i++){
            executorService.execute(new MyThreadFour());
        }
        executorService.shutdown();
    }
}

Executors执行Callable任务
import java.util.ArrayList;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

/**
 * @Author qgfzzzzzz
 * @Date 2019/8/19
 * @Version 1.0
 * <p>
 * Executors执行Callable任务
 * </p>
 */
public class MyThreadFive {

    // 业务线程
    private class AThread implements Callable<String> {
        private int id;
        public AThread(int id){
            this.id = id;
        }
        @Override
        public String call() {
            System.out.println("线程:" + id + " 运行..");
            try {
                Thread.sleep(3000);
            } catch (Exception e) {
                e.printStackTrace();
            }

            System.out.println("线程:" + id + " -> 结束.");
            return "返回的字符串" + id;
        }
    }

    public void ExecutorService() {
        ExecutorService pool = Executors.newFixedThreadPool(4);
        ArrayList<Future<String>> futures = new ArrayList<>();

        for (int i = 0; i < 10; i++) {
            AThread t = new AThread(i);
            // Runnable -> execute()
            // Callable -> submit()
            Future<String> f = pool.submit(t);
            futures.add(f);
        }
        System.out.println("....开始获取结果中...");
        // Future的get方法在获取结果时候将进入阻塞,阻塞直到Callable中的call返回。
        for (Future<String> f : futures) {
            try {
                System.out.println(f.get());
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        System.out.println(".....结束得到结果....");
        pool.shutdown();
    }
    public static void main(String[] args) {
        new MyThreadFive().ExecutorService();
    }
}

如果大家发现哪里错了,烦请指正,谢谢



发表于 2019-08-19 17:18:53 回复(5)

一、继承Thread类创建线程类

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

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

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


二、通过Runnable接口创建线程类

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

(2)创建 Runnable实现类的实例(注意 ,这里声明的是实现类,构造方法也是实现类的构造方法),并依此实例作为Thread构造方法的参数来创建Thread对象,该Thread对象才是真正的线程对象。

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

三、通过Callable和Future创建线程

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

(2)创建Callable实现类的实例(注意 ,这里声明的是实现类,构造方法也是实现类的构造方法)并依此实例作为FutureTask构造方法的参数来创建FutureTask对象,同时该FutureTask对象封装了该Callable对象的call()方法的返回值。

(3)使用FutureTask对象(需要泛型<V>)作为Thread构造方法(需要泛型<V>)的参数来创建Thread对象,该Thread对象才是真正的线程对象。
(4)调用线程对象的start()方法来启动该线程。

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

        Callable的call()方法类似于Runnable接口中run()方法,都定义任务要完成的工作,实现这两个接口时要分别重写这两个方法,主要的不同之处是call()方法是有返回值的,而且call()方法可以抛出异常,run方法不可以。
        运行Callable对象(实现类,也可以叫任务)可以拿到一个Future对象(注意future只是个泛型接口,我们用的是他的实现类FutureTask),表示异步计算的结果。
        FutureTask类同时实现了两个接口,Future和Runnable接口,所以它既可以作为Runnable被线程执行,又可以作为Future得到Callable的返回值。
        Future接口的方法介绍如下:
  • boolean cancel (boolean mayInterruptIfRunning) 取消任务的执行。参数指定是否立即中断任务执行,或者等等任务结束
  • boolean isCancelled () 任务是否已经取消,任务正常完成前将其取消,则返回 true
  • boolean isDone () 任务是否已经完成。需要注意的是如果任务正常终止、异常或取消,都将返回true
  • V get () throws InterruptedException, ExecutionException  等待任务执行结束,然后获得V类型的结果。InterruptedException 线程被中断异常, ExecutionException任务执行异常,如果任务被取消,还会抛出CancellationException
  • V get (long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException 同上面的get功能一样,多了设置超时时间。参数timeout指定超时时间,uint指定时间的单位,在枚举类TimeUnit中有相关的定义。如果计算超时,将抛出TimeoutException

第三种用callable和future的方法我用的比较少,只是了解,最常用还是第二种方法,使用runnable接口,它的优点在于,由于java单继承多实现的特点,实现了runnable接口的同时,我还可以继承其他父类,提高了代码的复用性;在这种方式下,多个线程可以共享一个runnable对象,所以非常适合多个相同线程来处理同一份资源的情况;但是也有缺点,thread子类获取线程时,用this.即可,而runnable接口则需要Thread.currentThread()这个静态方法

额外知识:

Thread的常用方法:

 1.start():启动线程并执行相应的run()方法

 2.run():子线程要执行的代码放入run()方法中

 3.currentThread():静态的,调取当前的线程

 4.getName():获取此线程的名字

 5.setName():设置此线程的名字

 6.yield():调用此方法的线程释放当前CPU的执行权(很可能自己再次抢到资源)

 7.join():在A线程中调用B线程的join()方法,表示:当执行到此方法,A线程停止执行,直至B线程执行完毕,A线程再接着join()之后的代码执行

 8.isAlive():判断当前线程是否还存活

 9.sleep(long l):显式的让当前线程睡眠l毫秒  (只能捕获异常,因为父类run方法没有抛异常)

10.线程通信(方法在Object类中):wait()   notify()  notifyAll()

11.getPriority():返回线程优先值

12.setPriority(int newPriority):改变线程的优先级


发表于 2018-05-18 10:52:50 回复(1)
1.继承thread类
2.实现Runnable接口
3.实现callable接口
4.使用executor框架实现线程池
发表于 2017-09-06 16:40:16 回复(0)
extend Thread类
 implements Runnable接口
应用程序可以使用Executor框架来创建线程池()
http://www.infoq.com/cn/articles/executor-framework-thread-pool-task-execution-part-01/
发表于 2016-01-14 19:59:06 回复(0)
有三种方式可以用来创建线程:
继承Thread类
实现Runnable接口
应用程序可以使用Executor框架来创建线程池
实现Runnable接口这种方式更受欢迎,因为这不需要继承Thread类。在应用设计中已经继承了别的对象的情况下,这需要多继承(而Java不支持多继承),只能实现接口。同时,线程池也是非常高效的,很容易实现和使用。
发表于 2015-10-29 12:10:41 回复(0)
1,继承Thread类,重写run方法; 2,实现Runnable接口,重写run方法,但是比继承Thread类好用,实现接口还可以继承类,避免了单继承带来的局限性; 3,实现callable接口,重写call方法,有返回值。 4,使用实现了Executor接口的ThreadPoolExecutor来创建线程池
发表于 2019-04-30 22:31:47 回复(0)
⑴继承Thread类创建线程类
①定义Thread类的子类,并重写该类的run()方法,该run()方法的方法体就代表了线程需要完成的任务,因此把run()方法称为线程执行体。
②创建Thread子类的实例,即创建了线程对象。
③调用线程对象的的start()方法来启动该线程。
下面程序示范了通过继承Thread类来创建并启动多线程。

// 通过继承Thread类来创建线程类
public class FirstThread extends Thread {
private int i;
//重写run()方法,run()方法的方法体就是线程执行体
public void run() {
for ( ; i < 100; i++) {
//当线程类继承Thread类时,直接使用this即可获取当前线程
//Thread对象的getName()返回当前线程的名字
//因此可以调用getName()方法返回当前线程的名字
System.out.println(getName() + " " + i);
}
}
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
//调用Thread的currentThread()方法来获取当前线程
System.out.println(Thread.currentThread().getName() + " " + i);
if (i == 20) {
//创建并启动第一个线程
new FirstThread().start();
//创建并启动第二个线程
new FirstThread().start();
}
}
}
}
注意:
a.    Thread.currentThred(): currentThread()是Thread类的静态方法,该方法总是返回当前正在执行的线程对象。
b.    getName(): 该方法是Thread类的实例方法,该方法返回调用该方法的线程名字。同时程序可以通过setName(String name)方法为线程设置名字。
c.    使用继承Thread类的方法来创建线程类时,多个线程之间无法共享线程类的实例变量。

⑵实现Runnable接口创建线程类
①定义Runnable接口的实现类,并重写该接口的run()方法,该run()方法的方法体同样是该线程的线程执行体。
②创建Runnbale实现类的实例,并以此实例作为Thread的target来创建Thread对象,该Thread对象才是真正的线程对象。代码如下所示。
提示:Runnable对象仅仅作为Thread对象的target,Runnable实现类里包含的run()方法仅仅作为线程执行体。而实际的线程对象依然是Thread实例,只是该Thread线程负责执行其target的run()方法。
③调用线程对象的start方法来启动该线程。
//通过实现Runnable接口来创建线程类
public class SecondThread implements Runnable {
private int i;
// run()方法同样是线程执行体
public void run() {
for ( ; i < 100; i++) {
//当线程类实现Runnable接口时
//如果想获得当前线程,只能用Thread.currentThread()方法
System.out.println(Thread.currentThread().getName() + " " + i);
}
}
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + " " + i);
if (i == 20) {
SecondThread st = new SecondThread(); //
//通过new Thread(target, name)方法创建新线程
new Thread(st, "新线程1").start();
new Thread(st, "新线程2").start();
}
}
}
}

⑶使用Callable和Future创建线程
前面已经指出,通过实现Runnbale接口创建多线程,Thread类的作用就是把run()方法包装成线程执行体。那么是否可以直接把任意方法都包装成线程执行体呢?Java目前不行
Java提供了Callable接口,该接口怎么看都像是Runnbale的增强版,Callable接口提供了一个call方法作为线程执行体,但call方法比run方法功能更强大。
->call()方法可以有返回值
->call()方法可以声明抛出异常
如何获取call方法的返回值呢?
Java提供了Future接口来实现Callable接口里的call方法的返回值,并为Future接口提供了一个FutureTask实现来,该实现类实现了Future接口,并实现了Runnable接口——可以作为Thread类的target。
注意:Callable接口有泛型限制,Callable接口里的泛型形参类型与call()方法返回值类型相同。而且Callable接口时函数式接口,因此可以使用Lambda表达式创建Callable对象。
①创建Callable接口的实现类,并实现call()方法,该call()方法将作为线程执行体,且该call()方法有返回值,再创建Callable实现类的实例。从Java 8开始,可以直接使用Lambda表达式创建Callable对象。
②使用FutureTask类来包装Callable对象,该FutureTask对象封装了该Callable对象的call()方法的返回值。
③使用FutureTask对象作为Thread对象的target创建并启动新线程。
④调用FutrueTask对象的get()方法来获得子线程执行结束后的返回值

public class ThirdThread {
public static void main(String[] args) {
//创建Callable对象
ThirdThread rt = new ThirdThread();
//先使用Lambda表达式创建Callable<Integer>对象
//使用FutureTask来包装Callable对象
FutureTask<Integer> task = new FutureTask<Integer>((Callable<Integer>) () -> {
int i = 0;
for ( ; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + "的循环变量i的值" +
i);
}
//call()方法可以有返回值
return i;
});
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + "的循环变量的值" + i);
if (i == 20) {
//实质还是以Callable对象创建并启动线程的
new Thread(task, "有返回值的线程").start();
}
}
try {
//获取线程返回值
System.out.println("子线程的返回值:" + task.get());
} catch (Exception ex) {
ex.printStackTrace();
}
}
}


创建线程的三种方式对比:
通过继承Thread类或实现Runnable、Callable接口都可以实现多线程,不过实现Runnable接口与实现Callable接口的方式基本相同,只是Callable接口里的定义方法有返回值,可以声明抛出异常而已。因此可以实现Runnbale接口和实现Callable接口归为一种方式。这种方式与继承Thread方式之间的主要差别如下:
采用实现Runnable、Callable接口的方式创建多线程的优缺点:
>线程类只是实现了Runnable、Callable接口,还可以继承其他类。
>在这种方式下,多个线程可以共享同一个target对象,所以非常适合多个线程来处理同一份资源的情况,从而可以将CPU,代码和数据分开,形成清晰的模型,较好地体现了面向对象的思想
劣势是,编程稍稍负责,如果需要访问当前线程,则必须使用Thread.currentThread()方法。
采用继承Thread类的方式创建多线程的优缺点:
劣势是:因为线程类已经继承了Thread类,所以不能再继承其他父类。
优势是:编写简单,如果需要访问当前线程,则无需使用Thread.currentThread()方法,直接使用this既可以获得当前编程。

使用线程池来执行编程任务的步骤如下:
①调用Executors类的静态工厂方法来创建一个ExecutorService对象,该对象代表一个线程池。
②创建Runnable实现类或Callable实现类的实例,作为线程执行任务。
③调用ExecutorService对象的submit方法来提交Runnable实例或者Callable实例。
④当不想提交任何任务时,调用ExecutorService对象的shutdown()方法来关闭线程池。
public class ThreadPoolTest {
public static void main(String[] args) {
//创建一个具有固定线程数(6)的线程池
ExecutorService pool = Executors.nexFiexedThreadPool(6);
//使用Lambda表达式创建Runnable对象
Runnable target = () -> {
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + "的i值为:" + i);
}
};
//向线程池提交两个线程
pool.submit(target);
pool.submit(target);
//关闭线程池
pool.shutdown();
}
}
发表于 2018-08-12 12:25:54 回复(1)
继承Thread类创建线程
实现Runnable接口
实现Callable接口通过FutureTask包装器来创建Thread线程
联合使用ExecutorService、Callable、Future

发表于 2018-03-27 10:51:44 回复(0)

一、      一、继承Thread类创建线程类

(1)定义Thread类的子类,重写该类run( )方法,方法体为要执行任务

(2)创建Thread子类的实例,即创建线程对象

(3)调用线程对象的start方法启动线程【new 子类名().start();

二、 通过Runnable接口创建线程类

(1)定义Runnable的实现类(RunnableThreadTest)实现Runnable接口,复写Runnable中的run方法,方法体为线程执行体

(2)创建一个Runnable实现类对象, 为线程对象提供入口方法,以实现类对象构造一个线程类Thread的实例对象,该对象才是真正的线程对象

RunnableThreadTest  rtt=new RunnableThreadTest( );

Thread t=new Thread(rtt);// 真正的线程对象

(3)调用真正线程对象的start()方法启动该线程

三、 通过CallableFuture创建线程

(1)创建Callable接口的实现类CallableThreadTest,实现call( )方法,call( )存在返回值:egInteger,该call( )将作为线程执行体。

(2)创建CallableThreadTest的实例,创建FutureTask对象封装该实现类对象的call( )的返回值类型

CallableThreadTest ctt=new CallableThreadTest( );

FutureTask<Integer> ft=new FutureTask<>(ctt);

(3)使用FutureTask对象ft作为Thread对象的target,调用.start( )启动新线程

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

使用继承Thread类的方式创建多线程

优势:编写简单,如果访问当前线程,直接使用this即可获得当前线程

劣势:已经继承了Thread类,所以不能够再继承其他父类

采取实现RunnableCallable接口的方式创建多线程

优势:线程类只实现了Runnable接口和Callable接口,还可以继承其它类,在此情形下多线程可共享同一个target对象,非常适合多个相同线程处理同一份资源的情况,从而将CPU、代码和数据分开,形成清晰的模型,较好的体现了面向对象的思想。

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

发表于 2019-09-04 19:48:15 回复(0)

1.继承thread类重写run方法

2.实现runnable接口,实现run方法

3.实现callable接口,实现call方法

4.executor框架的最大优点是把任务的提交和执行解耦。要执行任务的人只需把Task描述清楚,然后提交即可。这个Task是怎么被执行的,被谁执行的,什么时候执行的,提交的人就不用关心了。具体点讲,提交一个Callable对象给ExecutorService(如最常用的线程池ThreadPoolExecutor),将得到一个Future对象,调用Future对象的get方法等待执行结果就好了。


经过这样的封装,对于使用者来说,提交任务获取结果的过程大大简化,调用者直接从提交的地方就可以等待获取执行结果。而封装最大的效果是使得真正执行任务的线程们变得不为人知。有没有觉得这个场景似曾相识?我们工作中当老大的老大(且称作LD^2)把一个任务交给我们老大(LD)的时候,到底是LD自己干,还是转过身来拉来一帮苦逼的兄弟加班加点干,那LD^2是不管的。LD^2只用把人描述清楚提及给LD,然后喝着咖啡等着收LD的report即可。等LD一封邮件非常优雅地报告LD^2report结果时,实际操作中是码农A和码农B干了一个月,还是码农ABCDE加班干了一个礼拜,大多是不用体现的。这套机制的优点就是LD^2找个合适的LD出来提交任务即可,接口友好有效,不用为具体怎么干费神费力。


二、 一个最简单的例子


看上去这个执行过程是这个样子。调用这段代码的是老大的老大了,他所需要干的所有事情就是找到一个合适的老大(如下面例子中laodaA就荣幸地被选中了),提交任务就好了。


// 一个有7个作业线程的线程池,老大的老大找到一个管7个人的小团队的老大

       ExecutorService laodaA = Executors.newFixedThreadPool(7);

   //提交作业给老大,作业内容封装在Callable中,约定好了输出的类型是String。

   String outputs = laoda.submit(

            new Callable<String>() {

                public String call() throws Exception 

                {                   

                    return "I am a task, which submited by the so called laoda, and run by those anonymous workers";

                }

                //提交后就等着结果吧,到底是手下7个作业中谁领到任务了,老大是不关心的。

            }).get();


   System.out.println(outputs);

使用上非常简单,其实只有两行语句来完成所有功能:创建一个线程池,提交任务并等待获取执行结果。


例子中生成线程池采用了工具类Executors的静态方法。除了newFixedThreadPool可以生成固定大小的线程池,newCachedThreadPool可以生成一个***、可以自动回收的线程池,newSingleThreadScheduledExecutor可以生成一个单个线程的线程池。newScheduledThreadPool还可以生成支持周期任务的线程池。一般用户场景下各种不同设置要求的线程池都可以这样生成,不用自己new一个线程池出来。

发表于 2018-08-02 07:50:04 回复(0)
四种 1 继承Thread 2 实现Runnable接口 3 实现callable接口 4 利用executor创建线程池 第二种第三种好于第一种,因为java只能继承一个父类,但可以实现多个接口,当有其他父类需要继承时,实现接口更方便。
发表于 2017-12-05 00:10:27 回复(0)
1.继承Thread类
2、实现Runnable接口。
3.应用程序可以实现Executor框架创建线程池。
第二种最受欢迎,因为java不支持类的多继承。
发表于 2017-08-14 22:29:17 回复(0)
还有一种方式可以通过Callable接口实现多线程,重写call方法,优点是可以获取返回值,确定是比较繁琐
发表于 2017-04-07 15:08:12 回复(0)
Callable也算一种
发表于 2017-03-15 19:23:16 回复(0)

①继承Thread类(真正意义上的线程类),是Runnable接口的实现。

②实现Runnable接口,并重写里面的run方法。

③使用Executor框架创建线程池。Executor框架是juc里提供的线程池的实现。

调用线程的start():启动此线程;调用相应的run()方法

继承于Thread类的线程类,可以直接调用start方法启动线程(使用static也可以实现资源共享).一个线程(对象)只能够执行一次start(),而且不能通过Thread实现类对象的run()去启动一个线程。

实现Runnable接口的类需要再次用Thread类包装后才能调用start方法。(三个Thread对象包装一个类对象,就实现了资源共享)。

线程的使用的话,注意锁和同步的使用。(多线程访问共享资源容易出现线程安全问题)


 

一般情况下,常见的是第二种。

* Runnable接口有如下好处:

*①避免点继承的局限,一个类可以继承多个接口。

*②适合于资源的共享

 

/*

 * Thread的常用方法:

 * 1.start():启动线程并执行相应的run()方法

 * 2.run():子线程要执行的代码放入run()方法中

 * 3.currentThread():静态的,调取当前的线程

 * 4.getName():获取此线程的名字

 * 5.setName():设置此线程的名字

 * 6.yield():调用此方法的线程释放当前CPU的执行权(很可能自己再次抢到资源)

 * 7.join():在A线程中调用B线程的join()方法,表示:当执行到此方法,A线程停止执行,直至B线程执行完毕,

 * A线程再接着join()之后的代码执行

 * 8.isAlive():判断当前线程是否还存活

 * 9.sleep(long l):显式的让当前线程睡眠l毫秒  (只能捕获异常,因为父类run方法没有抛异常)

 * 10.线程通信(方法在Object类中):wait()   notify()  notifyAll()

 *

 *设置线程的优先级(非绝对,只是相对几率大些)

 * getPriority():返回线程优先值

 * setPriority(int newPriority):改变线程的优先级

 */

编辑于 2016-10-18 09:07:28 回复(15)
1,继承Thread类,重写run方法;
2,实现Runnable接口,重写run方法,但是比继承Thread类好用,实现接口还可以继承类,避免了单继承带来的局限性;
3,实现callable接口,重写call方法,有返回值。
4,使用实现了Executor接口的ThreadPoolExecutor来创建线程池。
发表于 2017-03-08 16:43:06 回复(1)
①继承Thread类(真正意义上的线程类),是Runnable接口的实现。 ②实现Runnable接口,并重写里面的run方法。 ③使用Executor框架创建线程池。Executor框架是juc里提供的线程池的实现。 调用线程的start():启动此线程;调用相应的run()方法
发表于 2021-06-25 16:04:53 回复(0)
<p>new Thread().start;</p><p><br></p>
发表于 2021-04-09 12:54:17 回复(0)
1. 继承Thread类,重写run方法 2. 实现Runnable接口,重写run方法 3. 使用Executor接口中的ThreadPoolExecutor创建线程池,调用线程的start方法开始一个线程,调用线程的run方法运行 4. 实现Callable接口,重写call方法
发表于 2021-03-12 23:22:45 回复(0)
原因一:Java中,任何对象都可以作为锁,既然wait是放弃对象锁,当然就要把wait定义在这个对象所属的类中。更通用一些,由于所有类都继承于Object,我们完全可以把wait方法定义在Object类中,这样,当我们定义一个新类,并需要以它的一个对象作为锁时,不需要我们再重新定义wait方法的实现,而是直接调用父类的wait(也就是Object的wait),此处,用到了Java的继承。 原因二:有的人会说,既然是线程放弃对象锁,那也可以把wait定义在Thread类里面啊,新定义的线程继承于Thread类,也不需要重新定义wait方法的实现。然而,这样做有一个非常大的问题,一个线程完全可以持有很多锁,你一个线程放弃锁的时候,到底要放弃哪个锁?当然了,这种设计并不是不能实现,只是管理起来更加复杂。
发表于 2020-12-08 10:52:40 回复(0)