Java多线程03:多线程深入+多线程综合实例

Java高级编程03

本文基于 阿里云大学:Java高级编程 整理记录,仅用于个人学习/交流使用。

五、多线程深入话题

优雅的停止线程

在多线程操作之中如果要启动多线程肯定使用的是Thread类中的 start()方法,而如果对于多线程需要进行停止处理,Thread类原本提供有 stop()方法,但是对于这些方法从JDK1.2版本开始就已经将其废除了。而且一直到现在也不再建议出现在你的代码之中,而除了stop()之外还有几个方法也被禁用了:

  • 停止多线程:public void stop()

  • 销毁多线程:public void destroy()

  • 挂起线程:public final void suspend()、暂停执行

  • 恢复挂起的线程执行:public final void resume()

之所以废除掉这些方法,主要的原因是因为这些方法有可能导致线程的死锁,所以从JDK1.2开始都不建议使用

如果想要实现线程的停止需要通过一种柔和的方式来进行

范例:实现线程柔和的停止

public class MyThread {
    public static boolean flag=true;
    public static void main(String[] args) throws InterruptedException {
        new Thread(()->{
            int num=0;
            while (flag){
                try {
                    Thread.sleep(20);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+",num="+(num++));
            }
        }).start();

        Thread.sleep(200);
        flag=false;//停止线程
    }

}

万一现在有其它的线程去控制这个 flag 的内容,那么这个时候对于线程的停止也不是说停就立刻停止的,而是会在执行中断flag 的内容来完成。

后台守护线程

现在假设有一个人并且这个人有一个保镖,那么这个保镖一定是在这个人活着的时候进行守护,如果这个人已经死了,保镖没用了。

所以在多线程里面可以进行守护线程的定义,也就是说如果现在主线线程的程序或者其它的线程还在执行的时候,那么守护线程将一直存在,并且运行在后台状态。.

在Thread类里面提供有如下的守护线程的操作方法:

  • 设置为守护线程: public final void setDaemon( boolean on);

  • 判断是否为守护线程: public final boolean isDaemon();

范例:使用守护线程

public class MyThread {

    public static void main(String[] args) throws InterruptedException {
        Thread userThread=new Thread(()->{
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            for (int i = 0; i <10; i++) {
                System.out.println(Thread.currentThread().getName()+",i="+i);
            }
        },"用户线程");//完成核心业务

        Thread deamonThread=new Thread(()->{
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            for (int i = 0; i <Integer.MAX_VALUE; i++) {
                System.out.println(Thread.currentThread().getName()+",i="+i);
            }
        },"守护线程");

        deamonThread.setDaemon(true);
        userThread.start();
        deamonThread.start();
    }

}

可以发现所有的守护线程都是围绕在用户线程的周围,如果程序执行完毕了,守护线程也就消失了,在整个的JVM里面最大的守护线程就是GC线程。

程序执行中GC线程会一直存在,如果程序执行完毕,GC线程也将消失。

volatile关键字

在多线程的定义之中,volatile 关键字主要是在属性定义上使用的,表示此属性为直接数据操作,而不进行副本的拷贝处理,这样的话在一些书上就将其错误的理解为同步属性了。

在正常进行变量处理的时候往往会经历如下的几个步骤:

  • 获取变量原有的数据内容副本;
  • 利用副本为变量进行数学计算;
  • 将计算后的变量,保存到原始空间之中;

image-20210313095527981

而如果一个属性上追加了volatile关键字,表示的就是使用副本,而是直接操作原始变量,相当于节约了:拷贝副本、重新保存的步骤。

public class MyThread implements Runnable {
    private volatile int tickets=5;
    @Override
    public void run() {
        synchronized (this){
            while (this.tickets>0){
                System.out.println(Thread.currentThread().getName()+"买票处理,tickets="+this.tickets--);
            }
        }
    }
}

class ThreadDemo{
    public static void main(String[] args) {
        MyThread myThread=new MyThread();
        new Thread(myThread,"票贩子A").start();
        new Thread(myThread,"票贩子B").start();
        new Thread(myThread,"票贩子C").start();

    }
}

面试题:请解释volatile 与 synchronized 的区别?

  • vblatile主要在属性上使用,而 synchronized是在代码块与方法上使用的;
  • volatile无法描述同步的处理,它只是一种直接内存的处理,避免了副本的操作; synchronized能够解决同步问题

六、多线程综合案例

数字加减

设计4个线程对象,两个线程执行减操作,两个线程执行加操作。

这一题目是一个经典的多线程的开发操作,这一个程序里面一定要考虑的核心本质在于:加一个、减一个,整体的计算结果应该只在0、-1、1之间循环出现。

package com.lut.JavaPlus;

public class MyThread{
    public static void main(String[] args) {
        Resource resource=new Resource();
        SubThread subThread=new SubThread(resource);
        AddThread addThread=new AddThread(resource);
        new Thread(addThread,"加法线程A").start();
        new Thread(addThread,"加法线程B").start();
        new Thread(subThread,"减法线程A").start();
        new Thread(subThread,"减法线程B").start();
    }
}

class AddThread implements Runnable{
    private Resource resource;
    public AddThread(Resource resource) {
        this.resource = resource;
    }
    @Override
    public void run() {
        for (int i = 0; i < 50; i++) {
            try {
                this.resource.add();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

class SubThread implements Runnable{
    private Resource resource;

    public SubThread(Resource resource) {
        this.resource = resource;
    }

    @Override
    public void run() {
        for (int i = 0; i < 50; i++) {
            try {
                this.resource.sub();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

class Resource{//定义一个操作的资源
    private int num=0;//需要进行加减的数据
    private boolean flag=true;//加减的切换
    //flag=true 可以进行加法操作,不能进行减法操作
    //flag=false 可以进行减法操作,不能进行加法操作

    public synchronized void add() throws Exception {
        if (this.flag==false){ //现在要进行减法操作,加法等待
            super.wait();
        }
        Thread.sleep(100);
        this.num++;
        System.out.println("【加法操作-"+Thread.currentThread().getName()+"】 num="+this.num);
        this.flag=false;//加法操作执行完毕
        super.notifyAll();
    }

    public synchronized void sub() throws Exception {
        if (this.flag==true){ //减法需要等待
            super.wait();
        }
        Thread.sleep(200);
        this.num--;
        System.out.println("【减法操作-"+Thread.currentThread().getName()+"】 num="+this.num);
        this.flag=true;
        super.notifyAll();
    }
}

生产电脑

设计一个生产电脑和搬运电脑类,要求生产出一台电脑就搬走一台电脑,如果没有新的电脑生产出来,则搬运工要等待新电脑产出

如果生产出的电脑没有搬走,则要等待电脑搬走之后再生产,并统计出生产的电脑数量。

package com.lut.JavaPlus;

public class MyThread{
    public static void main(String[] args) {
        Resource resource=new Resource();
        new Thread(new Producer(resource)).start();
        new Thread(new Consumer(resource)).start();

    }
}


class Producer implements Runnable{
    private Resource resource;
    public Producer(Resource resource) {
        this.resource = resource;
    }
    @Override
    public void run() {
        for (int i = 0; i <50; i++) {
            try {
                this.resource.make();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

class Consumer implements Runnable{
    private Resource resource;
    public Consumer(Resource resource) {
        this.resource = resource;
    }
    @Override
    public void run() {
        for (int i = 0; i <50; i++) {
            try {
                this.resource.get();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

class Computer{
    private static int count=0;
    private String name;
    private double price;
    public Computer(String name,double price){
        this.name=name;
        this.price=price;
        count++;
    }

    @Override
    public String toString() {
        return "【count="+count+
                "-Computer{" +
                "name='" + name + '\'' +
                ", price=" + price +
                "}】";
    }
}

class Resource{
    private Computer computer;
    public synchronized void make() throws Exception {
        if (this.computer!=null){
            super.wait();
        }
        this.computer=new Computer("DELL",3000);
        System.out.println("生产电脑:"+computer.toString());
        super.notifyAll();
    }

    public synchronized void get() throws Exception {
        if (this.computer==null){
            super.wait();
        }
        System.out.println("取走电脑:"+computer.toString());
        this.computer=null;
        super.notifyAll();
    }
}

竞争抢答

实现一个竞拍抢答程序:要求设置三个抢答者(三个线程),而后同时发出抢答指令,抢答成功者给出成功提示,未抢答成功者给出失败提示。

对于这一个多线程的操作由于里面需要牵扯到数据的返回问题,那么现在最好使用Callable

public class MyThread{
    public static void main(String[] args) throws Exception {

        ThreadDemo threadDemo=new ThreadDemo();
        FutureTask<String> taskA=new FutureTask<String>(threadDemo);
        FutureTask<String> taskB=new FutureTask<String>(threadDemo);
        FutureTask<String> taskC=new FutureTask<String>(threadDemo);
        new Thread(taskA,"竞赛者A").start();
        new Thread(taskB,"竞赛者B").start();
        new Thread(taskC,"竞赛者C").start();
        System.out.println(taskA.get());
        System.out.println(taskB.get());
        System.out.println(taskC.get());
    }
}

class ThreadDemo implements Callable<String> {
    private boolean flag=false;

    @Override
    public String call() throws Exception {
       synchronized (this){
           if (this.flag==false){
               this.flag=true;
               return Thread.currentThread().getName()+"抢答成功!";
           }else {
               return Thread.currentThread().getName()+"抢答失败!";
           }
       }
    }
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值