概述
- 线程,是程序执行流的最小单元。一个标准的线程由线程ID,当前指令指针(PC),寄存器集合和堆栈组成。
- 在java中可有两种方式实现多线程,一种是继承Thread类,一种是实现Runnable接口。
Thread
public class AdultVideoShow1 extends Thread{
private String name;//名称
public AdultVideoShow1(String name){
this.name=name;
}
public void run() {
for(int i=0;i<5;i++){
System.out.println(name+" 使用了第"+(i+1)+"种姿势!");
try {
sleep((int) Math.random() * 2);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) {
new AdultVideoShow1("泷泽萝拉").start();
new AdultVideoShow1("苍井空").start();
}
[print]
苍井空 使用了第1种姿势!
泷泽萝拉 使用了第1种姿势!
泷泽萝拉 使用了第2种姿势!
苍井空 使用了第2种姿势!
泷泽萝拉 使用了第3种姿势!
苍井空 使用了第3种姿势!
泷泽萝拉 使用了第4种姿势!
苍井空 使用了第4种姿势!
泷泽萝拉 使用了第5种姿势!
苍井空 使用了第5种姿势!
继承Thread类,并且实现run()方法,实现线程,start()是线程启动的方法;可以看出,两个线程是交替执行的!
Runnable
public class AdultVideoShow2 implements Runnable{
private String name;//名称
public AdultVideoShow2(String name){
this.name=name;
}
@Override
public void run() {
// TODO Auto-generated method stub
for(int i=0;i<5;i++){
System.out.println(name+" 使用了第"+(i+1)+"种姿势!");
try {
Thread.sleep((int) Math.random() * 2);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) {
new Thread(new AdultVideoShow2("小泽玛利亚")).start();
new Thread(new AdultVideoShow2("波多野结衣")).start();
}
[print]
小泽玛利亚 使用了第1种姿势!
波多野结衣 使用了第1种姿势!
波多野结衣 使用了第2种姿势!
小泽玛利亚 使用了第2种姿势!
波多野结衣 使用了第3种姿势!
小泽玛利亚 使用了第3种姿势!
波多野结衣 使用了第4种姿势!
小泽玛利亚 使用了第4种姿势!
波多野结衣 使用了第5种姿势!
小泽玛利亚 使用了第5种姿势!
实现Runnable接口,并且重写run()方法,实现线程,start()是线程启动的方法。
Runnable和Thread的区别
先看个例子
public class AdultVideoShow2 implements Runnable,AdultVideo{
private int count=1;
public AdultVideoShow2(){}
@Override
public void run() {
// TODO Auto-generated method stub
for(int i=0;i<5;i++){
System.out.println(Thread.currentThread().getName()+" 使用了第"+(count++)+"种姿势!");
try {
Thread.sleep((int) Math.random() * 2);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
@Override
public void warning() {
// TODO Auto-generated method stub
System.out.println("
FBI WARNING!
");
System.out.println("Federal Law provides severe civil and criminal penalties for");
System.out.println("the unauthorized reproduction,distribution,or exhibition of ");
System.out.println("copyrighted motion pictures!");
System.out.println();
}
}
//另外一个接口
public interface AdultVideo {
public void warning();
}
public static void main(String[] args) {
AdultVideoShow2 av=new AdultVideoShow2();
av.warning();
new Thread(av,"小泽玛利亚").start();
new Thread(av,"波多野结衣").start();
}
[print]
FBI WARNING!
Federal Law provides severe civil and criminal penalties for
the unauthorized reproduction,distribution,or exhibition of
copyrighted motion pictures.
小泽玛利亚 使用了第1种姿势!
波多野结衣 使用了第2种姿势!
小泽玛利亚 使用了第3种姿势!
波多野结衣 使用了第4种姿势!
小泽玛利亚 使用了第5种姿势!
波多野结衣 使用了第6种姿势!
小泽玛利亚 使用了第7种姿势!
波多野结衣 使用了第8种姿势!
小泽玛利亚 使用了第9种姿势!
波多野结衣 使用了第10种姿势!
- 一部XX影片,开头都有警告之类的信息,那么我们新建一个接口,抽象打印警告信息的方法。这种需要实现多个接口的情况,只能选择Runnable接口的实现方式,才能避免继承的局限;
- 这次我们的XX影片更新了规则,两位明星每人要完成5个动作,并且不能重复,我们记录正常电影的动作总数。这种涉及到线程间的资源共享的情况,也只能选择Runnable接口的实现方式。
所以,在程序开发中只要是多线程肯定永远以实现Runnable接口为主,因为实现Runnable接口相比继承Thread类有如下好处:
- 避免点继承的局限,一个类可以继承多个接口。
- 适合于资源的共享
- 增加程序的健壮性,代码可以被多个线程共享,代码和数据独立
争用条件
总所众知,这几位明星实例非凡,我们将每位明星动作的总数加大,规则不变,记录所用明星完成的总动作数。
public class AdultVideoShow2 implements Runnable,AdultVideo{
private int count=1;
private int perCount=1;
public AdultVideoShow2(){}
public AdultVideoShow2(int perCount){
this.perCount=perCount;
}
@Override
public void run() {
for(int i=0;i<perCount;i++){
System.out.println(Thread.currentThread().getName()+" 使用了第"+(count++)+"种姿势!");
}
@Override
public void warning() {
System.out.println("
FBI WARNING!
");
System.out.println("Federal Law provides severe civil and criminal penalties for");
System.out.println("the unauthorized reproduction,distribution,or exhibition of ");
System.out.println("copyrighted motion pictures!");
System.out.println();
}
}
public static void main(String[] args) {
AdultVideoShow2 av=new AdultVideoShow2(100000);
av.warning();
new Thread(av,"小泽玛利亚").start();
new Thread(av,"波多野结衣").start();
}
[print]
小泽玛利亚 使用了第199990种姿势!
小泽玛利亚 使用了第199991种姿势!
小泽玛利亚 使用了第199992种姿势!
小泽玛利亚 使用了第199993种姿势!
小泽玛利亚 使用了第199994种姿势!
小泽玛利亚 使用了第199995种姿势!
小泽玛利亚 使用了第199996种姿势!
小泽玛利亚 使用了第199997种姿势!
2位明星,每人10万个动作… (请无视),正确的结果应该是20万,但是结果却少了3!
[分析]
假定这里是单核,当极端情况发生时,即两位同时完成了动作,并且要求记录下来。线程1从内存中读取了count值为5,并且增加1,还没来得及写回内存时,cpu的时间片段耗尽;此时线程2获取了cpu时间片段,又从内存中读取了count,值仍然为5,增加1。这个时候无论线程1、线程2以何种顺序写回内存 结果都将是6,而正确的应该是7。
[解决]
public class AdultVideoShow2 implements Runnable,AdultVideo{
private int count=1;//总动作数
private int perCount=1;//每人动作数
private final Object lockObj = new Object();//锁
public AdultVideoShow2(){}
public AdultVideoShow2(int perCount){
this.perCount=perCount;
}
@Override
public void run() {
synchronized(lockObj){
for(int i=0;i<perCount;i++){
System.out.println(Thread.currentThread().getName()+" 使用了第"+(count++)+"种姿势!");
}
lockObj.notifyAll();//
}
}
@Override
public void warning() {
System.out.println("
FBI WARNING!
");
System.out.println("Federal Law provides severe civil and criminal penalties for");
System.out.println("the unauthorized reproduction,distribution,or exhibition of ");
System.out.println("copyrighted motion pictures!");
System.out.println();
}
}
[print]
波多野结衣 使用了第199996种姿势!
波多野结衣 使用了第199997种姿势!
波多野结衣 使用了第199998种姿势!
波多野结衣 使用了第199999种姿势!
波多野结衣 使用了第200000种姿势!
加入互斥与同步(synchronized/notifyAll),得到了正确的结果。
线程状态
- 初始状态(New):新创建了一个线程对象。
- 就绪状态(Runnable):线程对象创建后,其他线程调用了该对象的start()方法。该状态的线程位于可运行线程池中,变得可运行,等待获取CPU的使用权。
- 运行状态(Running):就绪状态的线程获取了CPU,执行程序代码。
- 阻塞状态(Blocked):阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。
- 死亡状态(dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。
最后
以上就是昏睡八宝粥为你收集整理的并发基础的全部内容,希望文章能够帮你解决并发基础所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复