Java多线程

基本概念

    /**
     * 1. 进程:就是我们在电脑上运行的一个一个程序
     * 2. 线程:操作系统进行运算的最小控制单位,一个进程内有多个线程
     *
     * 1. 并发: 统一时刻,多个指令在单个cpu上面交替执行
     * 2. 并行: 同一时刻,多个指令在多个cpu上面同时执行
     */

创建线程的几种方式

/**
 * 多线程实现的几种方式
 * 1. 继承Thread类,重写run方法,使用start方法调用
 * 2. 实现Runnable接口, new一个任务对象作为Thread的构造参数
 * 3 利用Callable和FutureTask接口实现,Callable表示要执行的任务,FutureTask管理多线程的结果
 */
   class MyThread extends Thread{
        @Override
        public void run(){
            for (int i = 0; i < 100; i++) {
                System.out.println(this.getName() + ": 线程被调用");
            }
        }
    }

    class MyRun implements Runnable{

        @Override
        public void run() {
            for (int i = 0; i < 10; i++) {
                System.out.println(Thread.currentThread().getName() + "------" + i);
            }
            //出让cpu的执行权仍有可能被抢回
            Thread.yield();
        }
    }

    //Integer表示线程返回的结果是Integer
    class MyCall implements Callable<Integer> {

        @Override
        public Integer call() throws Exception {
            int sum = 0;

            for (int i = 0; i < 100; i++) {
                sum += i;
            }
            return sum;
        }
    }

    @Test
    public void test1() throws ExecutionException, InterruptedException {
        //1. 通过继承Thread的开启线程
        MyThread t1 = new MyThread();
        MyThread t2 = new MyThread();
        t1.setName("Thread1");
        t2.setName("Thread2");
        t1.start();
        t2.start();

        //2. 通过实现Runnable接口实现
        //表示多线程要执行的对象
        MyRun myRun = new MyRun();

        Thread t1 = new Thread(myRun);
        Thread t2 = new Thread(myRun);
        t1.setName("Thread1");
        t2.setName("Thread2");
        t1.start();
        t2.start();

        //3. 实现Callable和FutureTask接口
        //创建我们的任务对象
        MyCall myCall = new MyCall();
        //创建线程运行结果的管理对象
        FutureTask<Integer> future = new FutureTask<>(myCall);
        //创建线程
        Thread thread = new Thread(future);
        //启动线程
        thread.start();
        //获取线程的结果
        System.out.println("result: " + future.get());
    }

线程常用的几种方法

/**
 * 线程常用的成员方法:
 * 1. String getName()                       获取线程名称
 * 2. void setName(String name)              设置线程名称
 * 3. static Thread currentThread()          获取当前线程
 * 4. static void sleep(Long time)           睡眠时间
 * 5. void setPriority(int newPriority)      设置线程优先级,默认5级,优先级越高越容易抢占cpu
 * 6. final int getPriority()                获取线程优先级
 * 7. final void setDaemon(boolean on)       设置为守护线程,当其他非守护线程结束后,守护线程也会随即结束
 * 8. static void yield()                    出让线程 / 礼让线程,出让cpu的执行权
 * 9. static void join()                     插入线程 / 插队线程
 */
//设置线程优先级
//创建任务对象
MyRun myRun = new MyRun();
//创建线程
Thread t1 = new Thread(myRun, "飞机");
Thread t2 = new Thread(myRun, "坦克");
//设置优先级
t1.setPriority(1);
t2.setPriority(10);
//启动线程
t1.start();
t2.start();

//设置守护线程
MyRun myRun = new MyRun();
//创建线程
Thread t1 = new Thread(myRun, "飞机");
Thread t2 = new Thread(myRun, "坦克");
//设置守护线程
t2.setDaemon(true);
//启动线程
t1.start();
t2.start();

//设置礼让线程,在Runnable中写上Thread.yield()
MyRun myRun = new MyRun();
//创建线程
Thread t1 = new Thread(myRun, "飞机");
Thread t2 = new Thread(myRun, "坦克");
//启动线程
t1.start();
t2.start();

//插入线程,在线程1启动后,插入线程,则会在线程1执行完后再执行线程2
MyRun myRun = new MyRun();
//创建线程
Thread t1 = new Thread(myRun, "飞机");
Thread t2 = new Thread(myRun, "坦克");
//启动线程
t1.start();
t1.join();
t2.start();

同步代码块

class MyThread extends Thread{

    //表示所有的对象共享这个ticket对象
    static int ticket = 0;

    //锁对象需要保持全局唯一,因此我们需要设置为static
    static Object obj = new Object();

    @Override
    public void run(){
        while(true){
            //这里我们要求是唯一的对象,所以我们一般会写MyThread.class
            synchronized (obj){
                if(ticket < 100){
                    System.out.println(this.getName() + "--------------正在卖第" + ticket++);
                }else{
                    break;
                }
            }
        }
    }
}
//同步代码快
public static void main(String[] args) {
    MyThread t1 = new MyThread();
    MyThread t2 = new MyThread();
    MyThread t3 = new MyThread();

    t1.setName("窗口1");
    t2.setName("窗口2");
    t3.setName("窗口3");

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

同步方法

    /**
     * 同步方法:就是将synchronized关键字放在方法上
     *
     * 特点:1. 同步方法锁住的是方法里所有的代码
     *      2. 不能指定锁对象: static > 类字节码文件对象  非static > this
     *
     * 技巧:我们只需要价将同步代码块中synchronized中的代码块按住 ctrl + alt + m 直接生成对应的同步方法
     */
public class MyRunnable implements Runnable{

    //因为我们这里线程用的同一个任务对象
    int ticket = 0;

    @Override
    public void run() {
        while(true){
            if (extracted()) break;
        }
    }

    //这里的锁对象默认是this
    private synchronized boolean extracted() {
        if(ticket < 100){
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println(Thread.currentThread().getName() + "-----------" + ticket++);
        }else{
            return true;
        }
        return false;
    }
}
    public static void main(String[] args) {
        MyRunnable myRunnable = new MyRunnable();
        Thread t1 = new Thread(myRunnable);
        Thread t2 = new Thread(myRunnable);
        Thread t3 = new Thread(myRunnable);

        t1.setName("窗口1");
        t2.setName("窗口2");
        t3.setName("窗口3");

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

LOCK锁

/**
 * LOCK锁:LOCK为借口没有实体对象,我们使用ReentrantLock对象
 *
 * 特点:1.lock可以手动的来开锁和关锁
 *      2.注意范围 static 为全局,否则为对象的
 *      3.关锁放在finnalu中
 */
class MyThread extends Thread{

    //表示所有的对象共享这个ticket对象
    static int ticket = 0;

    //锁对象需要保持全局唯一,因此我们需要设置为static
    static Object obj = new Object();

    //使用静态锁对象
    static Lock lock = new ReentrantLock();

    @Override
    public void run(){
        while(true){
//            synchronized (obj){
            //我们使用LOCK代替synchronized
            lock.lock();
            try {
                if(ticket < 100){
                    System.out.println(this.getName() + "--------------正在卖第" + ticket++);
                }else{
                    break;
                }
            } catch (Exception e) {
                throw new RuntimeException(e);
            } finally {
                //这里我们一定要放在finally中否则锁可能会释放失败
                lock.unlock();
            }
//            }
        }
    }
}
    public static void main(String[] args) {
        MyThread t1 = new MyThread();
        MyThread t2 = new MyThread();
        MyThread t3 = new MyThread();

        t1.setName("窗口1");
        t2.setName("窗口2");
        t3.setName("窗口3");

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

死锁

死锁是指两个或多个事务在同一资源上相互占用,并请求锁定对方的资源,从而导致恶性循环的现象。

等待唤醒机制

/**
 * wait: 阻塞,synchronized后的代码不会被执行,同时立即释放锁对象
 * notify: 继续执行完synchronized后面的代码,让被wait的线程重新获取锁执行之前未被执行完的代码
 */

 Demo1

//消息队列
static List<String> list = new ArrayList<>();

public static void main(String[] args) {
    new Thread(() -> {
        while (true){
            synchronized (list){
                //当队列中还有数据的时候我们进行等待
                if(list.size() != 0){
                    try {
                        list.wait();
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }else{
                    //当里面没有数据的时候我们生产数据
                    list.add(UUID.randomUUID().toString());
                    list.notify();
                    System.out.println("生产者线程A-----" + list);
                }
            }
        }
    }, "生产者线程A").start();

    new Thread(() -> {
        while (true){
            synchronized (list){
                //当队列中没有数据的时候选择等待
                if(list.size() == 0){
                    try {
                        list.wait();
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }else{
                    //对里面的数据消费
                    System.out.println("消费者线程B-----" + list);
                    list.notify();
                    list.clear();
                }
            }
        }

    }, "消费者线程B").start();
}

Demo2 

public class Desk {

    /**
     * 控制生产者和消费者的执行
     */

    //表示桌子上是否有面条:0 表示没有面条 1表示有面条
    public static int foodFlag = 1;

    //总个数,表示就生产10次
    public static int count = 10;

    //锁对象
    public static Object lock = new Object();

    public static void main(String[] args) {
        new Thread(() -> {
            while(true){
                synchronized (lock){
                    if(Desk.count == 0){
                        break;
                    }else{
                        //表示桌子上面有面条
                        if(Desk.foodFlag == 1){
                            try {
                                lock.wait();
                            } catch (InterruptedException e) {
                                throw new RuntimeException(e);
                            }
                        }else{
                            System.out.println("正在做面中。。。");
                            //唤醒其他线程
                            lock.notify();
                            //标记座子上有面条
                            Desk.foodFlag = 1;
                        }
                    }
                }
            }

        }, "生产者线程").start();

        new Thread(() -> {
            while (true){
                synchronized (lock){
                    if(Desk.count == 0){
                        break;
                    }else{
                        //表示桌子上面没有面条
                        if(Desk.foodFlag == 0){
                            try {
                                lock.wait();
                            } catch (InterruptedException e) {
                                throw new RuntimeException(e);
                            }
                        }else{
                            //吃面条
                            Desk.count--;
                            System.out.println("消费者线程正在吃面条------" + Desk.count);
                            //唤醒其他线程
                            lock.notify();
                            //标记座子上面没有面条了
                            Desk.foodFlag = 0;
                        }
                    }
                }
            }

        }, "消费者线程").start();
    }
}

Demo3 基于阻塞队列实现等待唤醒机制

/**
 * 阻塞队列实现等待唤醒机制
 *
 * ArrayBlockingQueue: 底层是数组,有界
 * LinkedBlockingQueue: 底层是链表,无界,但是并不是正真的无界,最大不超过int最大值
 *
 * 特点:在底层方法里都有锁了,我们在循环就不用锁了
 */
class Cook extends Thread{

    ArrayBlockingQueue<String> queue;

    public Cook(ArrayBlockingQueue<String> queue) {
        this.queue = queue;
    }

    @Override
    public void run(){
        while (true){
            try {
                queue.put("面条");
                System.out.println("我们将面条放入了队列");
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

class Foodie extends Thread{

    ArrayBlockingQueue<String> queue;

    public Foodie(ArrayBlockingQueue<String> queue) {
        this.queue = queue;
    }

    @Override
    public void run(){
        while (true){
            try {
                String take = queue.take();
                System.out.println("我们从队列中取出了》》》》》》" + take);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

public static void main(String[] args) {
    //两个线程用的是同一个阻塞队列
    ArrayBlockingQueue<String> queue = new ArrayBlockingQueue<>(1);

    Cook cook = new Cook(queue);
    Foodie foodie = new Foodie(queue);

    //因为我们的打印在锁的外面,所以打印并不会交替出现
    cook.start();
    foodie.start();
}

线程池

/**
* Executors.newCachedThreadPool() 创建一个没有上限的线程池
* Executors.newCachedThreadPool(int n) 创建一个有上限的线程池
*
* 特点:当提交任务的时候,线程池没有空闲的线程也不能新建线程,任务就会处于排队状态
*/

//创建线程池对象
ExecutorService pool = Executors.newCachedThreadPool();

//提交任务
pool.submit(new MyRunnable());
Thread.sleep(1000);
pool.submit(new MyRunnable());
Thread.sleep(1000);
pool.submit(new MyRunnable());
Thread.sleep(1000);
pool.submit(new MyRunnable());

//关闭线程池
pool.shutdown();

自定义线程池

 临时线程创建的时间:当核心线程被创建,且任务队列被排满的时候,这个时候开始创建临时线程,所以先被提交的线程不一定先被创建。

 触发任务拒绝策略:当核心线程、临时线程、任务队列都满了的时候,就会触发任务拒绝。

/**
 * ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor();
 * 
 * 参数一:核心线程数                    不能小于0  
 * 参数二:最大线程数                    不能小于0  
 * 参数三:临时线程最大存活时间(值)       不能小于0  
 * 参数四:临时线程最大存活时间(单位)     用TimeUnit指定
 * 参数五:任务队列                      不能为null
 * 参数六:创建线程工厂                   不能为null
 * 参数七:任务拒绝策略                   不能为null
 */

ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
        3,          //核心线程数
        6,                      //最大线程数
        60,                     //临时线程空闲60分钟
        TimeUnit.MINUTES,       //单位分钟
        new ArrayBlockingQueue<>(3),    //阻塞队列
        Executors.defaultThreadFactory(),       //线程创建方式
        new ThreadPoolExecutor.AbortPolicy()    //线程拒绝策略
);

综合练习 

抽红包:准备五个红包,其中只有三个红包有钱,且这三个红包总额为100元。

public class GrabRed extends Thread{

    static double money = 100;
    static int count = 3;
    static final double MIN = 0.01;

    @Override
    public void run(){
        synchronized (GrabRed.class){
            if(count == 0){
                System.out.println(Thread.currentThread().getName() + "...no money");
            }else{
                double prize = 0;
                if(count == 1){
                    prize = money;
                }else{
                    Random random = new Random();
                    prize = random.nextDouble(money - 2 * MIN);//最大值不超过这个
                    if(prize < MIN){
                        prize = MIN;
                    }
                }
                count--;
                money -= prize;
                System.out.println(Thread.currentThread().getName() + "...get money: " + prize);
            }
        }
    }
}
GrabRed t1 = new GrabRed();
GrabRed t2 = new GrabRed();
GrabRed t3 = new GrabRed();
GrabRed t4 = new GrabRed();
GrabRed t5 = new GrabRed();

t1.setName("AAA");
t2.setName("BBB");
t3.setName("CCC");
t4.setName("DDD");
t5.setName("EEE");

t1.start();
t2.start();
t3.start();
t4.start();
t5.start();

抽奖箱:模拟有两个抽奖箱从奖池内抽奖品

public class GrabRed extends Thread{

    private List<Integer> list;

    public GrabRed(List<Integer> list){
        this.list = list;
    }

    @Override
    public void run(){
        while (true){
            synchronized (GrabRed.class){
                if(list.isEmpty()){
                    break;
                }else{
                    Collections.shuffle(list);
                    int prize = list.remove(0);
                    System.out.println(Thread.currentThread().getName() + ">>>>getPrize>>>>" + prize);
                }
            }
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

在前一个问题的基础上统计每一个抽奖箱的数据

public class GrabRed extends Thread{

    private List<Integer> list;

    static List<Integer> list1;

    static List<Integer> list2;

    public GrabRed(List<Integer> list){
        this.list = list;
        list1 = new ArrayList<>();
        list2 = new ArrayList<>();
    }

    @Override
    public void run(){
        while (true){
            synchronized (GrabRed.class){
                if(list.isEmpty()){
                    if("抽奖箱1".equals(this.getName())){
                        System.out.println("抽奖箱1》》》" + list1);
                    }else{
                        System.out.println("抽奖箱2》》》" + list2);
                    }
                    break;
                }else{
                    //继续抽奖
                    Collections.shuffle(list);
                    int prize = list.remove(0);
                    if("抽奖箱1".equals(this.getName())){
                        list1.add(prize);
                    }else{
                        list2.add(prize);
                    }
                }
            }
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

在上面修改,为每一个抽奖箱设置一个队列

public class GrabRed extends Thread{

    private List<Integer> list;

    public GrabRed(List<Integer> list){
        this.list = list;
    }

    @Override
    public void run(){
        List<Integer> list1 = new ArrayList<>();
        while (true){
            synchronized (GrabRed.class){
                if(list.isEmpty()){
                    System.out.println(Thread.currentThread().getName() + ">>>" + list1);
                    break;
                }else{
                    //继续抽奖
                    Collections.shuffle(list);
                    int prize = list.remove(0);
                    list1.add(prize);
                }
            }
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}
public class Main {

    public static void main(String[] args) {
        List<Integer> list = new ArrayList<>();
        Collections.addAll(list, 10, 20, 5, 55, 50, 21, 20, 99);

        GrabRed t1 = new GrabRed(list);
        GrabRed t2 = new GrabRed(list);
        GrabRed t3 = new GrabRed(list);

        t1.setName("抽奖箱1");
        t2.setName("抽奖箱2");
        t3.setName("抽奖箱2");

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

在前面的基础上我们想知道每个抽奖箱的最大值,这个时候我们就需要使用Callable获取返回值

public class MyCallable implements Callable<Integer> {

    private List<Integer> list;

    public MyCallable(List list){
        this.list = list;
    }

    @Override
    public Integer call() throws Exception {
        List<Integer> list1 = new ArrayList<>();
        while (true){
            synchronized (GrabRed.class){
                if(list.isEmpty()){
                    System.out.println(Thread.currentThread().getName() + ">>>" + list1);
                    break;
                }else{
                    //继续抽奖
                    Collections.shuffle(list);
                    int prize = list.remove(0);
                    list1.add(prize);
                }
            }
            Thread.sleep(100);
        }
        if(list1.isEmpty()){
            return null;
        }else{
            return Collections.max(list1);
        }
    }
}
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        List<Integer> list = new ArrayList<>();
        Collections.addAll(list, 10, 12, 55, 60, 73, 22, 11, 19, 100);

        MyCallable myCallable = new MyCallable(list);
        FutureTask<Integer> ft1 = new FutureTask<>(myCallable);
        FutureTask<Integer> ft2 = new FutureTask<>(myCallable);
        FutureTask<Integer> ft3 = new FutureTask<>(myCallable);

        Thread t1 = new Thread(ft1);
        Thread t2 = new Thread(ft2);
        Thread t3 = new Thread(ft3);

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

        System.out.println(ft1.get());
        System.out.println(ft2.get());
        System.out.println(ft3.get());
    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值