Java 线程共享数据的常用方法是给多个线程传递同一个对象,由这个对象通过 synchronized 关键字同步共享数据的访问,让数据在同一时间只有一个线程能操作它。
除了使用 synchronized 关键字外,还可以使用显式锁或原子变量来实现数据访问的同步。本文只讨论 synchronized 关键字的实现。
1、使用 Runnable 对象
实例化一个自定义 Runnable 对象,然后用这个 Runnable 对象启动多个线程:
public class TestThread {
public static void main(String[] args) {
SharedRunnable runnable = new SharedRunnable();
new Thread(runnable).start();
new Thread(runnable).start();
}
}
class SharedRunnable implements Runnable{
private int ticket = 10;
public void run() {
while(ticket > 0){
ticket--;
System.out.println("当前票数为:"+ticket);
}
}
}
2、使用普通对象
使用普通对象的好处是,可以在不同线程里执行不同的方法。
public class TestThread {
public static void main(String[] args) {
final SharedData sharedData = new SharedData();
for (int i = 0; i < 2; i++) {
new Thread(new Runnable() {
public void run() {
sharedData.add();
}
}).start();
new Thread(new Runnable() {
public void run() {
sharedData.dec();
}
}).start();
}
}
}
class SharedData {
private int ticket = 0;
public synchronized void add() {
ticket++;
System.out.println("线程" + Thread.currentThread().getName() + " ticket 为:" + ticket);
}
public synchronized void dec() {
ticket--;
System.out.println("线程" + Thread.currentThread().getName() + " ticket 为:" + ticket);
}
}
3、使用单例
单例的方法更灵活,因为它不用关心线程的启动。
比如 Play 的 Job 中是没办法在创建线程的时候传入对象的,因为线程是系统自动创建的,开发者无法介入。
public class TestThread {
public static void main(String[] args) {
for (int i = 0; i < 2; i++) {
new Thread(new Runnable() {
public void run() {
SharedData.instance().add();
}
}).start();
new Thread(new Runnable() {
public void run() {
SharedData.instance().dec();
}
}).start();
}
}
}
class SharedData {
private static SharedData sharedData;
private int ticket = 0;
private SharedData() {
}
public static SharedData instance() {
if (sharedData == null) {
sharedData = new SharedData();
}
return sharedData;
}
public synchronized void add() {
ticket++;
System.out.println("线程" + Thread.currentThread().getName() + " ticket 为:" + ticket);
}
public synchronized void dec() {
ticket--;
System.out.println("线程" + Thread.currentThread().getName() + " ticket 为:" + ticket);
}
}
4、静态方法
静态方法上也可以使用 synchronized,它同步对象是 ClassName.class,比如 TestThread.class。
public class TestThread {
public static void main(String[] args) {
for (int i = 0; i < 2; i++) {
new Thread(new Runnable() {
public void run() {
while (true) {
if (!dec()) return;
}
}
}).start();
new Thread(new Runnable() {
public void run() {
while (true) {
if (!dec()) return;
}
}
}).start();
}
}
private static int ticket = 100;
public static synchronized boolean dec() {
if (ticket == 0) return false;
ticket--;
System.out.println("线程" + Thread.currentThread().getName() + " ticket 为:" + ticket);
return true;
}
}