多线程

多线程的实现

java.lang.Thread类代表多线程

注意事项

  • 启动线程必须是start方法,不是调用run方法
  • 不要把主线任务放在启动子线程之前

继承Thread

/**
 * @author Pickle
 * @version V1.0
 * @date 2024/3/11 16:43
 */
public class MyThread extends Thread{
    @Override
    public void run() {
        ....
    }
}

优点:编码简单

缺点:线程类已经继承Thread,无法继承其他类,不利于功能扩展

实现Runable接口

import java.lang.management.RuntimeMXBean;

/**
 * @author Pickle
 * @version V1.0
 * @date 2024/3/11 16:43
 */
public class MyThread implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println("子线程" + i);
        }
    }
}
/**
 * @author Pickle
 * @version V1.0
 * @date 2024/3/11 16:50
 */
public class ThreadDemo {
    public static void main(String[] args) {
        Runnable myThread = new MyThread();
        new Thread(myThread).start();
        for (int i = 0; i < 10;i ++){
            System.out.println("主线程" + i);
        }
    }
}

优点:人物类只是实现接口,可以继续继承其他类、实现其他接口,扩展性强

缺点:需要多一个Runnable对象

上述方法也可以使用匿名内部类实现

/**
 * @author Pickle
 * @version V1.0
 * @date 2024/3/11 16:55
 */
public class ThreadDemo {
    public static void main(String[] args) {
        new Thread(()-> {
                for (int i = 0; i < 10; i++) {
                    System.out.println("子线程" + i);
                }
        }).start();
        for (int i = 0; i < 10; i++){
            System.out.println("主线程" + i);
        }
    }
}

实现Callable接口

这种线程创建方式最大的优点就是可以返回线程执行的结果。

  • 实现Callable接口
  • 重写call方法
import java.util.concurrent.Callable;

/**
 * @author Pickle
 * @version V1.0
 * @date 2024/3/11 17:01
 */
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 "线程求出了1-"+n+"的和为:" + sum;
    }
}
  • 创建MyCallable对象
  • 创建FurtureTack对象
  • 创建线程并且Start
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

/**
 * @author Pickle
 * @version V1.0
 * @date 2024/3/11 16:55
 */
public class ThreadDemo {
    public static void main(String[] args) {
        Callable<String> myCallable = new MyCallable(100);
        //未来任务对象是一个任务对象,实现了Runnable接口
        //可以在线程执行完毕之后,用未来任务对象调用get方法获取线程执行完毕之后的结果
        final FutureTask<String> ft = new FutureTask<>(myCallable);
        new Thread(ft).start();
        for (int i = 0; i < 10; i++) {
            System.out.println("主线程" + i);
        }
        //获取线程执行之后的结果
        try {
            //如果执行到这里,线程的结果还没计算完毕,会wait
            System.out.println(ft.get());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

线程的安全问题

多个线程同时操作一个数据,可能会造成数据修改异常,造成错误的结果。

线程同步

锁的范围越小,性能越好

  • 同步代码块

代码块上锁

image-20240311174635756

image-20240311175522387

  • 同步方法

方法上锁

image-20240311175806286

  • Lock锁

image-20240311180210085

	private final Lock lk = new ReentrantLock();

使用Lock一定要使用try/catch/finally,最后解锁,否则会造成死锁,确保早必要时释放锁。

线程通信(同步)

线程同步的前提一定是先要保证线程安全

image-20240311180924767

生产者与消费者

import java.util.ArrayList;
import java.util.List;
/**
 * @author Pickle
 * @version V1.0
 * @date 2024/3/11 18:11
 */
public class ThreadDemo {
    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();
    }
}

class Desk{
    private List<String> list = new ArrayList<>();
    //厨师123
    public synchronized void put(){
        try {
            final String name = Thread.currentThread().getName();
            if(list.isEmpty()){
                list.add(name + "做的肉包子");
                System.out.println(name + "做了一个肉包子");
                Thread.sleep(2000);
                this.wait();
                this.notifyAll();
            }else{
                this.wait();
                this.notifyAll();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    //消费者12
    public synchronized void get(){
        try {
            final String name = Thread.currentThread().getName();
            if (!list.isEmpty()){
                System.out.println(name + "吃了" + list.get(0));
                list.clear();
                Thread.sleep(2000);
                this.wait();
                this.notifyAll();
            }else{
                this.wait();
                this.notifyAll();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

线程池

可复用的线程技术

image-20240311183732598

JDK5.0起提供了代表线程池的接口:ExecutorService

得到线程池的两种方式

  • 使用ExecutorService的实现类ThreadPoolExecutor自创建一个线程池对象

临时线程什么时候创建?

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

线程池处理Runnable任务

/**
 * @author Pickle
 * @version V1.0
 * @date 2024/3/11 22:11
 */
public class MyRunnable implements Runnable{
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + "==> 输出");
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

import java.util.concurrent.*;

/**
 * @author Pickle
 * @version V1.0
 * @date 2024/3/11 18:46
 */
public class ThreadPoolDemo {
    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 MyRunnable();
        pool.execute(target);
        pool.execute(target);
        pool.execute(target);
        pool.execute(target);
        pool.execute(target);
        pool.execute(target);
    }
}

线程池处理Callable任务

import java.util.concurrent.*;
/**
 * @author Pickle
 * @version V1.0
 * @date 2024/3/11 18:46
 */
public class ThreadPoolDemo {
    public static void main(String[] args) throws Exception {
        ExecutorService pool = new ThreadPoolExecutor(3, 5, 8,
                TimeUnit.SECONDS, new ArrayBlockingQueue<>(4),Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy());
        final Future<String> f1 = pool.submit(new MyCallable(100));
        final Future<String> f2 = pool.submit(new MyCallable(200));
        final Future<String> f3 = pool.submit(new MyCallable(300));
        final 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());
    }
}
  • 使用Executors(线程池工具类)调用方法返回不同特点的线程池对象
import java.util.concurrent.*;
/**
 * @author Pickle
 * @version V1.0
 * @date 2024/3/11 18:46
 */
public class ThreadPoolDemo {
    public static void main(String[] args) throws Exception {
        final ExecutorService ft = Executors.newFixedThreadPool(3);
        final Future<String> f1 = ft.submit(new MyCallable(100));
        final Future<String> f2 = ft.submit(new MyCallable(200));
        final Future<String> f3 = ft.submit(new MyCallable(300));
        final Future<String> f4 = ft.submit(new MyCallable(400));
        System.out.println(f1.get());
        System.out.println(f2.get());
        System.out.println(f3.get());
        System.out.println(f4.get());
    }
}

大型并发系统中,如果使用Executors如果不注意可能会出现系统风险

线程状态

image-20240311225149723

悲观锁、乐观锁

悲观锁:一上来就加锁,每次只能一个线程进入访问完毕之后,再解锁。------线程安全,性能较差

image-20240311230545820

乐观锁:一开始不上锁,等要出现线程安全问题的时候才开始控制。------线程安全,性能好

image-20240311231110247

posted @ 2024-03-11 23:17  破忒头头  阅读(19)  评论(0)    收藏  举报