多线程(multithreading),是指从软件或者硬件上实现多个线程并发执行的技术。具有多线程能力的计算机因有硬件支持而能够在同一时间执行多于一个线程,进而提升整体处理性能。在一个程序中,这些独立运行的程序片段叫作“线程”(Thread),利用它编程的概念就叫作“多线程处理“。
相关概念:
程序(program):程序是指令和数据的集合,他没有运行的概念,是一个静态的概念。
进程(process):程序执行的一次过程,当一个程序进入内存运行,即变成一个进程,它是一个动态的概念。是系统资源分配的单位。
线程(thread):线程是进程中的一个执行单元,负责当前进程中程序的执行,一个进程中至少有一个线程。一个进程中是可以有多个线程的,这个应用程序也可以称之为多线程程序。线程是CPU调度和执行的单位。
并行:多个cpu实例或者多台机器同时执行一段处理逻辑,是真正的同时。
并发:通过cpu调度算法,让用户看上去同时执行,实际上从cpu操作层面不是真正的同时。并发往往在场景中有公用的资源,那么针对这个公用的资源往往产生瓶颈,我们会用TPS或者QPS来反应这个系统的处理能力。
同步:Java中的同步指的是通过人为的控制和调度,保证共享资源的多线程访问成为线程安全,来保证结果的准确。如上面的代码简单加入@synchronized
关键字。在保证结果准确的同时,提高性能,才是优秀的程序。线程安全的优先级高于性能。
大部分操作系统都支持多进程并发运行,现在的操作系统几乎都支持同时运行多个程序。比如:现在我们上课一边使用编辑器,一边使用录屏软件,同时还开着画图板,dos窗口等软件。此时,这些程序是在同时运行,”感觉这些软件好像在同一时刻运行着“。
实际上,CPU(中央处理器)使用抢占式调度模式在多个线程间进行着高速的切换。对于CPU的一个核而言,某个时刻,只能执行一个线程,而 CPU的在多个线程间切换速度相对我们的感觉要快,看上去就是在同一时刻运行。
其实,多线程程序并不能提高程序的运行速度,但能够提高程序运行效率,让CPU的使用率更高。
方法名 | 描述 |
---|---|
start() | 启动当前线程(即调用start方法的线程),并调用当前线程的run方法 |
run() | 通常需要进行重写,将创建的线程需要执行的操作写在run方法中 |
currentThread() | 静态方法,这么用:Thread.currentThread()用来返回执行这一行代码的线程,返回类型为Thread |
getName() | 获取当前线程的名字 |
setName() | 设置当前线程的名字,记得要在start方法之前进行。 |
stop() | 强制线程生命期结束(不是阻塞),不推荐使用,API中已被废弃 |
sleep(long millitime) | 静态方法,让当前的线程阻塞指定的毫秒数。传入的参数是毫秒(1秒=1000毫秒),比如想让线程休息(阻塞)1秒,就写1000。经常在显示倒计时的程序中使用 |
isAlive() | 判断当前线程是否还存活(执行完run方法之后线程就死亡了) |
join() | 在线程a中调用线程b的join方法,使得线程a进入阻塞状态(CPU想让它执行也执行不了)直到线程b完全执行完,线程a才结束阻塞状态 |
线程的创建有三种方式:继承Thread类、实现Runable()接口、实现Callable接口。
步骤:
public class Demo02 extends Thread{ @Override public void run() { for(int i=0;i<10;i++) { try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("睡觉睡觉睡觉睡觉睡觉睡觉睡觉睡觉睡觉睡觉"); } } public static void main(String[] args) { Demo02 demo02 = new Demo02(); demo02.start(); for(int i=0;i<100;i++) System.out.println("吃饭吃饭吃饭吃饭吃饭吃饭吃饭吃饭"); } }
运行结果:
可以看到主线程和另一线程在交替执行。
实现Runnable接口避免了单继承的局限性,所以较为常用。实现Runnable接口的方式,更加的符合面向对象,线程分为两部分,一部分线程对象,一部分线程任务。继承Thread类,线程对象和线程任务耦合在一起。一旦创建Thread类的子类对象,既是线程对象,有又有线程任务。实现runnable接口,将线程任务单独分离出来封装成对象,类型就是Runnable接口类型。Runnable接口对线程对象和线程任务进行解耦。
1、定义类实现Runnable接口。
2、实现接口中的run方法。。
3、创建Thread类的对象
4、将Runnable接口的子类对象作为参数传递给Thread类的构造函数。
5、调用Thread类的start方法开启线程。
public class demo01 implements Runnable{ private int ticket; public demo01(int ticket) { this.ticket = ticket; } public static void main(String[] args) { demo01 demo01 = new demo01(10); new Thread(demo01,"狗剩").start(); new Thread(demo01,"铁锤").start(); new Thread(demo01,"柱子").start(); new Thread(demo01,"翠花").start(); } @Override public void run() { while (ticket!=0) { System.out.println(Thread.currentThread().getName() + "-->拿到了第" + ticket-- + "张票"); try { Thread.sleep(20); } catch (InterruptedException e) { e.printStackTrace(); } } } }
运行结果:
继承Thread类实现的多线程相当于每条线程对应一个任务,而实现Runable接口一个任务和可以由多条线程共享。
如果用买票例子来说明的话,继承Thread类是每一条线程都有十张票,而实现Runable接口可以是所有线程共同卖十张票。
实现Runable接口避免了单继承的局限,线程对象和线程任务分离,方便同一个对象被多条线程使用(方便资源共享)。
Callable接口:与Runnable接口功能相似,用来指定线程的任务。其中的call()方法,用来返回线程任务执行完毕后的结果,call方法可抛出异常。
与使用Runnable相比,Callable功能更强大:
可以对具体Runnable、Callable任务的执行结果进行取消、查询是否完成、获取结果等。
FutureTask是Future接口的唯一的实现类。
FutureTask同时实现了Runnable、Future接口,它既可以作为Runnable被线程执行,也可
作为Future得到Callable的返回值.
public class MyThread implements Callable { @Override public Boolean call() throws Exception { System.out.println("吃饭"); return true; } public static void main(String[] args) throws ExecutionException, InterruptedException { MyThread myThread = new MyThread(); // 1. 创建执行服务, ExecutorService ser= Executors.newFixedThreadPool(1); // 2. 提交执行, Future<Boolean> result1=ser.submit(myThread); // 3. 获取结果, Boolean r1=result1.get(); // 4. 关闭服务, ser.shutdownNow(); } }
运行结果:
原文:https://www.cnblogs.com/EricsBlog/p/15053930.html