多线程
并发和并行
- 并行:在同一时刻,有多个指令在多个CPU上同时执行
- 并发:在同一时刻,有多个指令在单个CPU上交替执行
进程
- 进程指在内存中运行的应用程序
线程
- 线程是进程的执行单元,是CPU调度的最小单位
- 一个进程可以由多个线程组成,线程间共享进程的所有资源
- 一个进程如果有多个线程在执行,则称为多线程程序 :::
多线程实现
实现多线程的三种方式
- 继承Thread类的方式
- 实现Runnable接口的方式
- 使用线程池的方式。 :::
Thread类
创建步骤
实现Runnable接口
创建步骤
- 自定义类实现Runnable接口(任务类)
- 重写接口的run()方法
- 创建任务类的对象
- 创建Thread类的对象,把任务对象作为构造方法的参数
- 调用start方法,启动线程 :::
Thread类与Runnable接口对比
继承Thread类
- 优点
- 编程比较简单,子类中可以直接使用Thread类中的方法
- 缺点
- 扩展性较差,子类不能再继承其他的类
实现Runnable接口
- 优点
- 扩展性强,任务类实现接口后还可以继承其他的类
- 缺点
- 编程相对复杂,任务类不能直接使用Thread类中的方法 :::
线程类常用方法
:::tips
- String getName( ) 获取当前线程名称
- void setName(String name) 设置线程名称
- static Thread currentThread( ) 获取当前正在执行的线程对象
- static void sleep(long time) 让线程休眠指定的时间,单位为毫秒(休眠时让出CPU执行权)
- void setPriority(int newPriority) 设置线程优先级
-
线程优先级
线程优先级从低到高分别有1~10级,通常CPU会优先执行优先级较高的线程任务
- 但这也不是绝对的,因为线程执行还是有随机性,只是概率上来说优先级越高的线程越有机会先执行 :::
线程安全问题
概述
- 当多个线程访问共享数据,且多个线程对共享数据有更新操作时,就容易出现线程安全问题
同步代码块
- 格式
- synchronized(同步锁) {
- 有线程安全问题的代码
- }
- 原理
- 在多线程环境下,多个线程会抢占同步代码块中的锁对象。当一个线程获取锁,就可以成功进入同步代码块,其他线程获取不到锁,需要在同步代码块外面等待(阻塞)
- 获得锁的线程执行完同步代码块后就会释放锁,此时所有等待的线程会重新争夺锁对象(释放锁的线程也会再次参与争夺)抢到锁的线程就可以进入同步代码块执行
- 同步锁特点
- 任何对象都可作为同步锁使用
- 多个线程会对锁对象进行抢夺,所以需要保证多个线程操作的是同一把锁(锁对象需要唯一)
同步方法
- 概述
- 使用synchronized修饰的方法,叫做同步方法
- 格式
- 修饰符 synchronized 返回值类型 方法名(参数列表) {
- 方法体;
- }
- 作用
- 调用方法的线程需要先抢到锁才能执行方法中的代码
- 同步方法中的锁:
- 实例方法(无static修饰) : 同步锁对象就是this(当前方法调用者对象)
- 静态方法(static修饰) : 同步锁对象为当前类的Class对象(类名.class 或者 对象名.getClass() 可以获取类的字节码对象)
同步代码块和同步方法区别
概述
- JDK1.5开始,并发包(java.util.concurrent)中新增了Lock接口和相关实现类来实现锁的功能,它提供了与synchronized关键字类似的同步功能。
- Lock中提供了获得锁和释放锁的方法:
- void lock():获得锁
- void unlock():释放锁 ::: ```java / 自定义一个线程类继承Thread类 重写run()方法 创建自定义线程类的对象 调用start方法,启动线程。 / public static void main(String[] args) { MyThread myThread = new MyThread(); //开启线程 myThread.start(); for (int i = 0; i < 10; i++) { System.out.println(“main: “ + i); } }
public class MyThread extends Thread{ @Override public void run() { for (int i = 0; i < 10; i++) { System.out.println(“Thread: “ + i); } } }
```java
/*
目标:实现Runnable接口创建线程
步骤:
1.定义一个类(任务类)实现Runnable接口。
2.重写接口的run()方法。
3.创建任务类的对象。
4.创建Thread类的对象,把任务对象作为构造方法的参数。
5.调用start方法,启动线程。
*/
public class DemoRunnable {
public static void main(String[] args) {
//创建任务类对象
DemoMyRunnable runnable = new DemoMyRunnable();
//创建线程
Thread thread = new Thread(runnable);
//启动线程
thread.start();
for (int i = 0; i < 10; i++) {
System.out.println("main:" + i);
}
}
}
//任务类
public class DemoMyRunnable implements Runnable{
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("Runnable: " + i);
}
}
}
//获取当前正在执行的线程对象:Thread.currentThred()
public class DemoRunnable {
public static void main(String[] args) {
//任务类
MyRunnable myRun = new MyRunnable();
//创建线程
Thread thread = new Thread(myRun);
//设置名字
thread.setName("Runnable");
//启动线程
thread.start();
}
}
class MyRunnable implements Runnable {
@Override
public void run() {
//获取当前正在执行的线程对象
Thread thread = Thread.currentThread();
//获取当前线程名称
String name = thread.getName();
System.out.println(name + "线程正在执行");
}
}
public static void main(String[] args) {
//重写Runnabl接口匿名内部类
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("t1: " + i);
}
}
});
Thread t2 = new Thread(() -> {
for (int i = 0; i < 10; i++) {
System.out.println("t2: " + i);
}
});
//获取优先级
System.out.println("t1线程默认优先级" + t1.getPriority());
System.out.println("t2线程默认优先级" + t2.getPriority());
//设置优先级
t1.setPriority(1);
t2.setPriority(10);
//启动线程
t1.start();
t2.start();
}
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < 5; i++) {
Date date = new Date();
System.out.println("时间" + date);
Thread.sleep(1000);
}
}
public static void main(String[] args) {
//创建任务类
//Ticket02 ticket = new Ticket02();
Ticket03 ticket = new Ticket03();
//创建线程。并命名
Thread thread1 = new Thread(ticket, "window_1");
Thread thread2 = new Thread(ticket, "window_2");
Thread thread3 = new Thread(ticket, "window_3");
//启动线程
thread1.start();
thread2.start();
thread3.start();
}
//synchronized
public class Ticket02 implements Runnable{
//定义成员变量,表示总票数
private static int total = 100;
//实现卖票业务
@Override
public void run() {
while (true) {
//锁的对象可以是任意的java对象,前提是保证唯一
synchronized (Ticket02.class) {
if (total > 0) {
total--;
String name = Thread.currentThread().getName();
System.out.println("[" + name + "]卖票成功,当前剩余:" +
total + "张");
} else {
break;//没有票结束循环
}
}
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
//lock
public class Ticket03 implements Runnable {
private static int ticket = 10;
Lock lock = new ReentrantLock();
@Override
public void run() {
Thread thread = Thread.currentThread();
String name = thread.getName();
while (true) {
try {
//获得锁
lock.lock();
if (ticket > 0) {
System.out.println(name + "窗口售票成功,现在余:" + (--ticket));
} else {
break;
}
} finally {
//释放锁,每条进锁线程必须执行,不然程序不会停止
lock.unlock();
}
//线程休眠模拟消耗
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}