同步方法究竟有什么作用



我正在使用Swing创建一个游戏。我start()stop()同步,因为我被告知它更好。同步有什么作用,使用它有什么优势?

我的代码:

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();
}

}

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

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

在此处阅读有关同步方法的更多信息。

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

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

你绝对不想要的。

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

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

最终效果是,当两个线程尝试同时执行该代码块时,一个线程等待"syncd"语句,直到另一个线程从块中出现。 这可确保"此处代码"部分一次仅在一个线程上运行。

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

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

下面是一个示例:想象一下一个简单的银行应用程序,可以重新提取和存入资金。如果你有三个线程在运行 - 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 美元。
  • 自动柜员机 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 美元。

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

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

换句话说,当您按下 start() 按钮时,在启动完成之前不允许按 stop() 按钮。

最新更新