java多线程相关问题
很多知识还是应该自己进行测试,看看结果,别人的看了印象不是太深刻。
java多线程实现的方法:
A, 集成Thread类: public class Thread1 extends Thread{}
B, 实现Runnable接口: public class Thread1 implements Runnable{}
两者之间的差别:
A, java不能多继承,故在继承Thread类时候我们就不能再继承其他类了。
B, 实现Runnable接口能够更好的对同一个资源进行访问。
A比较容易理解,B呢?为什么会这样?
我们做一个测试: 恰好今天教师节,举个应景的例子。假设再幼儿园里面老师给同学发苹果,有多个老师,但是苹果数是一定的,那这就是一个多线程的问题。我们做如下测试:
public class Test{
static class Teacher extends Thread{
public Teacher(String temp){
super(temp);
}
//苹果数量
private int apple = 5;
public void run(){
while(true){
if(apple>0){
try {
//让线程休眠1s
sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//老师进行分发
System.out.println(Thread.currentThread().getName()+"开始分发苹果,剩余"+apple--);
}
<span style="white-space:pre"> </span>else
<span style="white-space:pre"> </span> break;
}
}
}
public static void main(String[] args0){
Teacher teacher1 = new Teacher("Teacher1");
Teacher teacher2 = new Teacher("Teacher2");
Teacher teacher3 = new Teacher("Teacher3");
teacher1.start();
teacher2.start();
teacher3.start();
}
}
在上面的例子中大家会看到我们的逻辑是实例化多个teacher,那么在进行多个线程运行的时候,其实他们的占有各自的内存空间,彼此之间并没有什么直接联系。结果可以预见:Teacher1开始分发苹果,剩余5
Teacher2开始分发苹果,剩余5
Teacher3开始分发苹果,剩余5
Teacher1开始分发苹果,剩余4
Teacher2开始分发苹果,剩余4
Teacher3开始分发苹果,剩余4
Teacher1开始分发苹果,剩余3
Teacher2开始分发苹果,剩余3
Teacher3开始分发苹果,剩余3
Teacher1开始分发苹果,剩余2
Teacher2开始分发苹果,剩余2
Teacher3开始分发苹果,剩余2
Teacher1开始分发苹果,剩余1
Teacher2开始分发苹果,剩余1
Teacher3开始分发苹果,剩余1
但是这样写多线程并不是正确的方式,待会我们会解析。可以发现,所有实例都是各做各的,并没有什么直接联系。这是因为单独实例化了多个对象,每个对象都独立存储栈中,栈中存储的变量也相互独立。所以都是各自完成自己的工作。
换一种方式,用实现runnable接口实现:
class Teacher implements Runnable{
private int apple = 10;
@Override
public void run() {
// TODO Auto-generated method stub
while(true){
if(apple>0){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"开始分发苹果,剩余"+apple--);
}
else
break;
}
}
}
public class Test{
public static void main(String[] args0){
Teacher teacher = new Teacher();
Thread thread1 = new Thread(teacher,"Teacher1");
Thread thread2 = new Thread(teacher,"Teacher2");
Thread thread3 = new Thread(teacher,"Teacher3");
thread1.start()<span style="font-family: Arial, Helvetica, sans-serif;">;</span>
thread2.start():
thread3.start();
}
}
这样,其实可以看出来,虽然我们实例化了多个Thread,但是我们并只是相当于使用Thread来运行我们的同一个teacher实例,所以结果:Teacher1开始分发苹果,剩余10
Teacher1开始分发苹果,剩余9
Teacher1开始分发苹果,剩余8
Teacher1开始分发苹果,剩余7
Teacher1开始分发苹果,剩余6
Teacher1开始分发苹果,剩余5
Teacher1开始分发苹果,剩余4
Teacher1开始分发苹果,剩余3
Teacher1开始分发苹果,剩余2
Teacher1开始分发苹果,剩余1
Teacher3开始分发苹果,剩余0
这才是我们预期的结果,但是还是不正确,如果我们把sleep的时间改大一点就会出现问题。这是因为我们在操作的时候并没有获取对象锁,那么就可能存在这样两个线程,A和B,A判断之后执行--操作之前,B也进行了判断,发现apple=2,那么两个线程同时进行了--操作,则就导致了上面的情况,出现了apple=0的错误。