线程的创建

线程的创建分为4种方式:

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

启动线程是调用start方法,这样会创建一个新的线程,并执行线程的任务。
如果直接调用run方法,这样会让当前线程执行run方法中的业务逻辑,但不能得到多线程的效果。

public class MiTest {

    public static void main(String[] args) {
        MyJob t1 = new MyJob();
        t1.start();
        for (int i = 0; i < 100; i++) {
            System.out.println("main:" + i);
        }
    }

}
class MyJob extends Thread{
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println("MyJob:" + i);
        }
    }
}

2.实现Runnable接口 重写run方法

public class MiTest {

    public static void main(String[] args) {
        MyRunnable myRunnable = new MyRunnable();
        Thread t1 = new Thread(myRunnable);
        t1.start();
        for (int i = 0; i < 1000; i++) {
            System.out.println("main:" + i);
        }
    }

}

class MyRunnable implements Runnable{

    @Override
    public void run() {
        for (int i = 0; i < 1000; i++) {
            System.out.println("MyRunnable:" + i);
        }

    }
}

最常用的方式:

  • 匿名内部类方式
Thread t1 = new Thread(new Runnable() {
    @Override
    public void run() {
        for (int i = 0; i < 1000; i++) {
            System.out.println("匿名内部类:" + i);
        }
    }
});
  • lambda方式
Thread t2 = new Thread(() -> {
    for (int i = 0; i < 100; i++) {
        System.out.println("lambda:" + i);
    }
});

3.实现Callable 重写call方法,配合FutureTask

Callable一般用于有返回结果的非阻塞的执行方法

同步非阻塞。
FutureTask底层实现了Ruunable接口,run()中调用了call()

public class MiTest {

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //1. 创建MyCallable
        MyCallable myCallable = new MyCallable();
        //2. 创建FutureTask,传入Callable
        FutureTask futureTask = new FutureTask(myCallable);
        //3. 创建Thread线程
        Thread t1 = new Thread(futureTask);
        //4. 启动线程
        t1.start();
        //5. 做一些操作
        //6. 要结果
        Object count = futureTask.get();
        System.out.println("总和为:" + count);
    }
}

class MyCallable implements Callable{

    @Override
    public Object call() throws Exception {
        int count = 0;
        for (int i = 0; i < 100; i++) {
            count += i;
        }
        return count;
    }
}

4.基于线程池构建线程

线程是非常宝贵的计算资源,在每次需要时创建并在运行结束后销毁是非常浪费系统资源的。我们可以使用缓存策略并通过线程池来创建线程。具体实现过程为创建一个线程池并用该线程池提交线程任务。

ExecutorService threadPool = Executors.newFixedThreadPool(10);
		for (int i = 0; i < 10; i++) {
			//提交多个线程任务并执行
			threadPool.execute(new Runnable(){
				@Override
				public void run() {
					System.out.println(Thread.currentThread().getName()+"is running");
				}
			});
		}

总结:这四种方法追其底层,就是只有一种,实现Runable接口

5.相关问题:

(1)Thread类中为什么调用start()方法而不是run()方法

先看start()源码,底层调用start0(),它是一个本地方法

public synchronized void start() {
        /**
         * This method is not invoked for the main method thread or "system"
         * group threads created/set up by the VM. Any new functionality added
         * to this method in the future may have to also be added to the VM.
         *
         * A zero status value corresponds to state "NEW".
         */
        if (threadStatus != 0)    // 判断线程的状态
            throw new IllegalThreadStateException();    // 抛出异常
 
        /* Notify the group that this thread is about to be started
         * so that it can be added to the group's list of threads
         * and the group's unstarted count can be decremented. */
        group.add(this);
 
        boolean started = false;
        try {
            start0();    // 在start()方法中调用start0()
            started = true;
        } finally {
            try {
                if (!started) {
                    group.threadStartFailed(this);
                }
            } catch (Throwable ignore) {
                /* do nothing. If start0 threw a Throwable then
                  it will be passed up the call stack */
            }
        }
    }
 
    private native void start0();    // 只定义方法名称没有实现,返回类型native

run()的源码

 @Override
    public void run() {
        if (target != null) {
            target.run();
        }
    }

  原因就是Java以良好的跨平台可移植性著称,而在不同的操作系统之中他的资源调度算法是不同的,但操作系统会提供底层函数来进行资源调度。所以当我们在Java程序中调用start()方法时,然后start()方法会去调用start0(),而start0()只有定义没有实现,那么实现是由谁来做的呢?是JVM来做,JVM会根据用户系统类型实现相应的start0()方法,以此实现多线程。而直接调用run()方法并不能达到此效果。
  run()源码并没有实现多线程,如果是直接调用run(),则是顺序执行的,不是多线程。
线程的创建_第1张图片

(2)实现Callable接口创建线程与实现Runnable接口和继承Thread创建线程有什么区别?

① Callable接口可以有返回值
② Callable接口可以抛出异常
③ 执行方法不同,call()方法和run()方法
如果需要获取一个线程执行完毕的结果, 相比其他方式,使用 Callable 更方便不易出错。比如计算0-100累加的和:

非Callable接口方式:

static class Result {
    public int sum = 0;
    public Object lock = new Object();
}

public static void main(String[] args) throws InterruptedException {
    Result result = new Result();
    Thread t = new Thread() {
        @Override
        public void run() {
            int sum = 0;
            for (int i = 1; i <= 1000; i++) {
                sum += i;
           }
            synchronized (result.lock) {
                result.sum = sum;
                result.lock.notify();
           }
       }
   };
    t.start();
    synchronized (result.lock) {
        while (result.sum == 0) {
        	// 等待 t 线程计算完之后唤醒当前阻塞状态
            result.lock.wait();
       }
        System.out.println(result.sum);
   }
}

虽然这种方式也获取到了线程执行完毕的结果, 但本质上并不是作为返回值的, 而是两个线程修改了同一个变量, 这个变量是 result 类的成员属性, 并且实现的过程比较复杂

所以如果需要获取一个线程执行完毕的结果, 使用 Callable 更方便不易出错

Callable<Integer> callable = new Callable<Integer>() {
    @Override
    public Integer call() throws Exception {
        int sum = 0;
        for (int i = 1; i <= 1000; i++) {
            sum += i;
        }
        return sum;
   }
};
FutureTask<Integer> futureTask = new FutureTask<>(callable);
Thread t = new Thread(futureTask);
t.start();
int result = futureTask.get();
System.out.println(result);

你可能感兴趣的:(JAVA高并发,java,算法,开发语言)