java线程(一)创建线程的方式

创建线程的方式一共有4种,这里只说明前3种。

1、继承Thread类

重写run()方法,调用start方法启动

public class Demo1 {
    
    
    public static void main(String[] args) {
    
    
        MyThread th1 = new MyThread();
        th1.start(); // 注:一定要是使用start()方法调用

        System.out.println("我是主进程");
    }
}
class MyThread extends Thread{
    
    

    @Override
    public void run() {
    
    
        System.out.println("我是子线程");

    }
}

输出结果:

我是主进程
我是子线程

注意:一定要使用start()方法,启动,否则仅仅调用MyThread类的run()方法了。

2、实现Runnble接口

实现Runnble接口,再将实现类作为参数传入Thread

public class Demo2 {
    
    
    public static void main(String[] args) {
    
    
        MyRunnble runnble = new MyRunnble();

        Thread th1 = new Thread(runnble);
        th1.start();

        System.out.println("我是主进程");
    }
}

class MyRunnble implements Runnable{
    
    

    @Override
    public void run() {
    
    
        System.out.println("我是子进程");
    }
}

结果:

我是主进程
我是子进程

其实前面两种创建线程的方式一样的,都是实现Runnble接口在此看下Thread的源码:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
我们会发现,Thread类也继承了Runnble接口,我们选择继承Thread的形式,重写run()方法,实质上也是实现Runnble接口。

3、使用Callable、FutureTask

实现Callable接口,并将实现类作为参数传入FutureTask,再将FutureTask 作为参数传入Thread

public class Demo3 {
    
    

    public static void main(String[] args) throws Exception{
    
    
        MyCallable myCallable = new MyCallable();
        FutureTask<String> f = new FutureTask<>(myCallable); 
        Thread th = new Thread(f);

        th.start();
        String s = f.get();
        System.out.println(s);

        System.out.println("hahha");
    }
}

class MyCallable implements Callable{
    
    

    @Override
    public Object call() throws Exception {
    
    

        Thread.sleep(10000);

        return "hello world";
    }
}

注意:
1、Callable#call()方法有返回值,返回值的类型需要定义为FutureTask的泛型(如果不了解泛型,可以把泛型看作“特殊的Object对象”)
2、FutureTask#get()可以获取call()方法的返回值,get()方法是一个阻塞方法。这个在后续章节细讲

4、线程池

这个会在之后单独用一个系列来分析

几种方式的区别

1、Thread和Runnble的区别
这个我想大家应该都清楚,主要是程序扩展性问题。推荐使用实现接口的方式。如果一个类继承了Thread类就不能在继承其他的类了(java单继承,多实现),但是接口没有这种限制

2、Runnble和Callable的区别
(1)、run()方法没有返回值,call()方法有返回值
(2)、run()方法不能抛出异常(run方法一旦遇到异常,会导致本线程死掉),call()方法可以抛出异常(如果不调用FutureTask#get()方法,即使有异常也不会抛出)
eg:没有调用get方法,没有抛出异常

public class Demo3 {
    
    

    public static void main(String[] args) throws Exception{
    
    
        MyCallable myCallable = new MyCallable();
        FutureTask<String> f = new FutureTask<>(myCallable);
        Thread th = new Thread(f);

        th.start();
      // String s = f.get();
       // System.out.println(s);

        System.out.println("hahha");
    }
}

class MyCallable implements Callable{
    
    

    @Override
    public Object call() throws Exception {
    
    

        System.out.println(5/0);

        return "hello world";
    }
}
hahha

调用get方法,抛出异常

public class Demo3 {
    
    

    public static void main(String[] args) throws Exception{
    
    
        MyCallable myCallable = new MyCallable();
        FutureTask<String> f = new FutureTask<>(myCallable);
        Thread th = new Thread(f);

        th.start();
        String s = f.get();
        System.out.println(s);

        System.out.println("hahha");
    }
}

class MyCallable implements Callable{
    
    

    @Override
    public Object call() throws Exception {
    
    

        System.out.println(5/0);

        return "hello world";
    }
}
Exception in thread "main" java.util.concurrent.ExecutionException: java.lang.ArithmeticException: / by zero
	at java.util.concurrent.FutureTask.report(FutureTask.java:122)
	at java.util.concurrent.FutureTask.get(FutureTask.java:192)
	at cn.gdh.thread.create.Demo3.main(Demo3.java:14)
Caused by: java.lang.ArithmeticException: / by zero
	at cn.gdh.thread.create.MyCallable.call(Demo3.java:26)
	at java.util.concurrent.FutureTask.run(FutureTask.java:266)
	at java.lang.Thread.run(Thread.java:748)

猜你喜欢

转载自blog.csdn.net/qq_43579103/article/details/102768456