image-20230627161401445

线程实现【重点】

  • 线程名字

    //主线程定义线程名字并启动
    new Thread(testThread5,"销售A").start();

    //线程内获取当前线程名字
    Thread.currentThread().getName()

Thread

image-20230627161641749

image-20230627162037653

image-20230627164616243

//创建线程的方式一:继承Thread类,重写run()方法,调用start开启线程
public class TestThread1 extends Thread{

@Override
public void run() {
//run方法线程体
for (int i = 0; i < 200; i++) {
System.out.println("我在看代码---" + i);
}
}

//main线程,主线程
public static void main(String[] args) {

//创建一个线程对象
TestThread1 testThread1=new TestThread1();

//调用 start() 方法开启线程:同时运行,交替执行
testThread1.start();

//调用 run() 方法先跑线程
//testThread1.run();

for (int i = 0; i < 200; i++) {
System.out.println("我在学习多线程--");
}
}

}

注意

  • 继承Thread类,重写run()方法,调用start开启线程
  • 线程开启不一定立即执行,由cpu调度执行
  • java单继承,继承Thread无法继承其他类
  • 练习 – 使用多线程同时下载多张图片案例
//实现多线程同步下载图片
public class TestThread2 extends Thread{

private String url;
private String name;

public TestThread2(String url,String name){
this.url=url;
this.name=name;
}

//下载图片线程的执行体
@Override
public void run() {
WebDownloader webDownloader=new WebDownloader();
webDownloader.downloader(url,name);
System.out.println("下载了文件名为:"+name);
}

public static void main(String[] args) {
TestThread2 t1 = new TestThread2("https://chen-131.png","1.png");
TestThread2 t2 = new TestThread2("https://chen-131.jpg","2.jpg");
TestThread2 t3 = new TestThread2("https://chen-131.png","3.png");

t1.start();
t2.start();
t3.start();
}
}


//下载器
class WebDownloader{
//下载方法
public void downloader(String url,String name){
try {
FileUtils.copyURLToFile(new URL(url),new File(name));
} catch (IOException e) {
e.printStackTrace();
System.out.println("IO异常,downloader方法出现问题");
}
}
}

下载 – 导包 – commons-io






Runnable【*】

image-20230627161656312

image-20230628005138151

  • 实现 Runnable 接口
  • 实现 run() 方法
  • 创建 Thread 对象 来开启 start() 线程
//创建线程方法2:实现runable接口,重写run方法,执行线程需要丢入runnable接口实现类,调用start方法
public class TestThread3 implements Runnable {
@Override
public void run() {
//run方法线程体
for (int i = 0; i < 200; i++) {
System.out.println("我在看代码---" + i);
}
}


public static void main(String[] args) {

//创建runnable接口的实现类对象
TestThread3 testThread3 = new TestThread3();

//创建线程对象,通过线程对象来开启我们的线程,代理
//Thread thread = new Thread(testThread3);
//thread.start();

new Thread(testThread3).start();

for (int i = 0; i < 1000; i++) {
System.out.println("我在学习多线程--" + i);
}
}
}
  • 练习 – 使用多线程同时下载多张图片
//和继承Thread类使用方法基本一致。只不过是启动线程需要改变

new Thread(t1).start();
new Thread(t2).start();
new Thread(t3).start();



  • 龟兔赛跑案例
//模拟龟兔赛跑
public class Race implements Runnable {

//胜利者
private static String winner;

@Override
public void run() {
for (int i = 1; i <= 100; i++) {

//模拟兔子休息
if (Thread.currentThread().getName().equals("兔子")&&i%10==0){
try {
Thread.sleep(2);
} 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;
}else {
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();
}
}




小结

image-20230628013956991






Callable

image-20230627161721072

image-20230628182953554

  • 线程创建方式三:实现callable接口

  • callable的好处

    • 1.可以定义返回值
    • 2.可以抛出异常
public class TestCallabel implements Callable {

private String url;
private String name;

public TestCallabel(String url,String name){
this.url=url;
this.name=name;
}

//下载图片线程的执行体
@Override
public Boolean call() {
WebDownloader webDownloader=new WebDownloader();
webDownloader.downloader(url,name);
System.out.println("下载了文件名为:"+name);
return true;
}

public static void main(String[] args) throws ExecutionException, InterruptedException {
TestCallabel t1 = new TestCallabel("https://chen-1.png","1.png");
TestCallabel t2 = new TestCallabel("https://chen-1.jpg","2.jpg");
TestCallabel t3 = new TestCallabel("https://chen-1.png","3.png");

//创建执行服务
ExecutorService service = Executors.newFixedThreadPool(3);

//提交执行
Future<Boolean> r1 = service.submit(t1);
Future<Boolean> r2 = service.submit(t2);
Future<Boolean> r3 = service.submit(t3);

//获取结果
boolean rs1 = r1.get();
boolean rs2 = r2.get();
boolean rs3 = r3.get();

System.out.println(rs1);
System.out.println(rs2);
System.out.println(rs3);

//关闭服务
service.shutdownNow();

}
}


//下载器
class WebDownloader{
public void downloader(String url,String name){
try {
FileUtils.copyURLToFile(new URL(url),new File(name));
} catch (IOException e) {
e.printStackTrace();
System.out.println("IO异常,downloader方法出现问题");
}
}
}



Lambda表达式

image-20230628214936098

  • 函数式接口
    • 只有一个抽象方法
  • 原写法
public class TestLambdal {
public static void main(String[] args) {
ILike iLike = new Like();
iLike.lambda();
}
}

//1.定义一个函数式接口
interface ILike{
void lambda();
}

//2.实现类
class Like implements ILike{
@Override
public void lambda() {
System.out.println("I Like Lambda");
}
}

  • 推导lambda写法
public class TestLambdal {
public static void main(String[] args) {
//用Lambda简化
ILike iLike = () ->{
System.out.println("I Like Lambda5");
};
iLike.lambda();
}
}

定义一个函数式接口
interface ILike{
void lambda();
}
  • 简化写法
public class TestLambda2 {

public static void main(String[] args) {
//lambda
CP cpe=null;

//原型
cpe=(int a)->{
System.out.println("lambda : cpe-->" + a);
};

//简化1.去除参数类型 ------------ 推荐 -----------------------------
cpe = (a)->{
System.out.println("lambda : cpe-->" + a);
};

//简化2.简化括号
cpe = a -> {
System.out.println("lambda : cpe-->" + a);
};

//简化3.去掉花括号
cpe = a-> System.out.println("lambda : cpe-->" + a);

}
}

interface CP{
void cpe(int a);
}

总结:

  • lambada表达式只能有一行代码的情况下才能简化成一行,如果有多行,那么就用代码块包裹

  • 前提是接口为函数式接口

  • 多个参数也可以去掉参数类型,要去掉就都去掉,必须加上括号(a,b),




线程状态

image-20230629002043736

image-20230629002216764

image-20230629002309248


观察线程状态

public class TestState {
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(() -> {
for (int i = 0; i < 5; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("//////");
});

//获取线程状态
Thread.State state = thread.getState();
System.out.println(state); //输出为:NEW


//观察启动
thread.start();//启动线程
state = thread.getState();
System.out.println(state);//输出为:RUN

while (state != Thread.State.TERMINATED) {
//只要线程不终止,就一直输出状态
Thread.sleep(100);
state = thread.getState();//更新线程状态
System.out.println(state);//输出状态
}
}
}


线程停止 –stop

1.建议线程正常停止 —–> 利用次数,不建议死循环

2.建议使用标志位 –> 设置一个标志位

3.不要使用 stop 或者 destroy 等过时或者JDK不建议使用的方法

image-20230629003650091

public class TestTop implements Runnable{

//1.设置一个标识位
private boolean flag= true;

@Override
public void run() {
int i=0;
while(flag){
System.out.println("run.....Thread "+i++);
}
}

//2.设置一个公开的方法停止线程,转换标志位
public void stop(){
this.flag=false;
}

public static void main(String[] args) {

TestTop testTop = new TestTop();
new Thread(testTop).start();

for (int i = 0; i < 1000; i++) {
System.out.println("main "+i);
if (i==900){
//调用stop方法切换标志位,让线程停止
testTop.stop();
System.out.println("该线程停止了");
}
}
}
}


线程休眠–sleep

image-20230629003935215

//主线程打印当前系统时间
Date startTime = new Date(System.currentTimeMillis());//获取系统当前时间
while (true){
try {
Thread.sleep(1000);
System.out.println(new SimpleDateFormat("HH:mm:ss").format(startTime));
startTime = new Date(System.currentTimeMillis());//更新当前时间
} catch (InterruptedException e) {
e.printStackTrace();
}
}

线程礼让–yield

image-20230629005539522

public class TestYield {

public static void main(String[] args) {
MyYield myYield = new MyYield();

new Thread(myYield,"a").start();
new Thread(myYield,"b").start();
}

}

class MyYield implements Runnable{

@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"线程开始执行");
Thread.yield();//礼让
System.out.println(Thread.currentThread().getName()+"线程停止执行");
}
}

合并线程–join

  • Join合并线程,待此线程执行完成后,再执行其他线程,其他线程阻塞
  • 插队
public class TestJoin implements Runnable{
@Override
public void run() {
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
for (int i = 0; i < 100; i++) {
System.out.println("线程vip来了"+i);
}
}

public static void main(String[] args) throws InterruptedException {
//启动我们的线程
TestJoin testJoin = new TestJoin();
Thread thread = new Thread(testJoin);
thread.start();

//主线程
for (int i = 0; i < 1000; i++) {
if (i==200){
thread.join();//插队
}
System.out.println("man "+i);
}
}
}

线程优先级–priority

image-20230629014945533

  • 先设置优先级,再启动
public class TestPriority {
public static void main(String[] args) {
//主线程优先级--5
System.out.println(Thread.currentThread().getName()+"-->"+Thread.currentThread().getPriority());

MyPrority myPrority = new MyPrority();

Thread t1 = new Thread(myPrority);
Thread t2 = new Thread(myPrority);
Thread t3 = new Thread(myPrority);
Thread t4 = new Thread(myPrority);
Thread t5 = new Thread(myPrority);
Thread t6 = new Thread(myPrority);

//先设置优先级,再启动
t1.start();

t2.setPriority(2);
t2.start();

t3.setPriority(6);
t3.start();

t4.setPriority(Thread.MAX_PRIORITY);
t4.start();

t5.setPriority(Thread.MIN_PRIORITY);
t5.start();

t6.setPriority(1);
t6.start();


}
}

class MyPrority implements Runnable{

@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"-->"+Thread.currentThread().getPriority());
}
}


守护线程–daemon

image-20230629133341524

  • 即 守护线程 会**陪伴 **被守护的线程 执行完毕

  • //true则设置为守护线程
    //默认是false表示是用户线程,正常的线程都是用户线程
     thread.setDaemon(true);
    



    ```java
    public class TestDaemon {
    public static void main(String[] args) {
    God god = new God();
    You you = new You();

    Thread thread = new Thread(god);
    thread.setDaemon(true);//默认是false表示是用户线程,正常的线程都是用户线程

    thread.start();//保护线程启动

    new Thread(you).start();//用户线程启动
    }
    }

    //守护线程
    class God implements Runnable{

    @Override
    public void run() {
    while (true){
    System.out.println("保护着你");
    }
    }
    }

    //被守护的线程
    class You implements Runnable{

    @Override
    public void run() {
    for (int i = 0; i < 36500; i++) {
    System.out.println("开心");
    }
    System.out.println("==================bye====================");
    }
    }





线程同步【重点】

并发

  • 多个线程操作同一个资源
  • 等待机制 – 对象等待池

安全集合CopyOnWriteArrayList

不需要锁,各线程排队进行

//测试JUC安全类型的集合
public class TestJUC {
public static void main(String[] args) throws InterruptedException {
CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList();
for (int i = 0; i < 1000; i++) {
new Thread(()->{
list.add(Thread.currentThread().getName());
}).start();
}
Thread.sleep(100);
System.out.println(list.size());
}
}

锁机制 – synchronized

image-20230629165131152

synchronized方法

锁 类本身 private synchronized void buy(){ }

public class UnsafeBuyTicket {
public static void main(String[] args) {
BuyTicket station = new BuyTicket();

new Thread(station,"A员工").start();
new Thread(station,"B员工").start();
new Thread(station,"C员工").start();

}
}

class BuyTicket implements Runnable{

private int ticketNums = 10;
boolean flag = true;

@Override
public void run() {
//买票
while (flag){
buy();
//模拟延时
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

//synchronized 同步方法,锁的是this
private synchronized void buy(){
//判断是否有票
if (ticketNums<=0){
flag=false;
return;
}
//买票
System.out.println(Thread.currentThread().getName()+"拿到"+ticketNums--);
}
}

synchronized块

image-20230629181507934

  • 可以锁任意对象 synchronized (account){ }
  • 锁的对象:变化的量,需要增删改的量
package com.synchronizedc;

//不安全的取钱
//两人取,账户
public class UnsafeBank {
public static void main(String[] args) {
//账户
Account account = new Account(1000,"结婚基金");

Drawing you = new Drawing(account,50,"你");
Drawing girlFirend = new Drawing(account,100,"girlFirend");
girlFirend.start();
you.start();
}
}

//账号
class Account{
int money;
String name;
public Account(int money, String name) {
this.money = money;
this.name = name;
}
}

//银行:模拟取款
class Drawing extends Thread{
Account account;//账号
//取多少钱
int drawingMoney;
//手里
int nowMoney;

public Drawing(Account account,int drawingMoney,String name){
super(name);
this.account=account;
this.drawingMoney=drawingMoney;
}

//取钱
//synchronized 默认锁this

@Override
public void run() {
synchronized (account){

//判断有没有钱
if (account.money-drawingMoney<0){
System.out.println(Thread.currentThread().getName()+"不够,取不了");
return;
}

try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}

//余额
account.money=account.money-drawingMoney;

//手里的钱
nowMoney=nowMoney+drawingMoney;

System.out.println(account.name+"余额为:"+account.money);
//Thread.currentThread().getName()==this.getName()
System.out.println(this.getName()+"手里的钱:"+nowMoney);
}

}
}

死锁

  • 多个线程互相抱着对方需要的资源,然后形成僵持

image-20230629213148270

  • 使用 synchronized块 上锁和解锁
//死锁:多个线程互相抱着对方需要的资源,然后形成僵持
public class DeadLock {
public static void main(String[] args) {
Makeup g1 = new Makeup(0,"灰姑凉");
Makeup g2 = new Makeup(1,"白雪公主");
g1.start();
g2.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() throws InterruptedException {
if (choice == 0) {
synchronized (lipstick) {//获得口红的锁
System.out.println(this.girlName + "获得口红的锁");
Thread.sleep(1000);
}

synchronized (mirror) {//一秒后获取镜子的锁
System.out.println(this.girlName + "获得镜子的锁");
}
}else {
synchronized (mirror) {//获得镜子的锁
System.out.println(this.girlName + "获得镜子的锁");
Thread.sleep(2000);
}

synchronized (lipstick) {//2秒后获取口红的锁
System.out.println(this.girlName + "获得口红的锁");
}
}
}
}

image-20230629215343031


Lock(锁)

  • ReentrantLock

image-20230629215546621

  • 定义:private final ReentrantLock lock = new ReentrantLock();
  • 在 try 中上锁,finally 中解锁
  • 显式锁,手动开启和关闭
  • image-20230629220630609
public class TestLock {
public static void main(String[] args) {
TestLock2 testLock2 = new TestLock2();

new Thread(testLock2).start();
new Thread(testLock2).start();
new Thread(testLock2).start();
}
}

class TestLock2 implements Runnable{
int ticketNums = 10;

//定义lock锁
private final ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
while(true){
try {
lock.lock();//加锁
if (ticketNums>0){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(ticketNums--);
}else {
break;
}
}finally {
lock.unlock();//解锁
}

}
}
}

总结:

image-20230629220820159






线程通信问题

生产者消费者模式

image-20230629235829001

image-20230629235942756

管程法

image-20230630000140407

//生产者消费者模型 -->  利用缓冲区解决:管程法

//生产者 , 消费者 , 产品 , 缓冲区
public class TestPC {
public static void main(String[] args) {
SyncContainer container=new SyncContainer();

new Productor(container).start();
new Consumer(container).start();
}
}

//生产者
class Productor extends Thread{
SyncContainer container;

public Productor(SyncContainer container){
this.container=container;
}

//生产
@Override
public void run() {
for (int i = 0; i < 100; i++) {

container.push(new Chicken(i));
System.out.println("生产了"+i+"只鸡");
}
}
}

//消费者
class Consumer extends Thread{
SyncContainer container;

public Consumer(SyncContainer container){
this.container=container;
}

//消费

@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("消费了-->"+container.pop().id+"只鸡");
}
}
}

//产品
class Chicken{
int id ;//产品编号

public Chicken(int id) {
this.id = id;
}
}

//缓冲区
class SyncContainer{

//需要一个容器大小
Chicken[] chickens = new Chicken[10];
//容器计数器
int count=0;

//生产者放入产品
public synchronized void push(Chicken chicken){
//如果容器满了,就需要等待消费者消费
if (count==chickens.length){
//通知消费者消费,生产等待
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}

//如果没有满,我们就需要丢入产品
chickens[count]=chicken;
count++;

//可以通知消费者消费了
this.notifyAll();
}

//消费者消费产品
public synchronized Chicken pop(){
//判断能否消费
if (count==0){
//等待生产者生产。消费者等待
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}

//如果可以消费
count--;
Chicken chicken = chickens[count];

//吃完了,通知生产者生产
this.notifyAll();
return chicken;
}

}

信号灯法

//生产消费者模式:信号灯法,标志位解决
public class TestPC2 {
public static void main(String[] args) {
TV tv = new TV();
new Player(tv).start();
new Watcher(tv).start();
}
}

//生产者-->演员
class Player extends Thread{
TV tv;
public Player(TV tv){
this.tv=tv;
}

@Override
public void run() {
for (int i = 0; i < 20; i++) {
if (i%2==0){
this.tv.play("快乐大本营播放中");
}else {
this.tv.play("抖音:记录美好生活");
}
}
}
}

//消费者-->观众
class Watcher extends Thread{
TV tv;
public Watcher(TV tv){
this.tv=tv;
}

@Override
public void run() {
for (int i = 0; i < 20; i++) {
tv.watch();
}
}
}

//产品-->节目
class TV{
//演员表演,观众等待
//观众观看,演员等待
String voice;//表演的节目
boolean flag = true;

//表演
public synchronized void play(String voice){
if (!flag){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("演员表演了:"+voice);
//通知观众观看
this.notifyAll();//通知唤醒
this.voice=voice;
this.flag=!this.flag;
}

//观看
public synchronized void watch(){
if (flag){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("观看了:"+voice);
//通知演员表演
this.notifyAll();
this.flag=!this.flag;
}
}






线程池

image-20230630013914386

image-20230630014454197

//线程池
public class TestPool {

public static void main(String[] args) {
//1.创建服务,创建线程池
//newFixedThreadPool 参数为:线程池的大小
ExecutorService service = Executors.newFixedThreadPool(10);

service.execute(new MyThread());
service.execute(new MyThread());
service.execute(new MyThread());
service.execute(new MyThread());
service.execute(new MyThread());

//2.关闭连接
service.shutdownNow();
}
}

class MyThread implements Runnable{

@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}