Java 我的线程是并行运行的吗?

Java 我的线程是并行运行的吗?,java,multithreading,thread-safety,Java,Multithreading,Thread Safety,我有以下代码: public class A extends Thread{ static List<String> a = new ArrayList<String>(); private String name; public A(String name) { this.name = name; } public void run() { synchronized (A.class) { a.a

我有以下代码:

public class A extends Thread{

static List<String> a = new ArrayList<String>();

private String name;

public A(String name)
{
    this.name = name;
}

public void run() {
         synchronized (A.class) {
                    a.add(this.name);
                }     
    }

public static void main(String[] args) throws Exception{

    for(int i=0;i<100;i++)
    {
        A s1 = new A("thread 1");
        A s2 = new A("thread 2");
        A s3 = new A("thread 3");

        s1.start();
        s2.start();
        s3.start();
    }
        Thread.sleep(1000);
        System.out.println("The message is " + a);

}

}
从输出来看,我认为我的线程不是并行运行的

我应该在
run
方法中添加并发代码吗


我在这里遗漏了什么吗?

您的线程只运行了很短的一段时间,因为
run
几乎立即终止。所以这是可能的,但不太可能,它们同时运行。线程在其
run
方法返回时终止;
run
方法不会重复调用或类似的调用

当然,由于您在
run
中执行的所有操作都是在
A.class
上同步的,因此即使它们同时运行,其中一个在将其添加到共享列表时也会阻止其他操作

如果您希望看到线程实际同时运行的效果,则需要让它们继续执行某些操作(可能是在
run
中执行某种循环,或使用随机毫秒数),然后删除同步(或播放同步以查看其影响)。但是,在执行此操作时,在调用
a.add
(最好在
a
上进行同步,而不是在
a.class
)时保持同步,即使在其他位置不同步。(或者将您的列表包装成一个表格。)

下面是一个完整的示例,显示了实际上彼此重叠的线程:

import java.util.*;

public class ParallelExample extends Thread {

    static List<String> a = new ArrayList<String>();

    private String name;

    public ParallelExample(String name) {
        this.name = name;
    }

    public void run() {
        // Un-synchronized, random delay, just to let the threads
        // intermix.
        try {
            Thread.sleep((new Random()).nextInt(1000) + 500);
        }
        catch (Exception e) {
            // For this example I'm ignoring the InterruptedException
        }

        // Now add the name after that random delay
        synchronized(ParallelExample.class) {
            a.add(this.name);
        }
    }

    public static void main(String[] args) throws Exception {
        ParallelExample threads[] = new ParallelExample[10];

        // Start the threads
        for (int i = 0; i < threads.length; ++i) {
            threads[i] = new ParallelExample("thread " + i);
            threads[i].start();
        }

        // Don't use sleep here, use join
        try {
            for (int i = 0; i < threads.length; ++i) {
                threads[i].join();
            }
        }
        catch (Exception e) {
            // For this example I'm ignoring the InterruptedException
        }

        // Show results
        System.out.println("The message is " + a);
    }
}
import java.util.*;
公共类ParallelExample扩展了线程{
静态列表a=新的ArrayList();
私有字符串名称;
公共并行示例(字符串名称){
this.name=名称;
}
公开募捐{
//不同步,随机延迟,只是为了让线程
//混合。
试一试{
Thread.sleep((new Random()).nextInt(1000)+500);
}
捕获(例外e){
//对于这个例子,我忽略InterruptedException
}
//现在在随机延迟之后添加名称
已同步(ParallelExample.class){
a、 添加(此名称);
}
}
公共静态void main(字符串[]args)引发异常{
ParallelExample线程[]=新的ParallelExample[10];
//启动线程
对于(int i=0;i
运行示例:

$ java ParallelExample The message is [thread 4, thread 3, thread 9, thread 7, thread 6, thread 2, thread 5, thread 1, thread 8, thread 0] $java并行示例
消息是[thread 4、thread 3、thread 9、thread 7、thread 6、thread 2、thread 5、thread 1、thread 8、thread 0]要查看线程所产生的差异,请尝试以下操作:

public void run() {
    synchronized (A.class) {
        Thread.sleep(10000);
        a.add(this.name);
   }     
}
public void run() {
    Thread.sleep(10000);
    synchronized (A.class) {
        a.add(this.name);
   }     
}
然后尝试以下操作:

public void run() {
    synchronized (A.class) {
        Thread.sleep(10000);
        a.add(this.name);
   }     
}
public void run() {
    Thread.sleep(10000);
    synchronized (A.class) {
        a.add(this.name);
   }     
}
两者都将产生大致相同的输出(根据计时的工作方式可能略有不同),但您将看到总运行时间的巨大差异

在每种情况下,您都会产生3个线程,每个线程等待10秒,然后返回

在第一种情况下,虽然每个线程必须等待同步块,然后才能进行处理,但总运行时间大约为30秒

在第二种情况下,总运行时间约为10秒

这就是串行和并行执行之间的区别。想象一下,10秒的等待实际上是非常密集的,您可以看到线程是如何加快速度的

这也说明了为什么在线程中获得正确的同步是如此重要。如果你不这么做,你的程序就会崩溃,数据会被破坏,还有其他各种各样的肮脏行为。如果做得太多,您可能会失去线程的所有好处,甚至在某些情况下会使程序完全死锁

另一方面,虽然在正确的情况下,它可能非常强大,例如最近我重新处理了很多图像,每次重新处理可能需要10秒到2分钟


通过使用线程并向每个线程发送不同的图像,我能够使用CPU上的所有内核来进行处理。由于每个图像都是独立的,因此没有同步块来减慢速度。这导致了性能的大幅提高。

避免同步阻塞,使用非阻塞解决方案

而不是使用:

static List<String> a = new ArrayList<String>();
// Now add the name after that random delay
        synchronized(ParallelExample.class) {
            a.add(this.name);
        }
static List a=new ArrayList();
//现在在随机延迟之后添加名称
已同步(ParallelExample.class){
a、 添加(此名称);
}
您可以使用以下内容:

private static Queue<String> queue = new ConcurrentLinkedQueue<String>();
// Now add the name after that random delay
        queue.offer(this.name);
私有静态队列队列=新的ConcurrentLinkedQueue();
//现在在随机延迟之后添加名称
queue.offer(this.name);

也许OP可以在进入
已同步的
块之前随机休眠一段时间。另外,由于“a”是共享资源,因此明智的做法是在使用“a”的代码段周围加上一个已同步的(a)块,以防止死锁。@halileohalilei:现有的同步可以做到这一点,通过在
A.class
上同步。但是是的,如果删除它,至少在调用
a.add
(或使用同步列表)时需要同步。但是如果所有线程都被阻止运行
,多线程的意义何在?它和普通的方法调用一样吗?关键是要理解它是如何工作的。我认为这是一个很好的问题,肯定是一个很棒的答案。在RunStarnge中不是同步吗?类上的同步不是奇怪的,而方法不是静态的?线程所做的就是运行它的
run
方法,所以是的,您应该在那里添加并发代码。