一、先简单粗暴解释一下一些与线程有关的概念
1.并行与并发
并行:多个cpu实例或者多台机器同时执行一段处理逻辑,是真正的同时。
并发:通过cpu调度算法,让用户看上去同时执行,实际上从cpu操作层面不是真正的同时。
2.资源共享
多个线程调用资源,是同一个或多个资源。
3.线程安全
在并发的情况之下,代码经过多线程使用,线程的调度顺序不影响最后结果,则是线程安全的。
3.同步
Java中的同步指的是通过人为的控制和调度,保证共享资源的多线程访问成为线程安全。例如使用@synchronized。
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()方法,该线程结束生命周期
二、实现Runnable接口相比继承Thread类有以下好处:
1.避免单继承的局限,一个类可以实现多个接口
2.适合资源的共享。
通过例子说明资源共享:
Runnable接口:
测试:
结果:明显Runnable可以实现资源共享
Thread父类:
测试:
结果:没有实现资源共享
总结:
(资源共享)
java多线程访问共享资源的方式:
(1).如果每个线程执行的代码相同,可以使用同一个Runnable对象,这个Runnable对象中有那个共享数据。
(2).如果每个线程执行的代码不同,这时候需要使用不用的Runnable对象,这种情况有两种方式实现:
1.将共享资源封装在另一个对象中,然后将这个对象逐一传给各个Runnable对象(在java中,对象(同过new来生成的对象)作为参 数传递时,传递的是对象的地址),每个线程对共享数据的操作方法也分配到那个对象身上去完成,这样容易实现针对该数据 进行各个操作的互斥和通信。
2.将这些Runnable对象作为某一个类中的内部类,共享数据作为这个外部类中的成员变量,每个线程对共享数据的操作也分配给外 部类,以便实现对共享数据进行各个操作的互斥和通信,作为内部类的各个Runnable对象调用外部类的这些方法。
总之,要同步互斥的几段代码最好放在几个独立的方法中,这些方法再放在一个类中,这样比较容易实现他们之间的互斥和通 信。
(3).简单极端的方式,在任意一个类中定义一个static变量,static变量将被所有线程共享
(同步)。同步方法/同步代码块
注:同步是一种高开销的操作,因此应该尽量减少同步的内容。
通常没有必要同步整个方法,使用synchronized代码块同步关键代码即可。