目录
一:简单了解多线程
。是指从软件或者硬件上实现多个线程并发执行的技术。
。具有多线程能力的计算机因有硬件支持而能够在同一时间执行多个线程,提升性能。
并发和并行
并行:在同一时刻,有多个指令在多个CPU上同时执行。
并发:在同一时刻,有多个指令在单个CPU上交替执行。
进程和线程
进程:是操作系统中正在运行的一个应用程序
独立性:进程是一个能独立运行的基本单位,同时也是系统分配资源和调度的独立单位。
动态性:进程的实质是程序的一次执行过程,进程是动态产生,动态消亡的。
并发性:任何进程都可以同其他进程一起并发执行
线程:是进程中的单个顺序控制流,是一条执行路径
单线程:一个进程如果只有一条执行路径,则称为单线程程序
多线程:一个进程如果有多条执行路径,则称为多线程程序
二:多线程实现方式
1.继承Thread类的方式进行实现
定义一个类继承Thread类
在类中重写run()方法 创建类的对象
启动线程 => 最终是通过 Thread 类调用 start 方法启动
public class TestThread01 extends Thread {
//创建先方程一:继承Thread;重写run();调用start()类;
//注意:线程调动不一定立即执行,由cpu的调度执行;
@Override
public void run() {
//run的方法线程体
for (int i = 0; i < 20; i++) {
System.out.println("我在看代码:" + i);
}
}
public static void main(String[] args) {
final var testThread01 = new TestThread01();
testThread01.start();
2.实现Runnable接口方式实现
定义一个类实现Runnable接口
在类中重写run()方法 创建类的对象
创建Thread类的对象,把自定义类的对象作为构造方法的参数 启动线程
public class TestThread02 implements Runnable {
private String url;
private String name;
public TestThread02(String url, String name) {
this.name = name;
this.url = url;
}
@Override
public void run() {
final var webDownloader = new WebDownloader();
webDownloader.Download(url, name);
System.out.println("下载了文件名为:" + name);
}
public static void main(String[] args) {
final var testThread02 = new TestThread02("https://img2.woyaogexing.com/2022/11/07/81617bf06d5295a0!400x400.jpg ", "2.jpg");
testThread02.run();
}
}
3.实现Callable接口方式实现
定义一个类MyCallable实现Callable接口 在MyCallable类中重写call()方法
创建MyCallable类的对象 创建Future的实现类FutureTask对象,把MyCallable对象作为构造方法的参数
创建Thread类的对象,把FutureTask对象作为构造方法的参数
启动线程 再调用get方法,就可以获取线程结束之后的结果。
public class TestCallable implements Callable<Boolean> {
private String url;
private String name;
public TestCallable(String url, String name) {
this.name = name;
this.url = url;
}
public static void main(String[] args) {
TestCallable testCallable = new TestCallable("https://img2.woyaogexing.com/2022/11/07/81617bf06d5295a0!400x400.jpg ", "2.jpg");= new TestCallable("https://img2.woyaogexing.com/2022/11/07/81617bf06d5295a0!400x400.jpg ", "2.jpg");
testCallable.call();
}
@Override
public Boolean call() {
final var webDownloader = new WebDownloader();
webDownloader.Download(url, name);
System.out.println("下载了文件名为:" + name);
return true;
}
4.线程池
创建一个类,实现线程
重写方法 创建线程池,设置线程池数
创建类对象 向线程池提交任务(线程对象)
关闭线程池
public class TestPool {
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(10);
executorService.execute(new MyThread());
executorService.execute(new MyThread());
executorService.execute(new MyThread());
executorService.execute(new MyThread());
executorService.execute(new MyThread());
executorService.shutdown();
}
三:线程类的常见方法
获取线程的名字 String getName():
返回此线程的名称 Thread类中设置线程的名字 void setName(String name):
将此线程的名称更改为等于参数 name
通过构造方法也可以设置线程名称
获得当前线程的对象
public static Thread currentThread():返回对当前正在执行的线程对象的引用
线程休眠
public static void sleep(long time):让线程休眠指定的时间,单位为毫秒。
线程优先度
线程调度
多线程的并发运行:
计算机中的CPU,在任意时刻只能执行一条机器指令。
每个线程只有获得CPU的使用权才能执行代码。
各个线程轮流获得CPU的使用权,分别执行各自的任务。
线程有两种调度模型 分时调度模型:
所有线程轮流使用 CPU 的使用权,平均分配每个线程占用 CPU 的时间片 抢占式调度模型:
优先让优先级高的线程使用 CPU,如果线程的优先级相同,那么会随机选择一个,优先级高的线程
获取的 CPU 时间片相对多一些 代表的是几率,而不是一定先执行 Java使用的是抢占式调度模型
四:线程安全问题
1.锁多条语句操作共享数据,可以使用同步代码块实现
格式:
synchronized(任意对象) { 多条语句操作共享数据的代码 }
默认情况是打开的,只要有一个线程进去执行代码了,锁就会关闭 当线程执行完出来了,锁才会自动打开
同步的好处和弊端
好处:解决了多线程的数据安全问题 弊端:当线程很多时,因为每个线程都会去判断同步上的锁,这是很耗费资源的,无形中会降低程序的运行效率
private synchronized void buy() {
if (TicketNums <= 0) {
return;
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(Thread.currentThread().getName() + "我拿到了第" + TicketNums-- + "票");
}
}
2.Lock实现提供比使用synchronized方法和语句可以获得更广泛的锁定操作 Lock中提供了获得锁和释放锁的方法 void lock():
获得锁 void unlock():
释放锁 Lock是接口不能直接实例化,这里采用它的实现类ReentrantLock来实例化
ReentrantLock的构造方法 ReentrantLock():
创建一个ReentrantLock的实例
class TestLock1 implements Runnable {
private final ReentrantLock Lock = new ReentrantLock();
int ticketNums = 1000;
@Override
public void run() {
while (true) {
Lock.lock();
try {
if (ticketNums > 1) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(Thread.currentThread().getName() + "我拿到了第" + ticketNums-- + "票");
} else {
break;
}
} finally {
Lock.unlock();
}
}
}
}