多线程是指从软件或硬件上实现多个线程并发执行的技术,具有多线程能力的计算机因有硬件支持而能够在同一时间执行多个线程从而提升性能
并发和并行
并行:同一时刻多个CPU同时执行多个指令
并发:同一时刻单个CPU同时执行多个指令
进程和线程
进程:正在运行的程序
独立性:进程是一个能独立运行的基本单位,同时也是系统分配资源和调度的独立单位
动态性:进程的实质是程序的一次执行过程,进程是动态产生和动态消亡的
线程:是进程中的单个顺序控制流,一条执行路径
单线程:一个进程只有一条执行路径就称为单线程程序
多线程:一个进程有多条执行路径就称为多线程程序
实现多线程方式一:继承Thread
public class Test extends Thread{ // 重写run方法 @Override public void run() { for (int i = 0; i < 20; i++) { System.out.println(i); } } }
public class Test2 { public static void main(String[] args) { Test t1 = new Test(); Test t2 = new Test(); // 开启线程 t1.start(); t2.start(); } }
开启线程使用start方法
实现多线程方式二:实现Runnable接口
public class Test implements Runnable{ // 重写run方法 @Override public void run() { for (int i = 0; i < 20; i++) { System.out.println(i); } } }
public class Test2 { public static void main(String[] args) { // 创建实现Runnable接口的实现类对象 Test T = new Test(); // 创建Thread类的对象将Test对象作为参数传入 Thread t1 = new Thread(T); Thread t2 = new Thread(T); // 开启线程 t1.start(); t2.start(); } }
多线程实现方式三:实现Callable接口
public class Test implements Callable<String> { @Override public String call() throws Exception { for (int i = 0; i < 20; i++) { System.out.println(i); } return "线程执行完毕"; } }
public class Test2 { public static void main(String[] args) { Test T = new Test(); FutureTask<String> ft = new FutureTask<>(T); Thread t1 = new Thread(ft); Thread t2 = new Thread(ft); // 开启线程 t1.start(); t2.start(); } }
三种实现方式对比
实现Runnabl Callable接口
好处:扩展性强,实现该接口的同时还可以继承其他的类
缺点:编程复杂,不能直接使用Thread类中的方法
继承Thread类
好处:编程简单,可以直接使用Thread类中的方法
缺点:可扩展性不强,不能继承其他的类
设置和获取线程名字
Test t1 = new Test();
// 设置线程名字
t1.setName("线程一");
// 获取线程名字
String name = t1.getName();
线程休眠
// 执行到此的线程进行休眠,参数为毫秒数
Thread.sleep(1000);
线程优先级
两种线程调度方式:
分时调度:所有线程轮流使用CPU使用权,平均分配给每个线程使用CPU的时间片
抢占式调度:优先让优先级高的线程使用CPU,如果现成的优先级相同就随机选择一个。优先级高的线程获取CPU的时间片相对多一些
设置线程优先级
Test t1 = new Test(); Test t2 = new Test(); //设置线程优先级,默认是5,范围是1-10 t1.setPriority(1); //返回此线程的优先级 int priority = t2.getPriority();
守护线程
存在多个线程并有守护线程的情况下,普通线程执行完后守护线程也会慢慢停止
Test t1 = new Test(); Test t2 = new Test(); // 设置为守护线程 t2.setDaemon(true);
线程安全问题
同步代码块
同步代码块实现原理是把多条共享数据的代码锁起来,任意时刻只能有一条线程执行
synchronized(任意对象){ 多条语句操作共享数据的代码 }
synchronized(任意对象)就相当于给代码加锁,任意对象可以看成是一把锁
同步的好处和弊端
好处:解决多线程数据安全问题
弊端:当线程数量很多会无形中降低程序运行效率,因为每条线程都会判断是同步上的锁
同步方法
同步方法就是把synchronized关键字加到方法上
修饰符 synchronized 返回值类型 方法名(方法参数) { 方法体; }
同步方法的锁对象是this
同步静态方法
同步静态方法就是把synchronized关键字加到静态方法上
修饰符 static synchronized 返回值类型 方法名(方法参数) { 方法体; }
同步静态方法的锁对象是class
Lock锁
Lock锁是JDK5后提供的,我们无法看到同步代码块和同步方法在哪里加了锁和在哪里释放了锁,所以Lock是为了更清晰的表达如何加锁和释放锁。
Lock是接口,采用实现类Reentrant Lock来实例化
ReentrantLock lock = new ReentrantLock();
加锁和解锁的方法
//获得锁、加锁 lock.lock //释放锁、解锁 lock.unlock
死锁
死锁是由于两个或多个线程互相持有对方所需要的资源,导致这些线程处于等待状态,无法继续执行。
产生死锁的情况:
1资源有限
2同步嵌套
线程等待
Test t1 = new Test(); Test t2 = new Test(); Test t3 = new Test(); // 调用此方法的线程会进入等待模式,直到其他线程调用notify或notifyAll方法 t1.wait(); // 随机唤醒一个处于等待状态的线程 t2.notify(); // 唤醒所有处于等待阶段的线程 t3.notifyAll();