我正在使用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() 按钮。