前言:本博客主要涉及java编程中的线程、多线程、生成者消费者模型、死锁。
目录
创建一个简单的线程
获取执行当前代码的线程名:Thread.currentThread().getName()
开启线程:对象名.start()
守护线程 优先级
- //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();
- }
- }
- 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);
- }
-
- }
-
- }
线程安全
例子:多个人同时去一个账户里取钱,被取出后,不能再取出钱。但多个线程执行时,在访问时,账户里有钱的,但实际上已经被其他用户取走,此时再取钱就会引发数据不安全的问题。
使用多线程时,每个线程都对数据进行修改,如何来保证数据的安全性?
- 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 + "来取钱,但余额不足");
- }
- }
- }
- 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);
- }
- }
- 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
用其修饰成员方法,被修饰的方法,在同一时间,只能被一个线程执行
- //同步方法
- 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 + "来取钱,但余额不足");
- }
- }
- }
注意:解锁要放在finally里,以便程序出错时,可以解锁。
- 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();
- }
-
- }
- }
-
生产者线程负责生产数据
消费者线程负责消费生产者产生的数据
- package learn12;
-
- import java.util.ArrayList;
- import java.util.List;
-
- public class Desk {
- private List
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();
- }
-
- }
- }
- 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();
- }
- }
线程池就是一个可以复用线程的技术。
使用线程池的必要性:
用户发起一个请求,后台就需要创建一个新线程。不使用线程池,会产生大量的线程,会损害系统的性能。
线程池可以固定线程和执行任务的数量,可以避免系统瘫痪和线程耗尽的风险。
创建线程池
临时线程什么时候创建?
新任务提交时,发现核心线程都在忙,任务队列也满了,并且还可以创建临时线程,此时会创建临时线程。
什么时候可以拒绝新任务?
核心线程和临时线程都在忙,任务队列都满了,新的任务过来时才会开始拒绝任务。
- 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();
- }
- }
- }
- 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
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();
- }
- }


- package learn12;
-
- import java.util.concurrent.Callable;
-
- public class MyCallable implements Callable
{ - 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;
- }
- }
- 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
f1 = pool.submit(new MyCallable(100)); - Future
f2 = pool.submit(new MyCallable(200)); - Future
f3 = pool.submit(new MyCallable(300)); - //复用前面的线程
- Future
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如果不注意可能会出现系统风险。
核心线程数
计算密集型任务:核心线程数量 = CPU核数+1
IO密集型任务:核心线程数量 = CPU核数*2
- 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
f1 = pool.submit(new MyCallable(100)); - Future
f2 = pool.submit(new MyCallable(200)); - Future
f3 = pool.submit(new MyCallable(300)); - //复用前面的线程
- Future
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());
- }
- }
一开始就加锁,每次只能一个线程进入访问完毕后,再解锁,线程安全,性能较差。
- 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));
- }
- }
- }
- }
- 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();
- 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());
- }
- }
- }
- }
python进阶学习也涉及到这里的知识点。
进程:正在运行的程序
线程:一个进程中可以运行多个线程
并发:线程是由CPU调度执行的,但CPU能同时处理的线程数量有限,为了保证全部线程都能执行,CPU会轮询为系统的每个线程服务,由于CPU切换的速度很快,好像是线程在同时执行,这就是并发。
并行:同一时间点,任务同时地运行,比如一台电脑,有8个CPU,每个CPU的每个核心都可以独立地执行一个任务,在同一时间点,可同时执行8个任务,这时任务是同时执行,并行地运行任务。
New:新建
Runnable:可运行
Timinated:被终止
Timed Waiting:计时等待
Waiting:无线等待
Blocked:阻塞