每个正在系统上运行的程序都是一个进程。每个进程包含一到多个线程。线程是一组指令的集合,或者是程序的特殊段,它可以在程序里独立执行。也可以把它理解为代码运行的上下文。所以线程基本上是轻量级的进程,它负责在单个程序里执行多任务。通常由操作系统负责多个线程的调度和执行。
总结:进程是所有线程的集合,每一个线程是进程中的一条执行路径。
2.为什么要使用多线程
案例:如果一个工人修建一条1000米长的铁路需要10个小时,为了尽快完成工期,需要在一个小时内完成所有的铁路修建,如何解决呢?
此时可以在加派9个工人,最后就是10个工人一起修建(考虑每个工人的修建速度一样)则只需要1个小时就可以完成.
同样:使用多线程可以提高程序的效率.
1.继承Thread类,重写run方法
package com.thread; public class ThreadDemo extends Thread { @Override public void run() { System.out.println("继承Thread重写run方法"); } public static void main(String[] args) { ThreadDemo threadDemo = new ThreadDemo(); threadDemo.start(); } }
输出:继承Thread重写run方法
注意:启动一个线程是用的.start()方法 而不是直接调用的run方法,如果直接调用run方法只是实例化的一个方法调用,根本没有启动一个线程
思考:开启一个线程以后,该线程和main方法的执行先后顺序?
package com.thread; public class ThreadDemo extends Thread { @Override public void run() { System.out.println("继承Thread重写run方法"); } public static void main(String[] args) { System.out.println("main方法开始运行"); ThreadDemo threadDemo = new ThreadDemo(); threadDemo.start(); System.out.println("main方法结束"); } }
输出结果:
main方法开始运行
main方法结束
继承Thread重写run方法
从输出结果可以看出,主程序的运行结束与否和新创建的线程运行情况无关,即使主线程运行完成,此时可能用户线程还在运行,不会随着主线程消亡而结束(守护线程则会)
2.实现Runnable接口的方法
package com.thread; public class RunnableDemo implements Runnable { public void run() { System.out.println("实现Runnable接口重写run方法"); } public static void main(String[] args) { RunnableDemo runnableDemo = new RunnableDemo(); Thread thread = new Thread(runnableDemo); thread.start(); } }
输出结果:
实现Runnable接口重写run方法
3.使用匿名内部类的方法创建多线程
public static void main(String[] args) { new Thread(new Runnable() { public void run() { System.out.println("匿名内部类的方法创建线程运行run方法"); } }).start(); }
4.使用Callabel接口创建一个具有返回值的线程
package com.thread; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.FutureTask; public class CallableThreadDemo implements Callable<String> { public static void main(String[] args) throws ExecutionException, InterruptedException { CallableThreadDemo callableThreadDemo = new CallableThreadDemo(); FutureTask<Integer> futureTask = new FutureTask(callableThreadDemo); new Thread(futureTask, "有返回值的线程").start(); System.out.println(futureTask.get()); } public String call() throws Exception { return "我是线程 Callable接口的返回值哟"; } }
输出结果:
我是线程 Callable接口的返回值哟
5.使用线程池创建线程(后续补充)
4. 在创建线程的时候使用Thread继承好还是使用Runnable接口好
答:使用接口好,接口可以多继承
5.常用的线程API
1.获取当前线程对象的方法:currentThread()
2.启动线程:.start()
3.休眠线程:sleep(时间)cpu执行其他线程,但是不会释放当前线程已经拥有的锁
4.停止线程:stop()
线程分成两类
1.用户自定义线程:该线程不会随着主线程死亡而死亡,可以理解成main方法执行完毕以后,自定义线程还在执行
2.守护线程:主线程执行完毕以后守护线程就自动死亡,可以通过thread.setDaemon(true)将该线程设置为守护线程
7.线程的状态(重点)
1.新建状态:new Thread()表示一个线程的创建,但是此时线程还并没有开始执行
2.就绪状态:当前线程调用start()的时候 表示线程已经就绪,此时等待cpu调度
3.运行状态:如果当前线程获取到了cpu的执行权,则开始运行该程序(执行run方法)
4.死亡状态:run方法正常的执行完毕,或者run方法里面执行错误,该错误没有被捕获也会导致线程死亡(和main方法一样如果程序出错,没有捕获异常,则程序不会继续执行)
5.阻塞状态:导致线程阻塞的情况有很多场景:比如调用sleep()方法,在同步锁当中执行一个方法的时候,没有获取到该方法的锁也会阻塞。如果操作数据库的时候IO时间较长,该线程也会阻塞 在调用sleep()方法以后,休眠时间到了,重新获取到cpu的执行权,则该线程又进入到运行状态
优先级:现代操作系统基本采用时分的形式调度运行的线程,线程分配得到的时间片的多少决定了线程使用处理器资源的多少,也对应了线程优先级这个概念。在JAVA线程中,通过一个int priority来控制优先级,范围为1-10,其中10最高,默认值为5。可以通过 Thread.setPriority设置线程的优先级,设置优先级并不是每次都执行这个线程,只是会优先于这个线程
顺序性:如果有三个线程,你先让线程2先执行,执行完成以后 在执行线程1,最后在执行线程3 使用join
package com.thread; public class ThreadDemo extends Thread { @Override public void run() { String name = currentThread().getName(); System.out.println(name + "继承Thread重写run方法"); } public static void main(String[] args) throws InterruptedException { ThreadDemo threadDemo1 = new ThreadDemo(); threadDemo1.setName("线程1"); ThreadDemo threadDemo2 = new ThreadDemo(); threadDemo2.setName("线程2"); ThreadDemo threadDemo3 = new ThreadDemo(); threadDemo3.setName("线程3"); threadDemo2.start(); threadDemo2.join(); threadDemo1.start(); threadDemo1.join(); threadDemo3.start(); } }
输出结果:
线程2继承Thread重写run方法
线程1继承Thread重写run方法
线程3继承Thread重写run方法
原文:https://www.cnblogs.com/920913cheng/p/11340597.html