多线程

首先,需要理解线程和进程的区别。针对进程,每个进程都有独立的代码和数据空间,进程间的切换会有较大的开销,一个进程包含了1-n个线程。而一类线程会共享代码和数据空间,每个线程都有独立的运行栈和程序计数器,线程切换开销小。

线程是一个动态执行的过程,有一个产生到死亡的生命周期,首先是新建状态,使用new关键字和Thread类或其子类建立一个线程对象后,该线程就处于新建状态,保持这个状态直到程序start()这个进程。

当程序调用了start()方法后,就进入了就绪状态,就绪状态的进程处于就绪队列中,要等待JVM里线程调度器的调度。

如果就绪状态的线程获取了cpu的资源,就可以执行run(),此时线程便处于运行状态,此时线程的情况最为复杂,运行过程中可能变成阻塞状态、就绪状态、死亡状态。

阻塞状态指的是,线程因为某些原因放弃了cpu使用权,暂时停止运行,直到线程进入就绪状态,才有机会转到运行状态,一般来说阻塞的情况分为三种:1)等待阻塞,运行的线程执行了wait()方法;2)同步阻塞,运行的线程在获取对象的同步锁时,如果同步锁被其他线程占用,jvm就会把该线程放进锁池中;3)其他阻塞,运行的线程执行了sleep()或join()方法等,jvm会把线程设置为阻塞状态。

死亡状态指的是一个运行线程完成了任务或者其他终止条件发生时,该线程就切换到死亡状态。

以下图片会对线程的状态转换有更好的总结:
ポジショニングマップ

创建一个线程提供了三种方法:通过实现Runnable接口;通过继承Thread类本身;通过Callable和Future创建线程。这里主要介绍前两种。

关于怎么实现多线程,直接看代码就可以了,主要讲一下Thread和Runnable的区别,如果一个类继承Thread,则不适合资源共享,相反,Runnable更容易实现资源共享。总的来说,实现Runnable接口会有以下一些优势:1)适合多个相同的程序代码的线程去处理一个资源;2)可以避免java中单继承的限制;3)增加程序的健壮性,代码可以被多个线程共享,代码和数据独立;4)线程池只能放入实现Runnable或Callable类线程,不能直接放入继承Thread类。

最后再讲一下synchronized(线程同步),为什么需要线程同步,主要是因为java允许多线程并发控制,如果多个线程同时操作了一个可共享资源变量时,就会导致数据不准确,比如对同一组数据进行了几次同样的处理,就会出现重复的结果,因此需要加入同步锁避免在该线程没有完成操作前就被其他线程调用,从而保证了变量的唯一和准确性。