【问题标题】:What exactly does a synchronized method do?同步方法到底是做什么的?
【发布时间】:2013-11-29 11:18:45
【问题描述】:

我正在使用 Swing 创建游戏。我使start()stop() 同步,因为有人告诉我这样做更好。 synchronized 有什么作用,使用它有什么好处?

我的代码:

import java.awt.Canvas;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.image.BufferStrategy;
import java.awt.image.BufferedImage;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;

import javax.swing.JFrame;

public class SpritePractice extends Canvas implements Runnable{

private JFrame frame;
private final static int WIDTH = 200, HEIGHT = 200;
private final static int SCALE = 2;
private final static Dimension dimens= new Dimension(WIDTH*SCALE, HEIGHT*SCALE);
private BufferedImage image;
private Graphics g;
private long nanoSecond = 1000000000;
private double tick = nanoSecond/60;
private boolean running = false;
private int pixelsFromImage[];
private int pixel[][];
private static DateFormat dateFormat = new SimpleDateFormat("[" + "yyyy/MM/dd HH:mm:ss"
        +"]");
private static DateFormat dateFormat2 = new SimpleDateFormat("[" + "HH:mm:ss" + "]");

public SpritePractice()
{
    frame = new JFrame("Bomberman");
    frame.setSize(dimens);
    frame.setMinimumSize(dimens);
    frame.setMaximumSize(dimens);
    frame.setResizable(false);
    frame.setLocationRelativeTo(null);
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.add(this);
    frame.pack();
    frame.setVisible(true);
    init();
}
public void init()
{
    long startTime = System.nanoTime();
    Calendar cal = Calendar.getInstance();
    System.out.println("START: " + dateFormat.format(cal.getTime()));
    start();
}

public void run() 
{
    long now = System.nanoTime();
    long lastTick = System.nanoTime();
    long lastSecond = System.nanoTime();
    int frames = 0;

    while(running)
    {
        now = System.nanoTime();
        Calendar cal = Calendar.getInstance();

        if(now-lastTick >= tick)
        {
            lastTick = now;
            tick();
            render();
            frames++;
        }   
        if(now-lastSecond >= nanoSecond)
        {
            lastSecond = now;
            System.out.println(dateFormat2.format(cal.getTime()) + "FPS: " + frames);
            frames = 0;
        }
    }
}
public void tick()
{
    //updates values
}
public void render()
{
    BufferStrategy bs = getBufferStrategy();
    if(bs==null)
    {
        createBufferStrategy(2);
        return;
    }
    Graphics g = bs.getDrawGraphics();
    g.fillRect(0, 0, WIDTH*2, HEIGHT*2);
    g.dispose();
    bs.show();
    //renders graphics
}
public synchronized void start()
{
    running = true;
    run();
}
public synchronized void stop()
{
    running = false;
}
public static void main(String[] args)
{
    new SpritePractice();
}

}

【问题讨论】:

  • this 可能会有所帮助
  • 你能把你的问题说得更具体一些吗? docs.oracle.com/javase/tutorial/essential/concurrency/…
  • 在 start 方法中调用 run() 方法没有意义(至少对我来说),也许是 new Thread(this).start() ?!
  • 并发是一个复杂的话题。在使用多个线程之前,您绝对应该了解自己在做什么。这个程序不是线程安全的。幸运的是,它没有启动任何线程。

标签: java methods synchronized runnable


【解决方案1】:

由于没有人用简单的英语解释这一点,我会:

将方法标记为“已同步”意味着您不希望多个线程同时访问该方法。如果您不希望您的代码允许所谓的“竞争条件”含义 - 再次用简单的英语表示 - “无论哪个线程首先到达这里,都会获胜”,这很有用。

这是一个例子: 想象一个简单的银行应用程序,它可以重新提取和存入资金。如果您有 3 个线程在运行 - 3 个 ATM 放置在一个小镇周围 - 如果需要,您希望所有三个线程 (ATM) 能够同时向同一个帐户存入资金和从同一个帐户重新提取资金。

如果没有同步,就会发生竞争条件。让我们来看看: 银行账户上有100美元。 A 想存入 20 美元。 B 想存入 50 美元。 C 想重新提取 130 美元。如果 A 人在 B 人存款之前存款,这发生在 C 人重提之前,这是可能的。但这就是问题所在!

在 A 人存款之前,账户上有 100 美元。存款方式可能如下所示:

// Naive deposit
public void deposit(double amount) {
    double currentAmount = getCurrentAmount(); // Critical
    setCurrentAmount(currentAmount + amount);
}

当人 A 存款时,ATM 需要检索当前金额,将人想要存款的金额相加并将当前余额设置为该金额。然而,在临界点(标记为// Critial),B 也可能已经存款。如果 ATM A(A 的 ATM)和 ATM B(B 的 ATM)同时存款,以下是可能出现问题的细分:

  • ATM A 检索当前金额。返还 100 美元。
  • ATM B 检索当前金额。返还 100 美元。
  • ATM B 将当前金额更新为 $100 + $50 = $150。
  • ATM B 将 150 美元存储为帐户的总金额。
  • ATM A 认为帐户中只有 100 美元,将总余额更新为 120 美元。

有人刚刚损失了 30 美元,因为 ATM B 在 ATM A 之前完成,在关键时刻!这就是“竞争条件”——这完全取决于谁先到。

现在使用synchronized 关键字!这个关键字在方法上设置了一个虚拟锁,这意味着只有被赋予这个“锁”的“钥匙”的线程(ATM)才能执行代码。所有其他线程都必须等待具有这个神奇“键”的线程完成。在上面的银行示例中,这意味着 ATM A 将完成向帐户存入 20 美元,然后 ATM B 可以检索当前金额并再存入 50 美元。

【讨论】:

    【解决方案2】:

    进入同步方法的线程获得对拥有该方法的整个对象的锁定。

    在您的特定情况下,您可以确定不会有两个并发线程同时执行 start()stop()

    阅读here 了解有关同步方法的更多信息。

    例如如果一个线程进入start() 方法,它将在另一个线程进入stop() 方法之前完成其执行。如果不同步这 2 个方法,则可以进行以下继承:

    --线程1进入start()
    -- 线程 1 将布尔字段设置为 true
    -- 线程2进入stop()
    -- 线程 2 将 boolean 字段设置为 false
    -- 线程1执行run()方法

    你绝对不想要的。

    【讨论】:

    • 好的。因此,如果一个线程尝试执行一个同步方法,而其他一些同步方法还没有完成执行,那么同步这两个方法可以确保第一个方法完全执行并完成,然后才允许调用第二个方法的线程执行?
    • 没错。只要他们尝试调用同一个对象的方法。
    • 非常感谢您,也感谢其他所有人的帮助!
    【解决方案3】:

    Java 中的每个对象实例都有一个隐式互斥锁。在一个方法上同步相当于如下代码:

    synchronized void foo() {
      // Some code here
    }
    void foo() {
      synchronized(this) {
        // Same code here
      }
    }
    

    最终结果是,当两个线程尝试同时执行该代码块时,一个线程会在“同步”语句处等待,直到另一个线程从代码块中出现。这样可以确保“此处的代码”部分一次只在一个线程上运行。

    【讨论】:

      【解决方案4】:

      start() 调用完成之前,您不能运行stop() 方法。反之亦然。 synchronized关键字表示start()stop()方法中没有两个线程同时调用。

      换句话说,当你按下 start() 按钮时,你不能按下 stop() 按钮,直到启动完成。

      【讨论】:

        【解决方案5】:

        同步允许正确的线程处理,这样 Java 将创建一个队列并处理您在应用程序中可能跨越的不同线程,而您不必担心。

        【讨论】:

          猜你喜欢
          • 2023-04-11
          • 2015-07-04
          • 2015-01-23
          • 2015-08-06
          • 2013-09-02
          • 2014-01-02
          • 2013-10-10
          • 2017-05-08
          相关资源
          最近更新 更多