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);
}
}
运行结果:
假设一种场景:
有一个静态变量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);
}
}
运行结果:
相关文章推荐
- getparameter和getattribution的区别的 java详细
- 《Java程序设计基础》 第2章手记
- 探究java方法参数传递——引用传递?值传递!
- Java内部类
- 学习Java书籍
- Java自动装箱与拆箱
- Eclipse 快捷键整理
- Java自动装箱与拆箱
- ant、maven解决rt.jar访问限制问题
- JAVA IO流(下)
- 【SpringMVC】SpringMVC系列15之SpringMVC最佳实践
- Java [Leetcode 260]Single Number III
- 【SpringMVC】SpringMVC系列14之SpringMVC国际化
- 【SpringMVC】SpringMVC系列13之关于 mvc:annotation-driven
- java堆栈类解决括号匹配问题
- java mysql 中文查询
- java中==与equals()方法的区别与使用
- 【SpringMVC】SpringMVC系列12之数据类型转换、格式化、校验
- Java 集合类
- 【SpringMVC】SpringMVC系列11之Restful的CRUD