一起学JAVA线程(1)JAVA Thread基础:搞清楚线程的这几个重要问题,操作多线程并不难

今天是线程领域的开篇第一讲。java线程的文章,在网上已经铺天盖地了,但个人觉得线程是一个比较抽象的领域,很多文章没有从根本思维方式上把它具象化,一开始就直接给你从java.util.concurrent包开讲,结果读者被搞得云里雾里,越发觉得线程很神密,很深奥,很难以理解。一些复杂业务的线程运行确实难以琢磨的。但咱们不从业务开讲,咱从概念开讲,解决了概念的问题,线程编程就好办了。
在这一章里,我们提出第一讲的几个最基本的问题,都是初次接触线程时最疑惑的。
1、线程是个啥?
2、创建JAVA Thread
3、为啥要使用线程?
4、为啥使用线程非得要NEW,不NEW不行吗?
5、JAVA两个线程如何通讯?

1、线程是个啥?

在这里多的不唠,少的不说,线程的专业概念,各位另找。你就想像成一个命令就好了。
java应用启动时,最先启动main线程,main线程启动完成后再启动其他业务线程。

2、创建JAVA Thread

最简单开始创建线程有下面两种方式
1.通过继承Thread类创建线程。
2.通过实现Runnable接口创建线程。
其他方式创建线程后面再讲

3、为啥要使用线程?

因为cpu处理数据的速度足够快,其时间都是纳秒级别。如果服务器上的应用不多,那么cpu多数情况下是空闲的,为了充分利用cpu,应当使用线程。

一个简单例子:比如一个业务代码块里需要访问四、五个第三方的接口,并且访问这些接口又不需要先后顺序,同时访问的参数又不是共享参数。那么此时就应当使用线程来访问获取第三方接口数据。
使用方法是每个接口访问启动一个线程,等所有接口访问都获得数据后再进行计算处理。

根据实验数据访问一个http接口平均时间在700至900毫秒间,4-5个接口按顺序执行的得3~4秒时间这是让人无法接受的。如果使用线程时间将缩小到900毫秒以下。
复杂的业务计算代码中使用的线程更多,比如flink框架

4、为啥使用Thread非得要NEW,不NEW不行吗?

通俗的讲多使用一个线程,就相当于另起一个炉灶。比如夫妻快餐小吃店,厨师只有一位就是老板(也可能是老板娘),想提高上菜速度那必须得再加一个厨师,那你不就得new一个咯。所以使用线程非得要NEW,不new不行。new多了也不行,因为小吃店的厨房小啊。

5、JAVA两个线程如何通讯?

因为在CPU读取内存中的共享数据速度是非常的快,而业务处理过程所耗时间绝大多数是读取时间的几倍乃至上百倍。操作多线程最最重要的一个问题就是共享参数的一致性,解决的办法就是让多个线程进行通讯。java 关键字volatile,synchronized,持有同一对象等等都是通讯的一种手段。下面我们看看持有同一对象通讯的方法。

package LearnThread.learnthread;
public class MyThread implements Runnable{
    private Object phone;
    private MyCounter count;
    
    public MyThread(MyCounter count,Object phone) {
        this.phone = phone;
        this.count = count;
    }

    @Override
    public void run() {
        System.out.println("MyThread1开跑前=" + count.getRunNum());
        for (int i = 0; i < count.getRunNum(); i++) {
            System.out.println("MyThread1=" + (i+1) + "=" + count.getRunNum());
            count.setRunNum(count.getRunNum() - 1);
            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        
    }

}

package LearnThread.learnthread;
public class MyThread2 implements Runnable{
    private Object phone;
    private MyCounter count;
    
    public MyThread2(MyCounter count,Object phone) {
        this.phone = phone;
        this.count = count;
    }

    @Override
    public void run() {
        System.out.println("MyThread2开跑前=" + count.getRunNum());
        for (int i = 0; i < count.getRunNum(); i++) {
            System.out.println("MyThread2=" + (i+1) + "=" + count.getRunNum());
            count.setRunNum(count.getRunNum() - 1);
            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        
    }
}

package LearnThread.learnthread;
public class MyCounter {

    private Integer runNum = 0;
    
    public MyCounter(Integer runNum) {
        this.runNum = runNum;
    }

    public void setRunNum(Integer runNum) {
        this.runNum = runNum;
    }

    public Integer getRunNum() {
        return runNum;
    }
    
    public static void main(String[] args) {
        MyCounter count = new MyCounter(20);
        Object phone = new Object();
        MyThread run1 = new MyThread(count,phone);
        MyThread2 run2 = new MyThread2(count,phone);
        Thread thread1 = new Thread(run1);
        Thread thread2 = new Thread(run2);
        thread1.start();
        thread2.start();
    }
}

在这里插入图片描述

执行结果:19,17,15都不见了。此时有人会说:你这代码没加上同步块啊
说得非常好。我在MyThread和MyThread2的run方法加上一样的同步块,如下
在这里插入图片描述

在这里插入图片描述
增加了同步块,但加锁的都是自己。然并卵,在极端情况下还是会出现如下的结果:13不见了。
在这里插入图片描述
原因就是因为两个线程没有建立起有效的通讯。要建立起有效的通讯两个线程同步块必须持有同一个对象,将代码中的synchronized (this) --> synchronized (count) 就解决问题
将MyThread,MyThread2的run方法修改如下,会有什么效果,请看。MyThread的等待时间是100毫秒,MyThread2的等待时间是1毫秒(请自行修改代码)
在这里插入图片描述
在这里插入图片描述

执行结果在这里插入图片描述
大部份时间是mythread2执行的

               synchronized (count) {
                    count.wait(1);
                    System.out.println("MyThread2=" + (i+1) + "=" + count.getRunNum());
                    count.setRunNum(count.getRunNum() - 1);
                    count.notify();
                    //Thread.sleep(1); 
                }

将Mythread2的count.notify()放开后,执行结果如下
在这里插入图片描述
整个时间是交替进行的。再次证明要建立线程间的通讯,各线程应当持有同一对象。
看完这篇基础文章,你应该会对线程概念有个大致的了解,它也不是那么难。

如果你觉得这篇文章对你有用,请给个三赞吧

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值