文章目录
0x02 多线程
线程创建的三种方式
建议使用 Runable 接口, OOP实际思想,java单继承,多接口
0x00 继承Thread类
- 继承 Thread
- 重写 run()方法
- 调用 start()启动线程
// 创建线程一 继承Thread
public class MyThread1 extends Thread {
@Override
public void run() {
// run() 方法线程体
for (int i = 0; i < 20; i++) {
System.out.println("run()+++" + i);
}
}
public static void main(String[] args) {
// main()主线程
// 创建一个线程对象,开启线程
MyThread1 myThread1 = new MyThread1();
myThread1.start();
for (int i = 0; i < 20; i++) {
System.out.println("main()++"+i);
}
}
}
0x01 实现 Runnable 接口
- 创建一个类 继承 Runnable
- 实现run方法
- 创建Thread对象,调用start函数
// 创建线程二 实现Runnable接口
public class MyThread2 implements Runnable {
@Override
public void run() {
// run() 方法线程体
for (int i = 0; i < 20; i++) {
System.out.println("run()+++" + i);
}
}
public static void main(String[] args) {
// main()主线程
// 创建一个线程对象,通过线程对象来开启线程,代理
MyThread2 myThread2 = new MyThread2();
new Thread(myThread2).start();
for (int i = 0; i < 20; i++) {
System.out.println("main()++"+i);
}
}
}
静态代理,Runable和Thread的关系
Runable -> MyRun(自己继承实现的)
Runable -> Thread(静态代理)
new Thread(new MyRun()).start()
Marry -> Me
Marry -> WeddingCompany
new WeddingCompany(new Me()).happyMarry()
// 静态代理总结
// 真实对象和静态代理都要实现用一个接口
// 代理对象代理真实对象:
// 真实对象传递给代理 -> 代理调用对象的方法
//好处:
// 代理对象可以帮助做很多事
// 真实对象专注做自己的事
public class StaticProxyDemo {
public static void main(String[] args) {
Me me = new Me();
WeddingCompany weddingCompany = new WeddingCompany(me);
weddingCompany.happyMarry();
/*
婚礼前
我本人要结婚了
婚礼后
*/
}
}
// 结婚接口
interface Marry{
void happyMarry();
}
// 我要结婚了,请代理,请婚庆 公司
class Me implements Marry{
@Override
public void happyMarry() {
// 实现接口的方法
System.out.println("我本人要结婚了");
}
}
// 婚庆公司,静态代理
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 after() {
System.out.println("婚礼后");
}
private void before() {
System.out.println("婚礼前");
}
}
龟兔赛跑
// 龟兔赛跑
public class TestRace {
public static void main(String[] args) {
Race race = new Race();
new Thread(race,"乌龟").start();
new Thread(race,"兔子").start();
}
}
class Race implements Runnable{
// 赢者名字,静态变量,只能赋值一次
private static String winner;
// 线程方法体
@Override
public void run() {
for (int i = 0; i <= 100; i++) {
// 让兔子睡觉
if (Thread.currentThread().getName().equals("兔子") && i%10==0){
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//判断游戏是否结束
if (isGameOver(i)){
break;
}
System.out.println(Thread.currentThread().getName()+"跑了第-->"+i+" 步数");
}
}
private boolean isGameOver(int steps){
// 已经存在了胜利者
if (winner!=null){
return true;
}
if (steps>=100){
winner = Thread.currentThread().getName();
System.out.println("Winner is "+winner);
return true;
}
return false;
}
}
0x02 实现Callable接口 (了解)
-
- 实现 Callable接口,需要返回值类型
-
- 重写 call方法,需要抛出异常
-
- 创建目标对象
-
- 创建执行服务
-
- 提交执行
-
- 获取结果
-
- 关闭服务
import java.util.concurrent.*;
// 创建线程三 实现 Callable 接口
public class MyThread4 implements Callable<Boolean> {
@Override
public Boolean call() throws Exception {
System.out.println(Thread.currentThread().getName());
return true;
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
MyThread4 t1 = new MyThread4();
MyThread4 t2 = new MyThread4();
MyThread4 t3 = new MyThread4();
// 创建执行服务 线程池为3
ExecutorService ser = Executors.newFixedThreadPool(3);
// 提交执行
Future<Boolean> result1 = ser.submit(t1);
Future<Boolean> result2 = ser.submit(t2);
Future<Boolean> result3 = ser.submit(t3);
// 获取结果
boolean rs1 = result1.get();
boolean rs2 = result2.get();
boolean rs3 = result3.get();
// 关闭服务
ser.shutdown();
}
}
线程的基本知识
0x00 Lambda表达式
- 前提条件
- 接口,其中只有一个方法(默认是抽象方法),那么它就是一个函数式接口
public class myLambdaDemo {
// 3. 静态内部类
static class Like3 implements ILike{
@Override
public void lambda() {
System.out.println("I like lambda3");
}
}
public static void main(String[] args) {
ILike like = new Like2();
like.lambda();
like = new Like3();
like.lambda();
// 4. 局部内部类
class Like4 implements ILike{
@Override
public void lambda() {
System.out.println("I like lambda4");
}
}
like = new Like4();
like.lambda();
// 5. 匿名内部类, 没有类的名称,必须借助接口或者父类
like = new ILike() {
@Override
public void lambda() {
System.out.println("I like lambda5");
}
};
like.lambda();
// 6. 用lambda简化
like = ()->{
System.out.println("I like lambda6");
};
like.lambda();
}
}
// 1. 定义一个函数式接口
interface ILike{
// 默认就是 abstract
void lambda();
}
// 2. 实现类
class Like2 implements ILike{
@Override
public void lambda() {
System.out.println("I like lambda2");
}
}
0x01 线程的五个状态
- new -> 创建
- start -> 就绪
- run -> 运行 (这个方法CPU自动调用)
- sleep, wait 等 -> 阻塞
| 方法 | 说明 |
| — | — |
| setPriority(int newPriority) | 更改线程的优先级 |
| static void sleep(long millis) | 指定的毫秒数内让当前正在执行的线程休眠 |
| void join() | 等待该线程终止(想象成这个线程要插队) |
| static void yield() | 暂停当前正在执行的线程对象,并执行其他线程(礼让) |
| void interrupt() | 中断这个线程 (不建议使用) |
| boolean isAlive(0 | 测试线程是否处于活动状态 |
stop()
// 测试stop
//1. 不建议线程正常停止--->利用次数,不建议死循环
//2. 不要使用stop或者destroy等过时或者JDK不建议使用的方法
//3. 不要使用Stop或者破坏等过时或者jdk不建议使用的方法
public class myStopDemo implements Runnable{
// 1.设置一个标识位
private boolean flag = true;
@Override
public void run() {
int i = 0;
while (flag){
System.out.println("run-> "+i++);
}
}
// 2.设置一个公开的方法停止线程,转换标志位
public void stop(){
this.flag = false;
}
public static void main(String[] args) {
myStopDemo myStopDemo = new myStopDemo();
new Thread(myStopDemo).start();
for (int i = 0; i < 1000; i++) {
System.out.println("main-> "+i);
if (i==900){
// 调用stop
myStopDemo.stop();
System.out.println("thread stop");
}
}
}
}
sleep()
- 每一个对象都有一个锁,sleep不会释放锁
import java.text.SimpleDateFormat;
import java.util.Date;
public class mySleepDemo {
public static void main(String[] args) {
Date date = new Date(System.currentTimeMillis()); // 获取当前的时间
while (true){
try {
Thread.sleep(1000);
System.out.println(new SimpleDateFormat("HH:mm:ss").format(date));
date = new Date(System.currentTimeMillis()); // 获取当前的时间
} catch (InterruptedException e) {
e.printStackTrace();
}
}
/*
17:12:12
17:12:13
17:12:14
17:12:15
*/
}
}
yield()
- 礼让线程,让当前正在执行的线程暂停,但不阻塞
- 将线程从 运行状态 转为 就绪状态
- 让cpu重新调度,礼让不一定成功! 看CPU心情,
// 测试礼让线程
public class myYieldDemo implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"-> run");
Thread.yield();
System.out.println(Thread.currentThread().getName()+"-> stop");
}
public static void main(String[] args) {
myYieldDemo myYieldDemo = new myYieldDemo();
new Thread(myYieldDemo,"a").start();
new Thread(myYieldDemo,"b").start();
/*
b-> run
a-> run
b-> stop
a-> stop
*/
}
}
join()
- join合并线程,待此线程执行完成后,再执行其他线程,其他线程在此期间是阻塞
- 可以想象成插队
// 测试join
public class myJoinDemo implements Runnable{
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("run() " + i);
}
}
public static void main(String[] args) throws InterruptedException {
myJoinDemo myJoinDemo = new myJoinDemo();
Thread thread = new Thread(myJoinDemo);
for (int i = 0; i < 10; i++) {
if (i==5){
thread.start();
thread.join();
}
System.out.println("main-> " + i);
}
}
}
state()
public class myStateDemo {
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(()->{
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
// 观测状态
System.out.println(thread.getState()); //NEW
thread.start();
System.out.println(thread.getState());//Run
// 只要不终止,就一直输出
while (thread.getState() != Thread.State.TERMINATED){
Thread.sleep(100);
System.out.println(thread.getState());
}
/*
NEW
RUNNABLE
TIMED_WAITING
TERMINATED
*/
// 死亡之后的线程,不能再次start
// thread.start();
}
}
0x02 线程的优先级
getPriority()
setPriority(int priority)
public class myPriorityDemo {
public static void main(String[] args) {
Thread thread = new Thread();
System.out.println(thread.getPriority()); //5
thread.setPriority(8);
System.out.println(thread.getPriority()); //8
thread.setPriority(Thread.MAX_PRIORITY);
System.out.println(thread.getPriority()); //10
}
}
0x03 守护线程 daemon
- 线程分为 用户线程 和 守护线程
- 虚拟机必须确保用户线程执行完毕
- 虚拟机不用等待守护线程执行完毕
- 如,后台记录操作日志,监控内存,垃圾回收等待.
// 守护线程一直运行,不会主动结束
// 用户线程结束了,虚拟机就开始结束,虚拟机结束了,守护线程结束
// 虚拟机结束需要时间,所以用户线程结束了,守护线程还会运行一会儿
public class DaemonDemo {
public static void main(String[] args) {
God god = new God();
You you = new You();
Thread threadGod = new Thread(god);
Thread threadYou = new Thread(you);
// 让上帝变成守护线程
threadGod.setDaemon(true);
threadGod.start();
// 用户线程启动
threadYou.start();
}
}
// 上帝
class God implements Runnable{
@Override
public void run() {
while (true){
System.out.println("God run");
}
}
}
// 你
class You implements Runnable{
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("You run");
}
System.out.println("You goodbye!");
}
}
0x04 线程同步,并发 synchronized
- 实现并发: 队列 + 锁
冲突
public class MyThread3 implements Runnable{
private int ticketNum = 10;
@Override
public void run() {
while (true){
if (ticketNum<=0){break;}
// 模拟延时
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"拿到了第 "+ticketNum--+" 张票");
}
}
public static void main(String[] args) {
MyThread3 myThread3 = new MyThread3();
new Thread(myThread3,"小白").start();
new Thread(myThread3,"小红").start();
new Thread(myThread3,"小黄").start();
/*
多个线程操作了同一个资源
小红拿到了第 4 张票
小白拿到了第 4 张票
小黄拿到了第 4 张票
*/
}
}
同步锁
- 同步方法, 默认是锁this对象,不能锁对象的对象,
- 同步块,synchronized (obj){} obj是要锁的对象,
public class SafeBuyTicket {
public static void main(String[] args) {
BuyTicket2 buyTicket = new BuyTicket2();
new Thread(buyTicket,"小白").start();
new Thread(buyTicket,"小红").start();
new Thread(buyTicket,"小黄").start();
}
}
class BuyTicket2 implements Runnable{
// 票
private int ticketNum = 10;
// 是否卖完了
public boolean flag = true;
@Override
public void run() {
// 买票
while (flag){
try {
buy();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
// 加上 synchronized 同步方法, 锁的是对象本身,this
private synchronized void buy() throws InterruptedException {
// 判断是否有票
if (ticketNum<=0){
flag = false;
return;
}
// 模拟延时
Thread.sleep(100);
// 买票
System.out.println(Thread.currentThread().getName()+"拿到了第 "+ticketNum--+" 张票");
}
}
class A{}
class B{
A a = null;
// 同步块,所对应的对象,不然的话,就锁成B了
public void run(){
synchronized (a){ 操作 }
}
}
0x05 死锁
产生死锁的必要条件
- 互斥条件: 一个资源每次只能被一个进程使用。
- 请求与保持条件: 一个进程因请求资源而阻塞时,对已获得的资源保持不放。
- 不剥夺条件: 进程已获得的资源,在末使用完之前,不能强行剥夺。
- 循环等待条件: 若干进程之间形成一种头尾相接的循环等待资源关系。
上面列出了死锁的四个必要条件,我们只要想办法破其中的任意一个或多个条件就可以避免死锁发生
// 死锁
public class DeadLockDemo {
public static void main(String[] args) {
new Makeup(0,"小红").start();
new Makeup(1,"小白").start();
}
}
// 口红
class Lipstick{}
// 镜子
class Mirror{}
// 化妆
class Makeup extends Thread{
// 需要的资源只有一份, 用static来保证
static Lipstick lipstick = new Lipstick();
static Mirror mirror = new Mirror();
int choice; //选择
String girlName; //女孩名字
Makeup(int choice, String girlName){
this.choice = choice;
this.girlName = girlName;
}
@Override
public void run() {
// 化妆
try {
makeup();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 化妆方法
private void makeup(){}
}
//会死锁,自己有一个锁,还想要别人的锁,要不到,自己也不释放
private void makeup() throws InterruptedException {
if (choice==0){
synchronized (lipstick){ //获得口红的锁
System.out.println(this.girlName+" 获得口红的锁");
Thread.sleep(1000);
synchronized (mirror){ //一秒钟后想获得镜子的锁
System.out.println(this.girlName+" 获得镜子的锁");
}
}
}
if (choice==1){
synchronized (mirror){ //获得镜子的锁
System.out.println(this.girlName+" 获得镜子的锁");
Thread.sleep(1000);
synchronized (lipstick){ //一秒钟后想获得口红的锁
System.out.println(this.girlName+" 获得口红的锁");
}
}
}
}
// 不会死锁,自己 有一个锁,还会要别人的锁,如果要不到,就释放自己的锁
private void makeup() throws InterruptedException {
if (choice==0){
synchronized (lipstick){ //获得口红的锁
System.out.println(this.girlName+" 获得口红的锁");
Thread.sleep(1000);
}
synchronized (mirror){ //一秒钟后想获得镜子的锁
System.out.println(this.girlName+" 获得镜子的锁");
}
}
if (choice==1){
synchronized (mirror){ //获得镜子的锁
System.out.println(this.girlName+" 获得镜子的锁");
Thread.sleep(1000);
}
synchronized (lipstick){ //一秒钟后想获得口红的锁
System.out.println(this.girlName+" 获得口红的锁");
}
}
}
0x06 Lock 锁
java.util.concurrent.locks.Lock
是一个接口
ReentrantLock
类实现了Lock
是一个可重入锁
与synchronized类似(隐式锁),Lock(显示锁,显示的开启,关闭)
**使用顺序 ** Lock > synchronized代码块锁 > synchronized方法锁
import java.util.concurrent.locks.ReentrantLock;
public class TestLock {
public static void main(String[] args) {
BuyTicket buyTicket = new BuyTicket();
new Thread(buyTicket,"小白").start();
new Thread(buyTicket,"小红").start();
new Thread(buyTicket,"小黄").start();
}
}
class BuyTicket implements Runnable{
//票
private int ticketNum = 10;
//是否卖完了
public boolean flag = true;
// 定义lock锁
private final ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
//买票
while (flag){
buy();
}
}
private void buy(){
try{
// 加锁
lock.lock();
// 判断是否有票
if (ticketNum<=0){
flag = false;
return;
}
// 模拟延时
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 买票
System.out.println(Thread.currentThread().getName()+"拿到了第 "+ticketNum--+" 张票");
}finally {
// 解锁
lock.unlock();
}
}
}
0x07 线程通信
方法名 | 作用 |
---|---|
wait() | 表示线程一直等待,会释放锁,sleep()不会释放锁 |
wait(long timeout) | 指定等待的毫秒数 |
notify() | 唤醒一个处于等待状态的线程 |
notifyAll() | 唤醒同一个对象上所有调用wait()方法的线程,优先级别高的线程优先调 |
注意:都是Object类的方法,都只能在同步方法或者同步代码块中使用,否则会抛出异常
管程法-缓冲区
设计思想
- 生产者是一个线程,所以继承Thread类,重写run方法,用于生产
- 消费者是一个线程,所以继承Thread类,重写run方法,用于消费
- 产品时一个类,用于填入缓冲区
- 缓冲区是一个类,是生产者和消费者公用的,所以它两的构造函数要填入同一个缓冲区
- 用到了synchronized 锁的是对象本身,也就是缓冲区
// 测试生产者,消费者模型 -> 缓冲区:管程法
public class BufferDemo {
public static void main(String[] args) {
SynBuffer synBuffer = new SynBuffer();
Producer producer = new Producer(synBuffer);
Customer customer = new Customer(synBuffer);
customer.start();
producer.start();
}
}
// 生产者
class Producer extends Thread{
SynBuffer synBuffer;
Producer(SynBuffer synBuffer){
this.synBuffer = synBuffer;
}
// 生产
@Override
public void run() {
for (int i = 1; i < 30; i++) {
synBuffer.push(new Chicken(i));
}
}
}
// 消费者
class Customer extends Thread{
SynBuffer synBuffer;
Customer(SynBuffer synBuffer){
this.synBuffer = synBuffer;
}
// 消费
@Override
public void run() {
for (int i = 1; i < 30; i++) {
synBuffer.pop();
}
}
}
// 产品
class Chicken {
int id; //产品编号
public Chicken(int id) {
this.id = id;
}
}
// 缓冲区
class SynBuffer{
// 容器大小
Chicken[] chickens = new Chicken[10];
// 当前可以被消费的鸡的数量
int count = 0;
// 生产者放入产品
public synchronized void push(Chicken chicken){
// 是否满?
if (count == chickens.length){
// 等消费者消费
System.out.println("鸡满了,等消费");
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 如果没有满
chickens[count] = chicken;
count++;
System.out.println("生产了第 "+count+" 只鸡");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 等消费者消费
this.notifyAll();
}
// 消费者消费产品
public synchronized void pop(){
// 是否有产品?
if (count == 0){
// 等待生产者生产产品
System.out.println("没鸡了,等生产");
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("消费了第 "+count+" 只鸡");
count--;
Chicken chicken = chickens[count];
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 吃完了,等待生产者生产
this.notifyAll();
}
}
信号灯法-标志位
和上面的缓冲区类似,只是上面判断是否为空,是否满,这里是用标志位表示满或者空
// 测试生产者,消费者模型 -> 信号灯法:标志位解决
public class FlagDemo {
public static void main(String[] args) {
SynFlag synFlag = new SynFlag();
new Customer2(synFlag).start();
new Producer2(synFlag).start();
}
}
//生产者
class Producer2 extends Thread{
SynFlag synFlag;
public Producer2(SynFlag synFlag) {
this.synFlag = synFlag;
}
@Override
public void run() {
for (int i = 0; i < 5; i++) {
synFlag.push();
}
}
}
// 消费者
class Customer2 extends Thread{
SynFlag synFlag;
public Customer2(SynFlag synFlag) {
this.synFlag = synFlag;
}
@Override
public void run() {
for (int i = 0; i < 5; i++) {
synFlag.pop();
}
}
}
// 产品
class Product{}
// 标识位
class SynFlag{
private boolean flag = false; // 初值false代表空
// 生产
public synchronized void push(){
if (flag){
// 如果不为空,就让消费
System.out.println("已有,等待消费");
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
flag = !flag;
System.out.println("已生产");
// 通知消费
this.notifyAll();
}
// 消费
public synchronized void pop(){
if (!flag){
// 如果为空,就让生产
System.out.println("为空,等待生产");
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
flag = !flag;
System.out.println("已消费");
// 通知生产
this.notifyAll();
}
}
0x08 线程池
思路:提前创造好多个线程,放入线程池中。使用时获取,使用完放回,可以避免频繁的创建销毁,实现重复利用。便于线程管理
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
// 测试线程池
public class TestPool {
public static void main(String[] args) {
//1.创建服务,创建线程池
ExecutorService service = Executors.newFixedThreadPool(3);
//执行
service.execute(new MyThread());
service.execute(new MyThread());
service.execute(new MyThread());
//2.关闭链接
service.shutdown();
}
}
class MyThread extends Thread{
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}