多线程:从软件或硬件实现多个线程并发执行的技术。一个程序中,独立运行的程序片段称为线程。
两个例子理解多线程:
1、工厂和工厂线:工厂理解为进程,工厂线理解为线程,多个工厂线同时进行,从而提高效率;
2、百度网盘下载多个内容:百度网盘为进程,多个下载线路为线程。从而提高整体下载效率。
多线程两种写法:(手写)
public class Thread1 extends Thread {
public void run() {
// 内容1
}
}
Thread1 t1 = new Thread1();
public class Runnable1 implements Runnable {
public void run() {
// 内容2
}
}
Thread1 t1 = new Thread1(new Runnable1());
线程安全
什么时候会出现线程安全问题?(线程之间相互独立)
例子:售票员A、B售票;共1、2、3、4、5、6、7、8、9、10十张票;
售票员A在售第3张票,售票员B也在售第3张票;从而出现线程安全问题。
如何解决线程安全问题?
原理:同步机制(锁)让某些共享数据代码通过加锁来保证在同一个线程下执行,不被其他线程参与执行过程;
两种加锁方式:
1、同步代码块
synchronized(this)
2、同步方法 (修饰符前面加关键字synchronized)
public synchronized void saleOne(){}
public class SaleWindow1 implements Runnable{
private int id = 10; // 表示10张火车票,这是共享资源
// 卖10张火车票
public void run(){
for (int i = 0; i < 10; i++) {
synchronized (this){
if(id > 0){
System.out.println(Thread.currentThread().getName() + "卖了编号为"+id+"的火车票");
id--;
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
}
public class SaleWindow2 implements Runnable{
private int id = 10; // 表示10张火车票,这是共享资源
public synchronized void saleOne(){
if(id > 0){
System.out.println(Thread.currentThread().getName() + "卖了编号为"+id+"的火车票");
id--;
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
// 卖10张火车票
public void run(){
for (int i = 0; i < 10; i++) {
saleOne();
}
}
}
线程通信
比如我想1-10号火车票按照一定顺序卖票;而线程之间是相互独立的,那么通过什么来保证线程间通信?
等待唤醒机制
wait()方法和notify()方法
场景:筐子,最大放10个苹果(锁)、农夫(线程A)、小孩(线程B)
农夫放苹果,小孩从筐子里拿苹果;
筐子满10个苹果让农夫休息,wait(),某则小孩吃水果,唤醒notify()农夫放苹果;筐子里面苹果吃完让小孩休息;
public class Kuang {
// 这个集合就是水果筐,假设最多存10个水果
public static ArrayList<String> kuang = new ArrayList<String>();
}
public class Farmer extends Thread{
@Override
public void run() {
while (true){
//1、筐放满了就让农夫去休息
synchronized (Kuang.kuang){
if(Kuang.kuang.size()==10){
try {
Kuang.kuang.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 否则往筐里面放水果
Kuang.kuang.add("apple");
System.out.println("农夫放了一个水果,目前筐里面有"+Kuang.kuang.size()+"个水果");
// 唤醒小孩继续吃
Kuang.kuang.notify();
}
// 模拟控制速度
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class Child extends Thread{
public void run(){
while (true){
synchronized (Kuang.kuang){
// 筐里面没水果了就让小孩休息
if(Kuang.kuang.size()==0){
try {
Kuang.kuang.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 有水果就继续吃
Kuang.kuang.remove("apple");
System.out.println("小孩吃了一个水果,目前筐里面有"+Kuang.kuang.size()+"个水果");
// 唤醒农夫继续放水果
Kuang.kuang.notify();
}
// 控制速度
try {
Thread.sleep(400);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class TestFarmerChild {
public static void main(String[] args) {
new Farmer().start();
new Child().start();
}
}
实践
考评批量导出学生试卷采用多线程方式拼接试卷;
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
// 考生拼接试卷方法
}
});