1 三种创建方式
- 继承
Thread
类 - 实现
Runnable
接口 - 实现
Callable
接口
2 继承Thread
类
public class MyThread extends Thread {
// 线程入口点
@Override
public void run() {
// 线程体
for (int i = 0; i < 20; i++) {
System.out.println(i);
}
}
public static void main(String[] args) {
// 创建线程对象
MyThread myThread = new MyThread();
// 启动线程
myThread.start();
}
}
- 自定义线程类继承
Thread
类 - 重写
run()
方法,编写线程执行体 - 创建线程对象,调用
start()
方法启动线程 - 调用
start()
方法后线程不一定立即执行,需要CPU安排调度 - 不建议使用此方法,会由于单继承产生局限性
3 实现Runnable
接口
public class MyThread implements Runnable {
@Override
public void run() {
// 线程体
for (int i = 0; i < 20; i++) {
System.out.println(i);
}
}
public static void main(String[] args) {
// 创建实现类对象
MyThread myThread = new MyThread();
// 创建代理类对象
Thread thread = new Thread(myThread);
// 启动
thread.start();
}
}
- 自定义线程类实现
Runnable
接口 - 重写
run()
方法,编写线程执行体 - 创建实现类对象,并将其作为
Thread
类的参数创建线程对象 - 调用线程对象的
start()
方法启动线程 - 推荐使用实现
Runnable
接口的方法创建线程,这样可以避免Java单继承的局限性,方便同一个对象被多个线程使用
4 初识并发
模拟网站抢火车票场景,此时多个线程同时操作同一个对象:
public class MyThread implements Runnable {
// 共有10张票
private int ticketNums = 10;
@Override
public void run() {
while (true) {
if (ticketNums <= 0) {
break;
}
try {
// 延时100ms
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
// Thread.currentThread()返回当前线程对象
// getName()方法返回线程对象名称
System.out.println(Thread.currentThread().getName() + "抢到了第" + ticketNums-- + "张票");
}
}
public static void main(String[] args) {
MyThread myThread = new MyThread();
// 三个用户一同抢票
// Thread类可以在new时为线程赋予名称
new Thread(myThread, "黄牛").start();
new Thread(myThread, "小张").start();
new Thread(myThread, "大黄").start();
}
}
程序运行结果如下:
黄牛抢到了第10张票
大黄抢到了第9张票
小张抢到了第8张票
黄牛抢到了第7张票
小张抢到了第6张票
大黄抢到了第5张票
大黄抢到了第3张票
小张抢到了第2张票
黄牛抢到了第4张票
小张抢到了第1张票
大黄抢到了第1张票
此时发现问题,在多线程下操作同一个资源,会产生数据紊乱的线程不安全问题,例如上述代码中实际运行时有两个用户获取了同一张票
5 案例:龟兔赛跑
龟兔赛跑需求如下:
- 首先需要赛道距离,然后需要离终点越来越近
- 判断比赛是否结束
- 打印出胜利者
- 龟兔赛跑开始
- 兔子在途中会休息,需要模拟兔子休息
- 最终乌龟赢得比赛
public class Race implements Runnable {
// 记录胜者
private static String winner;
@Override
public void run() {
for (int i = 1; i <= 100; i++) {
// 模拟兔子睡觉,每跑10步休息一次
if ("兔子".equals(Thread.currentThread().getName()) && i % 10 == 0) {
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 判断比赛是否结束
boolean flag = gameOver(i);
// 如果比赛结束,则结束线程
if (flag) {
break;
}
System.out.println(Thread.currentThread().getName() + "跑了" + i + "步");
}
}
// 判断是否比赛结束
private boolean gameOver(int steps) {
// 判断是否存在胜者
if (winner != null) {
return true;
}
// 跑完全程则比赛结束,并为winner赋予当前线程名称
if (steps >= 100) {
winner = Thread.currentThread().getName();
System.out.println("winner is " + winner);
return true;
}
return false;
}
public static void main(String[] args) {
Race race = new Race();
new Thread(race, "乌龟").start();
new Thread(race, "兔子").start();
}
}
在使用
Object
类的equals()
方法判断是否相等时,如果比较双方一方是变量,一方是常量,最好将常量放在equal()
方法的左侧,防止变量值为null产生空指针异常
6 实现Callable
接口
public class TestCallable implements Callable<String> {
// 需要重写call()方法,该方法有返回值,且需要抛出异常
@Override
public String call() throws Exception {
return Thread.currentThread().getName();
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
TestCallable t1 = new TestCallable();
TestCallable t2 = new TestCallable();
TestCallable t3 = new TestCallable();
// 创建执行服务
// 由线程池创建,数量为3
ExecutorService service = Executors.newFixedThreadPool(3);
// 提交执行
Future<String> future1 = service.submit(t1);
Future<String> future2 = service.submit(t2);
Future<String> future3 = service.submit(t3);
// 获取结果
String result1 = future1.get();
String result2 = future2.get();
String result3 = future3.get();
System.out.println(result1);
System.out.println(result2);
System.out.println(result3);
// 关闭服务
service.shutdown();
}
}
- 实现
Callable
接口,需要返回值类型 - 重写
call()
方法,需要抛出异常 - 创建目标对象
- 创建执行服务:
ExecutorService service = Executors.newFixedThreadPool(3);
- 提交执行:
Future<String> future1 = service.submit(t1);
- 获取结果:
String result1 = future1.get();
- 关闭服务:
service.shutdown();
7 静态代理
public class StaticProxy {
public static void main(String[] args) {
You you = new You("李华");
WeddingCompany weddingCompany = new WeddingCompany(you);
weddingCompany.happyMarry();
}
}
interface Marry {
void happyMarry();
}
// 真实角色
class You implements Marry {
private String name;
public You(String name) {
this.name = name;
}
@Override
public void happyMarry() {
System.out.println(name + "马上要结婚了");
}
}
// 代理角色
class WeddingCompany implements Marry {
// 真实目标角色
private Marry target;
public WeddingCompany(Marry target) {
this.target = target;
}
@Override
public void happyMarry() {
before();
this.target.happyMarry();
after();
}
private void before() {
System.out.println("结婚之前,布置会场");
}
private void after() {
System.out.println("结婚之后,收拾会场");
}
}
-
代理模式的核心思想:代理人跟被代理人都能做同一件事,只不过被代理人把执行权限交给了代理人,外部只能通过代理人来联系被代理人,通过代理人来增强被代理人的一些功能。
-
真实对象(被代理对象)和代理对象都要实现同一个接口
-
在
Thread
类的使用中也使用到了静态代理public static void main(String[] args) { // Thread类也实现了Runnable接口 Thread thread = new Thread(new Runnable() { @Override public void run() { } }); }
8 Lambda表达式
-
Lambda表达式用于简化程序,避免匿名内部类定义过多
-
其本质属于函数式编程
(params) -> expression[表达式] (params) -> statement[语句] (params) -> {statements}
-
函数式接口:一个接口如果只包含唯一一个抽象方法,那么它就是一个函数式接口。对于函数式接口,可以通过lambda表达式来创建该接口的对象
public interface Runnable { public abstract void run(); }
-
在Java8后,可以在接口前在
@FunctionalInterface
注解表示改接口为一个函数式接口,此时接口中只能有一个抽象方法@FunctionalInterface public interface MyInterface { void func1(); // void func2(); 不能有多余的抽象方法 }
-
lambda表达式用于简化匿名内部类
public class TestLambda { // 静态内部类 static class Like2 implements ILike { @Override public void lambda() { System.out.println("I like lambda2"); } } public static void main(String[] args) { ILike like = new Like1(); like.lambda(); like = new Like2(); like.lambda(); // 局部内部类 class Like3 implements ILike { @Override public void lambda() { System.out.println("I like lambda3"); } } like = new Like3(); like.lambda(); // 匿名内部类,没有类的名称,必须借助接口或父类 like = new ILike() { @Override public void lambda() { System.out.println("I like lambda4"); } }; like.lambda(); // lambda表达式 like = () -> System.out.println("I like lambda5"); like.lambda(); } } // 函数式接口 @FunctionalInterface interface ILike { void lambda(); } // 实现类 class Like1 implements ILike{ @Override public void lambda() { System.out.println("I like lambda1"); } }
-
函数式接口中的抽象方法中存在一个参数时
public class TestLambda { public static void main(String[] args) { ILove love = null; love = (String name) -> { System.out.println("I love " + name); }; // 简化1:可以省略参数类型 love = (name) -> { System.out.println("I love " + name); }; // 简化2:可以省略括号 love = name -> { System.out.println("I love " + name); }; // 简化3:可以省略大括号 // 省略大括号只能在lambda表达式只有一行代码的情况下使用 love = name -> System.out.println("I love " + name); love.love("kayne"); } } // 函数式接口 @FunctionalInterface interface ILove { void love(String name); }
-
函数式接口中的抽象方法中存在多个参数时
public class TestLambda { public static void main(String[] args) { ILove love = null; love = (String name1, String name2) -> { System.out.println("I love " + name1 + " and " + "name2"); }; // 多个参数时也可以去掉参数类型,但要去掉就全部去掉 // 多参数时不能省略圆括号 love = (name1, name2) -> System.out.println("I love " + name1 + " and " + "name2"); love.love("kayne"); } } // 函数式接口 @FunctionalInterface interface ILove { void love(String name1, String name2); }