您的位置:首页 > 编程语言 > Java开发

Java线程同步

2016-03-04 13:53 323 查看
一、问题产生
假设一种场景:
有一个静态变量num,初始值为0。现在开了个线1000程,每个线程内循环1000次,每循环对num自加1,问最后的值是大于、等于还是小于1000000?
下面编写代码来看一下结果:

importjava.util.concurrent.TimeUnit;

public
class Test implements Runnable{
private
static int num = 0;

private
void increaseNumber() {
num++;
}

@Override
public
void run() {
for(int
i = 1; i <=1000;
i++){
increaseNumber();
System.out.println("值为" +
num + ",当前" + Thread.currentThread().getName());
}
}

public
static void main(String[]
args) throws InterruptedException {
for (int
i = 1; i <=1000;
i++) {
Thread thread =
new Thread(new Test());
thread.setName("线程"+i);
thread.start();
}

try {
// 等待全部子线程执行完毕
TimeUnit.SECONDS.sleep(30);
} catch(InterruptedException
e) {
e.printStackTrace();
}
System.out.println("num的最终值为" +
num);
}
}

运行结果:



分析:这里可以看出,结果小于1000000。这是因为不同的线程可能同时访问num,比如两个线程同时在某个时候访问num,这个时候num只自加了1次。若是改为先后访问,则num应该自加2次。这样导致最终结果不等于1000000。

二、同步
为了防止数据被两个或多个线程同时访问,咱们可以用synchronized关键字将数据保护起来。Synchronized的内部机理是用锁对代码段进行保护,线程A要访问这段代码时,先查看数据代码段是否处于加锁状态。若代码段处于加锁状态,说明有别的线程在访问这段代码,必须等特别的进程访问完,锁才会被释放,然后A线程才可以访问这段代码。若数据未处于加锁状态,则线程A可以访问这段代码,同时给该代码段加锁,这样别的线程要访问这块代码段就得等线程A把锁释放掉(代码段执行完时释放锁)。
同步后的代码如下:

importjava.util.concurrent.TimeUnit;

public
class Test implements Runnable{
private
static int num = 0;

synchronized
private void increaseNumber() {
num++;
}

@Override
public
void run() {
for(int
i = 1; i <=1000;
i++){
increaseNumber();
System.out.println("值为" +
num + ",当前" + Thread.currentThread().getName());
}
}

public
static void main(String[]
args) throws InterruptedException {
for (int
i = 1; i <=1000;
i++) {
Thread thread =
new Thread(new Test());
thread.setName("线程"+i);
thread.start();
}

try {
// 等待全部子线程执行完毕
TimeUnit.SECONDS.sleep(30);
} catch(InterruptedException
e) {
e.printStackTrace();
}
System.out.println("num的最终值为" +
num);
}
}

运行结果:



分析:在increateNumber()方法加上synchronized关键字后,num的值仍然不为1000000,这是什么原因呢?这要从synchronized的作用对象来分析。事实上,synchronized的作用对象是this,也就是当前对象。这样只有多个线程访问同一对象时,synchronized才会起作用。而上面代码是生成了1000个对象,即new Test()1000次,所以synchronized加跟没加都一样。

三、静态同步
若一个类的多个对象(这个例子中是1000个对象)要访问同一个数据(这里是static int num),要考虑对整个类进行同步,这样被同步的代码块就作用于该类的所有实例,从而不会被两个或多个线程(本例中一个线程就是一个实例)同时访问。Java中,是用static synchronized关键字来表示静态同步。

importjava.util.concurrent.TimeUnit;

public
class Test implements Runnable{
private
static int num = 0;

static
synchronized private
void increaseNumber() {
num++;
}

@Override
public
void run() {
for(int
i = 1; i <=1000;
i++){
increaseNumber();
System.out.println("值为" +
num + ",当前" + Thread.currentThread().getName());
}
}

public
static void main(String[]
args) throws InterruptedException {
for (int
i = 1; i <=1000;
i++) {
Thread thread =
new Thread(new Test());
thread.setName("线程"+i);
thread.start();
}

try {
// 等待全部子线程执行完毕
TimeUnit.SECONDS.sleep(30);
} catch(InterruptedException
e) {
e.printStackTrace();
}
System.out.println("num的最终值为" +
num);
}
}

运行结果:

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: