多线程程序可以打印1到50的数字?

时间:2014-10-05 00:38:50

标签: java multithreading

我正在尝试编写一个程序,其中创建了两个线程,输出应该像第一个线程打印1,下一个线程打印2,第一个线程再次打印3,依此类推。我是初学者所以请帮助我清楚。我认为线程共享相同的内存,所以他们将共享i变量并相应地打印。但在输出中我得到像thread1:1,thread2:1,thread1:2,thread2:2等等。请帮助。这是我的代码

class me extends Thread
{
public int name,i;
public void run()
{
    for(i=1;i<=50;i++)
    {
        System.out.println("Thread" + name + " : " + i);
        try
        {
            sleep(1000);
        }
        catch(Exception e)
        {
            System.out.println("some problem");
        }
    }
}
}
public class he
{
public static void main(String[] args)
{
    me a=new me();
    me b=new me();
    a.name=1;
    b.name=2;
    a.start();
    b.start();
}
}

3 个答案:

答案 0 :(得分:0)

首先,您应该阅读此http://www.oracle.com/technetwork/java/codeconventions-135099.html。 其次,类成员变量不是共享内存。您需要将对象(例如计数器)显式传递给两个对象,以使其成为共享对象。但是,这还不够。共享内存可以由线程缓存,因此您将具有竞争条件。要解决此问题,您需要使用Lock或使用AtomicInteger

答案 1 :(得分:0)

你想要做的似乎是:

  • 将所有数字从1到50写入System.out
    • 多次打印没有任何号码
    • 正在按顺序打印数字
  • 由两个并发线程完成此执行

首先,让我们看看你的代码中发生了什么:每个数字打印两次。原因是ime的实例变量,即Thread。因此,每个Thread都有自己的i,即它们不共享该值。

要使两个线程共享相同的值,我们需要在构造me时传递相同的值。现在,使用原语int这样做对我们没有多大帮助,因为通过传递int我们没有传递引用,因此两个线程仍将在独立的内存位置上工作。

让我们定义一个新类Value,它为我们保存整数:(编辑:也可以通过传递数组int[]来实现保存对其内容的内存位置的引用)

class Value{
  int i = 1;
}

现在,main可以实例化类型为Value一个对象,并将对它的引用传递给两个线程。这样,他们就可以访问相同的内存位置。

class Me extends Thread {
  final Value v; 
  public Me(Value v){
    this.v = v;
  }
  public void run(){
    for(; v.i < 50; v.i++){
    // ...
  }

  public static void main(){
    Value valueInstance = new Value(); 
    Me a = new Me(valueInstance); 
    Me b = new Me(valueInstance); 
  }
}

现在i每次都不打印两次。但是,您会注意到行为仍然不符合要求。这是因为操作是交错的:a可能会读取i,比方说,值为5.接下来,b增加i的值,并存储新的值。 i现在为6.但是,a仍然会读取旧值5,并且会再次打印5,即使b刚刚打印了5。

要解决此问题,我们必须锁定实例v,即Value类型的对象。 Java提供了关键字synchronized,它将在synchronized块内的所有代码执行期间保持锁定。但是,如果你只是在你的方法中放置同步,你仍然无法得到你想要的。假设你写:

public void run(){ synchronized(v) {
  for(; v.i < 50; v.i++) {
  // ...
}}

您的第一个线程将获取锁定,但在整个循环执行之前(即i的值为50时)永远不会释放它。因此,在安全的情况下,必须以某种方式释放锁定。嗯...... run方法中唯一不依赖于i(因此不需要锁定)的代码是sleep,幸运的是这也是线程花费的地方最多的时间。

由于一切都在循环体中,因此一个简单的synchronized块不会。我们可以使用Semaphore获取锁定。因此,我们在Semaphore方法中创建main实例,与v类似,将其传递给两个线程。然后我们可以获取并释放Semaphore上的锁,让两个线程都有机会获得资源,同时保证安全性。

以下是可以解决问题的代码:

public class Me extends Thread {
    public int name;
    final Value v;
    final Semaphore lock;

    public Me(Value v, Semaphore lock) {
        this.v = v;
        this.lock = lock;
    }

    public void run() {
        try {
            lock.acquire();
            while (v.i <= 50) {
                System.out.println("Thread" + name + " : " + v.i);
                v.i++;
                lock.release();
                sleep(100);
                lock.acquire();
            }
            lock.release();
        } catch (Exception e) {
            System.out.println("some problem");
        }
    }

    public static void main(String[] args) {
        Value v = new Value();
        Semaphore lock = new Semaphore(1);
        Me a = new Me(v, lock);
        Me b = new Me(v, lock);
        a.name = 1;
        b.name = 2;
        a.start();
        b.start();
    }

    static class Value {
        int i = 1;
    }
}

注意:由于我们在循环结束时获取锁,因此我们还必须在循环之后释放它,否则永远不会释放资源。此外,我将for - 循环更改为while循环,因为我们需要在第一次释放锁之前更新i,否则其他线程可以再次读取相同的值。

答案 2 :(得分:-1)

检查以下链接以获取解决方案。使用多个线程,我们可以按升序打印数字

http://cooltekhie.blogspot.in/2017/06/#987628206008590221