我从A类创建了一个扩展Thread类的两个线程a1
,a2
。 A类声明两个构造函数并运行同步的方法。当我以这种形式编写代码时,虽然run
方法被声明为synchronized并且我得到的结果像
0 0 1 1 2 3 4 5 6 7 2 8 3 9 4 10 5 11 6 12 7 8 9 10 11 12
代码:
public class Main {
public static void main(String[] args) {
A t = new A () ;
A a1 = new A (t) ;
A a2 = new A (t) ;
a1.start();
a2.start();
}
}
class A extends Thread {
public A() {
super() ;
}
public A(Thread th ) {
super(th) ;
}
@Override
public synchronized void run () {
for (int i = 0; i <13; i++) {
System.out.print(i+" ");
}
}
}
但是当我通过类Thread而不是A创建两个线程时,
Thread a1 = new Thread (t);
Thread a2 = new Thread (t);
同步方法run
工作,两个线程不会同时启动,总是给出结果
0 1 2 3 4 5 6 7 8 9 10 11 12 0 1 2 3 4 5 6 7 8 9 10 11 12
我的问题:当我从A类创建一个两个线程时,为什么synchronized关键字不起作用(虽然我定义了两个构造函数)并且当我从Thraed类创建一个两个线程时工作?
答案 0 :(得分:1)
当您通过在某个时刻调用start()
来运行线程时,它会调用run()
类Thread
类中的public void run() {
if (target != null) {
target.run();
}
}
方法:
run
此代码负责执行Runnable target
构造函数中传递的new Thread(Runnable target)
对象Thread#run()
方法的代码。
但是你在A
课程中覆盖了start()
。所以现在A#run
方法调用target.run()
(因为多态),这意味着它永远不会调用t.run()
(在您的情况下 - t
,因为A
被传递为{ {1}}线程目标)。
现在即使A#run
方法是synchronized
,因为每个线程在单独的实例(线程对象本身)上调用它,所以没有发生同步,因为线程没有使用 common lock / monitor。
您只能获得正确的结果,因为有时一个线程能够在其他线程开始之前完成其全部工作。
为了避免这种令人困惑的问题(以及许多其他问题)根本不扩展线程。创建实现Runnable的类并将其传递给Thread
实例。
将Runnable
视为任务,将Thread
视为应该执行该任务的 worker 。你的工作是描述 工作者应该做什么(拿走它,把它放在那里),而不是如何(弯曲你的膝盖,抓住它,......)。
答案 1 :(得分:0)
除非您使用Semaphore
,CountDownLatch
或CyclicBarrier
等同步器,否则您无法确定线程是否同时启动。
synchronized
关键字本身用于保护相同对象(方法或代码块)免受并发访问。在您的代码synchronized
中没用。
答案 2 :(得分:0)
以下是处理您问题的修改后的代码。
public class RunnableDemo {
public static void main(String[] args) {
Object lock = new Object();
Thread t1 = new Thread (new MyRunnable(lock));
Thread t2 = new Thread (new MyRunnable(lock));
t1.start();
t2.start();
}
}
class MyRunnable implements Runnable {
Object lock = new Object();
public MyRunnable(Object lock){
this.lock = lock;
}
@Override
public void run () {
synchronized(lock){
for (int i = 0; i <13; i++) {
System.out.print(i+" ");
}
}
}
}
RunnableDemo
MyRunnable
接口的Runnable
对象创建了两个线程。将Thread
接口实现传递给Thread
构造函数是首选方法,而不是将Runnable
扩展为创建新Thread
。 Thread
完成run()
阻止现在您可以看到输出的顺序如下:
0 1 2 3 4 5 6 7 8 9 10 11 12 0 1 2 3 4 5 6 7 8 9 10 11 12
我刚刚展示了基本的锁定机制。对于多线程的高级版本,您可以参考high level concurrency教程。