多线程01-多线程基本概念

180 阅读4分钟

1、什么是多线程、什么场景下使用多线程?

要学习多线程,首先,我们要学习关于多线程的基本概念。 - 程序:为完成某任务,用某种语言编写所组成的静态代码

- • 进程:正在运行的程序,也就是加载到内存中。一个进程有一个方法区和一个堆

• 线程:一个程序内部的执行路径,一个进程通常会有多个线程。一个线程有一个虚拟机栈和一个程序计数器,也就是进程下的线程共享该进程的方法区和堆区,且每个线程都有自己独有的虚拟机栈和程序计数器(PC)(拓展:JVM调优也就是调整共享的这部分) • 并行:多个CPU同时执行多个任务

• 并发:一个CPU“同时”(采用时间片)执行多个任务,如:秒杀、多个人做同一个事

• 多线程优点:提高程序响应(对图形化界面更有意义,提升用户体验)、提高CPU利用率、改善程序结构,利于理解与修改

• 多线程场景:程序需要同时执行两个或多个任务、需要实现一些等待任务(文件读写、网络操作、搜索等)、需要一些后台运行的程序;现在的cpu基本是多核,我通常利用多线程利用并行,提升cpu性能。

2、如何创建多线程? 实现线程,首先,我们需要创建线程,在java基础中,我们会学习继承Thread和实现Runnable接口,还有后面加入的其他相关方式,但是其归根还是利用同一种方式实现。 1、实现 Runnable 接口

public class RunnableThread implements Runnable {
    @Override
    public void run() {
        System.out.println("start the thread");
    }
    public static void main(String[] args) {
        //创建实现runnable的实例
        RunnableThread runnableThread = new RunnableThread();
        //将创建的实例创建thread,即可开启线程
        new Thread(runnableThread).start();
    }
} 
      方式1中RunnableThread实现Runnable接口,重写run方法;将实现runnable 的接口传进Threa类,即可实现。

2、继承Thread

public class ExecuteThread extends Thread {
    @Override
    public void run() {
        System.out.println("开启线程");
    }
    public static void main(String[] args) {
        new ExecuteThread().start();
    }
}

ExecuteThread继承Thread并重写run方法, 3、线程池创建线程 在使用线程池ThreadPoolExecutor或者其他线程池中,其构造函数会指定默认线程工厂DefaultThreadFactory 。 那DefaultThreadFactory 如何实现多线程的创建。

 static class DefaultThreadFactory implements ThreadFactory {
        private static final AtomicInteger poolNumber = new AtomicInteger(1);
        private final ThreadGroup group;
        private final AtomicInteger threadNumber = new AtomicInteger(1);
        private final String namePrefix;
        DefaultThreadFactory() {
            SecurityManager s = System.getSecurityManager();
            group = (s != null) ? s.getThreadGroup() :
                                  Thread.currentThread().getThreadGroup();
            namePrefix = "pool-" +
                          poolNumber.getAndIncrement() +
                         "-thread-";
        }
        public Thread newThread(Runnable r) {
            Thread t = new Thread(group, r,
                                  namePrefix + threadNumber.getAndIncrement(),
                                  0);
            if (t.isDaemon())
                t.setDaemon(false);
            if (t.getPriority() != Thread.NORM_PRIORITY)
                t.setPriority(Thread.NORM_PRIORITY);
            return t;
        }
    }
多线程本质上也是通过newThread实现线程的创建。

4、有返回值的 Callable 创建线程

public class CallableThread implements Callable {
    @Override
    public Object call() throws Exception {
        return "callable";
    }
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        ExecutorService executorService = Executors.newSingleThreadExecutor();
        Future submit = executorService.submit(new CallableThread());
        System.out.println(submit.get());
        System.out.println("main end");
    }
}
4 种线程创建方式是通过有返回值的 Callable 创建线程,Runnable 创建线程是无返回值的,而 Callable 和与之相关的 Future、FutureTask,它们可以把线程执行的结果作为返回值返回,

5、总结

5.1、实现线程只有一种方式

多线程的实现都是通过Runnable 接口或继承 Thread 类实现的。,其他形式只不过对其进行了包装。

5.2、实现 Runnable 接口比继承 Thread 类实现线程要好

- 1. 在某些场合会带来性能的提升,继承Thread,每次执行任务时需要创建对象,特别当任务耗时特别短,创建对象的开销可能比任务执行更大。当使用runnable时可以将任务丢给线程池,复用线程来提升性能。

- 2. 由于java语言的特性,类只能单继承,可以多实现;使用继承不便于代码的扩展。

3、多线程有几种状态

- 1. New(新创建)当我们通过new创建线程之后,调用start方法之前的状态

2. Runnable(可运行)当线程调用start方法之后,线程处于ready或者running状态,这取决cpu的任务调度机制

- 3. Blocked(被阻塞)当我们加了锁,其便处于堵塞状态,需要等待其他线程将该锁释放,才能可能从Blocked变换为Runnable。

- 4. Waiting(等待) 当线程Runnable状态通过wait()、sleep()、join()、LockSupport.park()当前线程便处于堵塞状态,通过notify或者LockSupport.unpark()

- 5. Timed Waiting(计时等待)当线程Runnable状态通过wait(timeout)、sleep(timeout)、join(timeout)、LockSupport.park(timeout)当前线程便处于堵塞状态,通过notify或者LockSupport.unpark()

- 6. Terminated(被终止)即线程执行结束