有地方说,Java不同线程资源共享要通过实现Runnable接口,而通过继承Thread不能实现资源共享。 其实这里有一个误区: 我们以猴子抢香蕉为例
public class Monkey extends Thread{
//为了更直观看到结果,使用公平锁
public Lock lock = new ReentrantLock(true);
public Integer banana = 10;
@Override
public void run() {
for(;;) {
lock.lock();
if(banana<=0){
System.out.println("香蕉抢完了");
break;
}
System.out.println(super.getName() + "抢到了香蕉:" + banana--);
lock.unlock();
}
}
}
class Main{
public static void main(String[] args) {
Thread thread1 = new Monkey();
Thread thread2 = new Monkey();
thread1.start();
thread2.start();
}
}
定义了两个猴子线程,让这两个猴子去抢香蕉,大致一看没什么问题,但实际运行结果却是这样。
看到这估计大家已经知道问题所在了。这分明是两个线程自己执行自己的。相当于我们本来想要的效果是在一个世界里有两个猴子抢十个香蕉,但是现在却成了两个猴子分别在自己的世界里拿自己的香蕉。
实际上误区就在这里,我们new了两个线程,虽然这两个线程是同一个类,但实际上是创建了两个猴子对象,这两个对象各自执行自己的run方法。我们通过源码分析。
Runnable接口里只有一个run()方法,该方法代表线程体,也就是用户线程的核心代码。
Thread类的构造器可以添加参数Runnable类型的变量。
Thread类有一个Runnable类型的target变量。
在Thread的init()方法中,会将传入的Runnable类型的变量传给target变量。
然后再Thread的run()方法中,会判断target变量是否为空(也就是是否传入Runnable类型的变量),如果不为空,就执行target的run()方法,也就是传入的Runnable类型的对象的run方法。
所以,我们要实现的是让多个线程执行同一个run()方法,而不是自己执行自己的run()方法。 就可以通过Thread的构造器将Monkey对象传入,这样Monkey对象只有一个,run方法也就只有一个,多个线程执行的也是同一个run()方法。
修改后代码:
public class Monkey extends Thread{
public Lock lock = new ReentrantLock(true);
public Integer banana = 10;
@Override
public void run() {
for(;;) {
lock.lock();
if(banana<=0){
System.out.println("香蕉抢完了");
break;
}
System.out.println(Thread.currentThread().getName() + "抢到了香蕉:" + banana--);
lock.unlock();
}
}
}
class Main{
public static void main(String[] args) {
Monkey monkey = new Monkey();
//Thread类也是实现了Runnable接口
Thread thread1 = new Thread(monkey);
Thread thread2 = new Thread(monkey);
thread1.start();
thread2.start();
}
}
执行结果:
总结:
继承Thread类并不是不能实现资源共享,资源共享在于多个线程执行同一个核心代码块(不绝对),因此要通过Thread构造器传入Runnable类型的参数,让多个参数执行这个Runnable的run()方法。
延申:
还可以通过这种形式
public class Monkey extends Thread{
Banana b;
Lock lock;
public Monkey(Banana b,Lock lock){
this.b = b;
this.lock = lock;
}
@Override
public void run() {
for(;;) {
lock.lock();
if(b.banana<=0){
System.out.println("香蕉抢完了");
break;
}
System.out.println(Thread.currentThread().getName() + "抢到了香蕉:" + b.banana--);
lock.unlock();
}
}
}
class Banana{
Integer banana = 10;
}
class Main{
public static void main(String[] args) {
Banana banana = new Banana();
Lock lock = new ReentrantLock(true);
Thread thread1 = new Monkey(banana,lock);
Thread thread2 = new Monkey(banana,lock);
thread1.start();
thread2.start();
}
}
即将共享资源提出来,作为唯一对象传给两个线程。尽管两个线程执行了两个run()方法,但是在各自的run()方法中,操作的资源(变量)是同一个。
运行结果: