我正在学习多线程。我编写了一个使用同步程序来使用两个线程打印10和5表的程序。虽然同步方法给了我预期的结果,但同步块却没有。我究竟做错了什么?
public class SynchronizationDemo {
public static void main(String[] args) {
Thread1 t=new Thread1(10);
Thread1 t1=new Thread1(5);
Thread thread1=new Thread(t);
Thread thread2=new Thread(t1);
thread1.start();
thread2.start();
}
//synchronized method
/*public static synchronized void printTable(int num) {
for (int i = 1; i <= 10; i++) {
System.out.println(num*i);
try {
Thread.sleep(1000);
}catch(InterruptedException ie){
ie.printStackTrace();
}
}
}*/
//synchronized block
public void printTable(int num)
{
synchronized(this){
for (int i = 1; i <= 10; i++) {
System.out.println(num*i);
try {
Thread.sleep(1000);
}catch(InterruptedException ie){
ie.printStackTrace();
}
}
}
}
}
class Thread1 implements Runnable{
int num;
Thread1(int num){
this.num=num;
}
@Override
public void run() {
new SynchronizationDemo().printTable(num);
}
}
代码输出: 10 5 10 20 30 15 20 40 25 50 60 30 70 35 40 80 90 45 100 50
预期输出: 10 20 30 40 50 60 70 80 90 100 5 10 15 20 25 30 35 40 45 50
答案 0 :(得分:4)
这两种方法的主要区别在于细节小而重要。
您的同步块正在this
上进行同步。
您的同步方法(已注释掉!)是static
方法。这意味着它正在SynchronizationDemo
类对象上进行同步!
但这并不能解释全部。另一件事是调用方法的方式。
new SynchronizationDemo().printTable(num);
您正在创建一个新实例,然后在其上调用该方法。
调用静态方法时,使用哪个实例没有任何区别,因为您正在Class
对象上进行同步。
调用实例方法时,实例是不同的,因此因此根本没有相互排斥。当两个线程在同一个对象上同步时,您只会得到互斥和适当的同步。
答案 1 :(得分:1)
您在两个不同的上下文中使用synchronized
关键字。
static synchronized
方法等待并获得SynchronizationDemo类级别的锁,并且该类只有一个锁。因此,该类的所有实例都必须按顺序等待该锁。
synchronized(this)
块(甚至是一个方法)等待并获取SynchronizationDemo类的对象锁,每个对象都有一个锁。当每个run()方法创建自己的实例new SynchronizationDemo()
时,该方法无需等待其他方法完成即可。
尝试通过传递对象来更改Thread1类。
class Thread1 implements Runnable{
SynchronizationDemo demo;
int num;
Thread1(SynchronizationDemo demo, int num){
this.demo = demo;
this.num = num;
}
@Override
public void run() {
demo.printTable(num);
}
}
现在,您可以将SynchronizationDemo类的相同对象传递给两个线程,如下所示。
public static void main(String[] args) {
SynchronizationDemo demo = new SynchronizationDemo();
Thread1 t=new Thread1(demo, 10);
Thread1 t1=new Thread1(demo, 5);
Thread thread1=new Thread(t);
Thread thread2=new Thread(t1);
thread1.start();
thread2.start();
}
答案 2 :(得分:0)
区别在于您还将方法更改为不再是static
。
public static synchronized void printTable(int num) { }
此locks on the class, not on any single instance。
等效的同步块为
synchronized(SynchronizationDemo.class){
}
因此,即使您有两个实例,它们仍然使用相同的锁(因为它们都是同一类),而synchronized(this)
则锁在两个独立的对象上。
答案 3 :(得分:0)
您正在SynchronizationDemo
线程方法中创建run()
对象的新实例。
@Override
public void run() {
new SynchronizationDemo().printTable(num);
}
因此,每个线程都有一个新对象。因此,在SynchronizationDemo.class
中,如果两个或多个线程无法访问同一对象,则同步块是无用的。因此,线程工作于不同的对象,并且每个线程都打印出其编号。
如果您的代码运行了2次或更多次,则无论thread1
是否早于thread2
,您都可以看到不同的输出。
您应将static synchronized
关键字与printTable
方法配合使用;
public static synchronized void printTable(int num){
//do sth.
}