Groovy - 块同步
当我们在 Groovy 程序中启动两个或多个 thread 时,可能会出现多个 thread 尝试访问同一资源的情况,最终由于并发问题导致不可预见的结果。例如,如果多个 thread 尝试写入同一个文件,它们可能会破坏数据,因为其中一个 thread 可能会覆盖数据,或者当一个 thread 正在打开同一个文件时,另一个 thread 可能同时正在关闭该文件。
因此,需要同步多个 thread 的操作,确保在给定时间点只有一个 thread 可以访问资源。这通过一种称为 monitor 的概念来实现。Groovy 中的每个 object 都关联着一个 monitor,thread 可以锁定或解锁该 monitor。同一时间只有一个 thread 可以持有 monitor 的锁。
Groovy 中的块同步
Groovy 编程语言提供了一种非常便捷的方式来创建 thread 并同步它们的任务,即使用 synchronized 块。您可以将共享资源保存在这个块中。
语法
以下是 synchronized 语句的通用形式 −
synchronized(objectidentifier) {
// 访问共享变量和其他共享资源
}
在这里,objectidentifier 是对一个 object 的引用,其锁与 synchronized 语句所代表的 monitor 相关联。现在我们来看两个示例,我们将使用两个不同的 thread 来打印一个 counter。当 thread 未同步时,它们打印的 counter 值不是顺序的,但当我们将 counter 放在 synchronized() 块中打印时,两个 thread 都会非常有序地打印 counter。
示例 - 没有同步的多线程
这是一个简单的示例,它可能按顺序打印 counter 值,也可能不按顺序,每次运行时都会根据 CPU 对 thread 的可用性产生不同的结果。
Example.groovy
class Example {
static void main(String[] args) {
PrintDemo printDemo = new PrintDemo();
ThreadDemo t1 = new ThreadDemo( "Thread - 1 ", printDemo );
ThreadDemo t2 = new ThreadDemo( "Thread - 2 ", printDemo );
t1.start();
t2.start();
// 等待 thread 结束
try {
t1.join();
t2.join();
} catch ( Exception e) {
println("Interrupted");
}
}
}
class PrintDemo {
void printCount() {
try {
for(int i = 5; i > 0; i--) {
Thread.sleep(50);
println("Counter --- " + i );
}
} catch (Exception e) {
System.out.println("Thread interrupted.");
}
}
}
class ThreadDemo extends Thread {
private Thread t;
private String threadName;
PrintDemo printDemo;
ThreadDemo( String threadName, PrintDemo printDemo) {
this.threadName = threadName;
this.printDemo = printDemo;
}
void run() {
printDemo.printCount();
println("Thread " + threadName + " exiting.");
}
void start () {
println("Starting " + threadName );
if (t == null) {
t = new Thread (this, threadName);
t.start ();
}
}
}
输出
每次运行此程序都会产生不同的结果 −
Starting Thread - 1 Starting Thread - 2 Counter --- 5 Counter --- 5 Counter --- 4 Counter --- 4 Counter --- 3 Counter --- 3 Counter --- 2 Counter --- 2 Counter --- 1 Counter --- 1 Thread Thread - 1 exiting. Thread Thread - 2 exiting.
块级同步的多线程示例
这是一个相同的示例,它会按顺序打印 counter 值,并且每次运行都会产生相同的结果。我们在块上添加了 synchronized 关键字,这样 counter 递增代码在方法执行期间会根据对象进行锁定。我们使用当前对象作为锁,并在 synchronized 块中作为参数传递。
Example.groovy
class Example {
static void main(String[] args) {
PrintDemo printDemo = new PrintDemo();
ThreadDemo t1 = new ThreadDemo( "Thread - 1 ", printDemo );
ThreadDemo t2 = new ThreadDemo( "Thread - 2 ", printDemo );
t1.start();
t2.start();
// 等待线程结束
try {
t1.join();
t2.join();
} catch ( Exception e) {
println("Interrupted");
}
}
}
class PrintDemo {
void printCount() {
try {
for(int i = 5; i > 0; i--) {
Thread.sleep(50);
println("Counter --- " + i );
}
} catch (Exception e) {
println("Thread interrupted.");
}
}
}
class ThreadDemo extends Thread {
private Thread t;
private String threadName;
PrintDemo printDemo;
ThreadDemo( String name, PrintDemo pd) {
threadName = name;
printDemo = pd;
}
void run() {
synchronized(printDemo) {
printDemo.printCount();
}
println("Thread " + threadName + " exiting.");
}
void start () {
println("Starting " + threadName );
if (t == null) {
t = new Thread (this, threadName);
t.start ();
}
}
}
输出
每次运行此程序都会产生相同的结果 −
Starting Thread - 1 Starting Thread - 2 Counter --- 5 Counter --- 4 Counter --- 3 Counter --- 2 Counter --- 1 Thread Thread - 1 exiting. Counter --- 5 Counter --- 4 Counter --- 3 Counter --- 2 Counter --- 1 Thread Thread - 2 exiting.
方法级同步的多线程示例
这是一个相同的示例,它会按顺序打印 counter 值,并且每次运行都会产生相同的结果。这次我们在方法上添加了 synchronized 关键字,这样整个方法在执行期间会根据对象进行锁定。
Example.groovy
class Example {
static void main(String[] args) {
PrintDemo printDemo = new PrintDemo();
Thread t1 = new Thread(printDemo );
Thread t2 = new Thread(printDemo );
t1.start();
t2.start();
// 等待线程结束
try {
t1.join();
t2.join();
} catch ( Exception e) {
println("Interrupted");
}
}
}
class PrintDemo extends Thread {
void printCount() {
try {
for(int i = 5; i > 0; i--) {
Thread.sleep(50);
println("Counter --- " + i );
}
} catch (Exception e) {
println("Thread " + Thread.currentThread().getName()+" interrupted.");
}
}
synchronized void run() {
printCount();
println("Thread " + Thread.currentThread().getName() + " exiting.");
}
}
输出
每次运行此程序都会产生相同的结果 −
Counter --- 5 Counter --- 4 Counter --- 3 Counter --- 2 Counter --- 1 Thread Thread-1 exiting. Counter --- 5 Counter --- 4 Counter --- 3 Counter --- 2 Counter --- 1 Thread Thread-2 exiting.