Java基础(四)

前言:本博客主要涉及java编程中的线程、多线程、生成者消费者模型、死锁。

目录

线程多线程

线程同步

synchronized

Lock锁

线程通信

生产者消费者模型

线程池

使用线程池处理Runnable任务

使用线程池处理Callable任务

Excutors

悲观锁

乐观锁

并发VS并行

线程的生命周期


线程多线程

创建一个简单的线程

获取执行当前代码的线程名:Thread.currentThread().getName()

开启线程:对象名.start()

守护线程 优先级

java 复制代码
//1.创建一个继承于Thread类的子类
class MyThread extends Thread {
    //2.重写Thread类的run()
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
                System.out.println(Thread.currentThread().getName() + ":" + i);
        }
    }
}

public class ThreadTest {
    public static void main(String[] args) {
        MyThread t1 = new MyThread();
        t1.start();
    }
}
java 复制代码
package learn12;

class MyRunnable implements Runnable {
    @Override
    public void run() {
        for (int i = 1; i <= 5; i++) {
            System.out.println("子线程输出:" + i);
        }
    }
}

public class MyThreadTest2 {
    public static void main(String[] args) {
        Runnable target = new MyRunnable();
        new Thread(target).start();
        for (int i = 1; i <= 5; i++) {
            System.out.println("主线程main输出:" + i);
        }

    }

}

线程安全

例子:多个人同时去一个账户里取钱,被取出后,不能再取出钱。但多个线程执行时,在访问时,账户里有钱的,但实际上已经被其他用户取走,此时再取钱就会引发数据不安全的问题。

使用多线程时,每个线程都对数据进行修改,如何来保证数据的安全性?

java 复制代码
package learn12;

public class Account {
    private String cardId;
    private double money;

    public Account() {
    }

    public Account(String cardId, double money) {
        this.cardId = cardId;
        this.money = money;
    }

    public String getCardId() {
        return cardId;
    }

    public void setCardId(String cardId) {
        this.cardId = cardId;
    }

    public double getMoney() {
        return money;
    }

    public void setMoney(double money) {
        this.money = money;
    }

    public void drawMoney(double money) {
        String name = Thread.currentThread().getName();
            if (this.money >= money) {
                System.out.println(name + "来取钱" + money + "成功!");
                this.money -= money;
                System.out.println(name + "来取钱,此时剩余:" + this.money);
            } else {
                System.out.println(name + "来取钱,但余额不足");
            }
    }
}
java 复制代码
package learn12;

public class DrawThread extends Thread {
    private Account acc;

    public DrawThread(Account acc, String name) {
        super(name);
        this.acc = acc;
    }

    @Override
    public void run() {
        acc.drawMoney(100000);
    }
}
java 复制代码
package learn12;

public class ThreadTest {
    public static void main(String[] args) {
        Account acc = new Account("ICBC-100", 100000);
        new DrawThread(acc, "小明").start();
        new DrawThread(acc, "小红").start();
        new DrawThread(acc, "小李").start();
    }
}

线程同步

使用了加锁的机制

synchronized

用到了同步控制关键字synchronized

用其修饰成员方法,被修饰的方法,在同一时间,只能被一个线程执行

java 复制代码
//同步方法
public synchronized void drawMoney(double money) {
        String name = Thread.currentThread().getName();
//同步代码块
        synchronized (this) {
            if (this.money >= money) {
                System.out.println(name + "来取钱" + money + "成功!");
                this.money -= money;
                System.out.println(name + "来取钱,此时剩余:" + this.money);
            } else {
                System.out.println(name + "来取钱,但余额不足");
            }
        }
    }

Lock锁

注意:解锁要放在finally里,以便程序出错时,可以解锁。

java 复制代码
package learn12;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class Account {
    private String cardId;
    private double money;
    //创建Lock锁对象
    private final Lock lk = new ReentrantLock();

    public Account() {
    }

    public Account(String cardId, double money) {
        this.cardId = cardId;
        this.money = money;
    }

    public String getCardId() {
        return cardId;
    }

    public void setCardId(String cardId) {
        this.cardId = cardId;
    }

    public double getMoney() {
        return money;
    }

    public void setMoney(double money) {
        this.money = money;
    }

    public void drawMoney(double money) {
        String name = Thread.currentThread().getName();
        //加锁
        lk.lock();
        try {
            if (this.money >= money) {
                System.out.println(name + "来取钱" + money + "成功!");
                this.money -= money;
                System.out.println(name + "来取钱,此时剩余:" + this.money);
            } else {
                System.out.println(name + "来取钱,但余额不足");
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //解锁
            lk.unlock();
        }

    }
}

线程通信

生产者线程负责生产数据

消费者线程负责消费生产者产生的数据

生产者消费者模型

java 复制代码
package learn12;

import java.util.ArrayList;
import java.util.List;

public class Desk {
    private List<String> list = new ArrayList<>();

    public synchronized void put() {
        try {
            String name = Thread.currentThread().getName();
            if (list.size() == 0) {
                list.add(name + "做的肉包子");
                System.out.println(name + "做了一个肉包子");
                Thread.sleep(2000);
                //唤醒别人 等待自己
                this.notifyAll();
                this.wait();
            } else {
                //唤醒别人 等待自己
                this.notifyAll();
                this.wait();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public synchronized void get() {
        try {
            String name = Thread.currentThread().getName();
            if (list.size() == 1) {
                System.out.println(name + "吃了:" + list.get(0));
                list.clear();
                Thread.sleep(1000);
                //唤醒别人 等待自己
                this.notifyAll();
                this.wait();
            } else {
                //唤醒别人 等待自己
                this.notifyAll();
                this.wait();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }
}
java 复制代码
package learn12;

public class ThreadTest4 {
    public static void main(String[] args) {
        Desk desk = new Desk();
        //生产者线程

        new Thread(() -> {
            while (true) {
                desk.put();
            }
        }, "厨师1").start();
        new Thread(() -> {
            while (true) {
                desk.put();
            }
        }, "厨师2").start();
        new Thread(() -> {
            while (true) {
                desk.put();
            }
        }, "厨师3").start();
        //消费者线程
        new Thread(() -> {
            while (true) {
                desk.get();
            }
        }, "吃货1").start();
        new Thread(() -> {
            while (true) {
                desk.get();
            }
        }, "吃货2").start();
    }
}

线程池

线程池就是一个可以复用线程的技术。

使用线程池的必要性:

用户发起一个请求,后台就需要创建一个新线程。不使用线程池,会产生大量的线程,会损害系统的性能。

线程池可以固定线程和执行任务的数量,可以避免系统瘫痪和线程耗尽的风险。

创建线程池

临时线程什么时候创建?

新任务提交时,发现核心线程都在忙,任务队列也满了,并且还可以创建临时线程,此时会创建临时线程。

什么时候可以拒绝新任务?

核心线程和临时线程都在忙,任务队列都满了,新的任务过来时才会开始拒绝任务。

使用线程池处理Runnable任务

java 复制代码
package learn12;

public class MyRunnableLearn implements Runnable {
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + "==>输出666");
        try {
            Thread.sleep(Integer.MAX_VALUE);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
java 复制代码
package learn12;

import java.util.concurrent.*;

public class ThreadPoolTest {
    public static void main(String[] args) {
        //创建线程池对象
//        new ThreadPoolExecutor(
//                                int corePoolSize,
//                                int maximumPoolSize,
//                                long keepAliveTime,
//                                TimeUnit unit,
//                                BlockingQueue<Runnable> workQueue,
//                                ThreadFactory threadFactory,
//                                RejectedExecutionHandler handler
//        )
        ExecutorService pool = new ThreadPoolExecutor(3, 5, 8,
                TimeUnit.SECONDS, new ArrayBlockingQueue<>(4), Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy());
        Runnable target = new MyRunnableLearn();
        pool.execute(target);
        pool.execute(target);
        pool.execute(target);

        pool.execute(target);
        pool.execute(target);
        pool.execute(target);
        pool.execute(target);

        pool.execute(target);
        pool.execute(target);
        //都满了 拒绝新任务
        pool.execute(target);
//        pool.shutdownNow();
        //pool.shutdown();
    }
}

使用线程池处理Callable任务

java 复制代码
package learn12;

import java.util.concurrent.Callable;

public class MyCallable implements Callable<String> {
    private int n;

    public MyCallable(int n) {
        this.n = n;
    }

    @Override
    public String call() throws Exception {
        int sum = 0;
        for (int i = 1; i <= n; i++) {
            sum += i;
        }
        return Thread.currentThread().getName() + "求出了1-" + n + "的和是:" + sum;
    }
}
java 复制代码
package learn12;

import java.util.concurrent.*;

public class ThreadPoolTest2 {
    public static void main(String[] args) throws Exception {
        ExecutorService pool = new ThreadPoolExecutor(3, 5, 8,
                TimeUnit.SECONDS, new ArrayBlockingQueue<>(4), Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.CallerRunsPolicy());
        //使用线程池处理Callbable任务
        Future<String> f1 = pool.submit(new MyCallable(100));
        Future<String> f2 = pool.submit(new MyCallable(200));
        Future<String> f3 = pool.submit(new MyCallable(300));
        //复用前面的线程
        Future<String> f4 = pool.submit(new MyCallable(400));
        System.out.println(f1.get());
        System.out.println(f2.get());
        System.out.println(f3.get());
        System.out.println(f4.get());
    }
}

Excutors

大型并发系统环境中使用Excutors如果不注意可能会出现系统风险。

核心线程数

计算密集型任务:核心线程数量 = CPU核数+1

IO密集型任务:核心线程数量 = CPU核数*2

java 复制代码
package learn12;

import java.util.concurrent.*;

public class ThreadPoolTest2 {
    public static void main(String[] args) throws Exception {
        //通过Executors创建一个线程池对象
        ExecutorService pool = Executors.newFixedThreadPool(3);
        Executors.newSingleThreadExecutor();
        //使用线程池处理Callbable任务
        Future<String> f1 = pool.submit(new MyCallable(100));
        Future<String> f2 = pool.submit(new MyCallable(200));
        Future<String> f3 = pool.submit(new MyCallable(300));
        //复用前面的线程
        Future<String> f4 = pool.submit(new MyCallable(400));
        System.out.println(f1.get());
        System.out.println(f2.get());
        System.out.println(f3.get());
        System.out.println(f4.get());
    }
}

悲观锁

一开始就加锁,每次只能一个线程进入访问完毕后,再解锁,线程安全,性能较差。

java 复制代码
package learn12;

public class MyRunnable implements Runnable {
    public int count;

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println(this);
            synchronized (this) {
                System.out.println("count ===>" + (++count));
            }
        }
    }
}
java 复制代码
package learn12;

public class Test {
    public static void main(String[] args) {
        Runnable target = new MyRunnable();

        for (int i = 1; i <= 100; i++) {
            new Thread(target).start();
        }
    }
}

乐观锁

一开始不上锁,等要出现线程安全问题时,等要出现线程安全问题时,线程安全,性能较好。

如何实现乐观锁

复制代码
private AtomicInteger count = new AtomicInteger();
java 复制代码
package learn12;

import java.util.concurrent.atomic.AtomicInteger;

public class MyRunnable implements Runnable {
    private AtomicInteger count = new AtomicInteger();
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println(this);
            synchronized (this) {
                System.out.println("count ===>" + count.incrementAndGet());
            }
        }
    }
}

并发VS并行

python进阶学习也涉及到这里的知识点。

Python进阶(二)-CSDN博客

进程:正在运行的程序

线程:一个进程中可以运行多个线程

并发:线程是由CPU调度执行的,但CPU能同时处理的线程数量有限,为了保证全部线程都能执行,CPU会轮询为系统的每个线程服务,由于CPU切换的速度很快,好像是线程在同时执行,这就是并发。

并行:同一时间点,任务同时地运行,比如一台电脑,有8个CPU,每个CPU的每个核心都可以独立地执行一个任务,在同一时间点,可同时执行8个任务,这时任务是同时执行,并行地运行任务。

线程的生命周期

New:新建

Runnable:可运行

Timinated:被终止

Timed Waiting:计时等待

Waiting:无线等待

Blocked:阻塞

相关推荐
BestandW1shEs几秒前
彻底理解消息队列的作用及如何选择
java·kafka·rabbitmq·rocketmq
爱吃烤鸡翅的酸菜鱼3 分钟前
Java算法OJ(8)随机选择算法
java·数据结构·算法·排序算法
码蜂窝编程官方6 分钟前
【含开题报告+文档+PPT+源码】基于SpringBoot+Vue的虎鲸旅游攻略网的设计与实现
java·vue.js·spring boot·后端·spring·旅游
hccee20 分钟前
C# IO文件操作
开发语言·c#
Viktor_Ye22 分钟前
高效集成易快报与金蝶应付单的方案
java·前端·数据库
hummhumm24 分钟前
第 25 章 - Golang 项目结构
java·开发语言·前端·后端·python·elasticsearch·golang
一二小选手29 分钟前
【Maven】IDEA创建Maven项目 Maven配置
java·maven
J老熊34 分钟前
JavaFX:简介、使用场景、常见问题及对比其他框架分析
java·开发语言·后端·面试·系统架构·软件工程
猿java39 分钟前
什么是 Hystrix?它的工作原理是什么?
java·微服务·面试
AuroraI'ncoding41 分钟前
时间请求参数、响应
java·后端·spring