今天准备开始简书之路,记录自己的进步,这个阶段从java开始,希望自己可以坚持下来。。。这里的东西很多都是网上其他大神的结论,都说的非常好!
废话不多说:这几天我们来看看 java多线程。
今天我们从多线程定义,生命周期,互斥/同步,简单场景来稳固一下多线程的定义。
什么是多线程:我之前的理解,就是多个用户进行了同一个操作。但是我在之前开发的时候,也没有用得到多线程代码,但是多个人访问同一个接口也没有错啊。。。为什么呢?直到我看了某网友的解释以后明白了:
方法 存在JVM的一个内存区,这个方法区被各个线程共享。
打个不恰当的比方,如下:
老师在黑板上面写了一条1+1=?,同学们 同时看到信息后开始拿起笔计算。这里的黑板就相当于内存区,1+1=? 指令相当于方法,同学们相当于线程,大家一起并行计算,并没有阻塞。
那什么时候有阻塞?
老师说,谁要解答,请上讲台上来,这个时候可能会出现阻塞。
这个解释真的还是比较好懂的。。。直到了线程的基本概念以后,我们来看看线程的生命周期
Java线程具有五中基本状态
新建状态(New):当线程对象对创建后,即进入了新建状态,如:Thread t = new MyThread();
就绪状态(Runnable):当调用线程对象的start()方法(t.start();),线程即进入就绪状态。处于就绪状态的线程,只是说明此线程已经做好了准备,随时等待CPU调度执行,并不是说执行了t.start()此线程立即就会执行;
运行状态(Running):当CPU开始调度处于就绪状态的线程时,此时线程才得以真正执行,即进入到运行状态。注:就 绪状态是进入到运行状态的唯一入口,也就是说,线程要想进入运行状态执行,首先必须处于就绪状态中;
阻塞状态(Blocked):处于运行状态中的线程由于某种原因,暂时放弃对CPU的使用权,停止执行,此时进入阻塞状态,直到其进入到就绪状态,才 有机会再次被CPU调用以进入到运行状态。根据阻塞产生的原因不同,阻塞状态又可以分为三种:
1.等待阻塞:运行状态中的线程执行wait()方法,使本线程进入到等待阻塞状态;
2.同步阻塞 -- 线程在获取synchronized同步锁失败(因为锁被其它线程所占用),它会进入同步阻塞状态;
3.其他阻塞 -- 通过调用线程的sleep()或join()或发出了I/O请求时,线程会进入到阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。
死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。
这5种状态基本就是我们现在能用到和遇到的99%的功能了。
看到这里,差不多基本就对线程有了很多的理解,我基本也就理解了这些。。。下面我们来看看线程的两个常用方法:互斥和同步
互斥
啥是互斥:就是有个方法,里面有个变量 i = 0,每次运行,这个变量 i 都会+1。但是现在有2个线程同时访问了这个方法,那么就有可能第一个线程算出来了 i+1=1;还没有保存呢,第二个线程进来了,获取到的i还是0,而不是第一个线程的结果,第二个线程也做了i+1的操作=1;这个时候第一个线程做的运算就没有被计算进去,本来应该是2的,结果返回了1。
这时候就要用到 synchronized 线程锁的概念
临界段:多线程互斥使用共享资源的程序段,在操作系统中称为临界段。临界段是一种加锁的机制,与多线程共享资源有关。
临界段的作用是在任何时刻一个共享资源只能供一个线程使用。当资源未被占用,线程可以进入处理这个资源的临界段,从而得到该资源的使用权;当线程执行完毕,便退出临界段。如果一个线程已进入某个共享资源,并且还没有使用结束,其他线程必须等待。
在JAVA中使用关键字synchronized定义临界段,能对共享对象进行上锁操作。
synchronized 线程锁是线程安全的保障。
互斥就是大家都去操作一个东西,互不干扰,但是这个数据要保证每个人处理的过程都有效的保存,还有一种情况就是,大家都要去操作一个东西,A要等B操作完才能操作,这种怎么办呢,那么就有了一个东西,叫做:
同步
多线程之间除了有互斥情况外,还有线程同步。当线程A使用某个对象,而此对象又需要线程B修改后才能符合本线程的需要,此时线程A就要等待线程B完成修改工作。这种线程相互等待称为线程的同步。
为实现同步,JAVA语言提供了wait()、notify()和notifyAll()三个方法供线程在临界段中使用。
在临界段中使用wait()方法,使执行该方法的线程等待,并允许其他线程使用这个临界段。wait()常用两种格式:
wait()——让线程一直处于等待队列,知道被使用了notify()或notifyAll()方法唤醒。
wait(long timeout)——让线程等待到被唤醒,或经过指定时间后结束等待。
当线程使用完临界段后,用notify()方法通知由于想使用这个临界段而处于等待状态的线程结束等待。notify()方法只是通知第一个处于等待的线程。
如果某个线程在使用完临界段方法后,其他早先等待的线程都可以结束等待,一起重新竞争CPU,则可以使用notifyAll()方法。
现在的企业应用开始越来越多的使用多线程的开发,可能有的人不懂到底什么时候才要用多线程呢,那我们这里举一些简单的例子:批量上传,批量发邮件,异步的添加,活动中的秒杀等等。
现在很多程序更多的会遇到 高并发!
那么多线程也是解决高并发的一个有效手段,但不是唯一的方式!至于高并发,在后面介绍springboot的时候,我们在来看看高并发的定义和实际问题。
多线程(1)就到这里结束了,下一节,我们用真实场景,来看一下多线程在代码中的应用和实现!