1.对Java方法进行加锁

Java使用synchronized对一个对象进行加锁,也就可使用synchronized对一个方法进行加锁。

在执行synchronized语句时,我们首先获得synchronized指定的锁。当我们添加synchronized语句块的时候,首先需要注意的是锁住那个对象?让线程自己决定锁住哪个对象,不是一个好的设计。更好的方法是通过数据封装,把同步逻辑封装到持有数据的实例中。

1.1 同步方法

由于多线程访问实例变量的时候,可能会出现同步问题,所以在add和dec方法中把this对象加锁,这样就可以保证对任何一个count实例,多线程访问的时候都是安全的。当我们对this进行加锁的时候,我们可以用synchronized来修饰这个方法,这样就把同步的代码块变为方法级别。

用synchronized修饰的方法相当于在方法内部用synchronized锁住this变量,这2种方法是完全等价的



注意:如果方法内的语句不在同步代码块内,就不再等价。

1.2 静态方法:锁住的是Class实例

如果对静态方法使用synchronized修饰符,锁住的将是当前class的Class实例。

2 示例

class Counter{
int count = 0;
public synchronized void add(int n){
count += n;
}
public synchronized void dec(int n){
count -= n;
}
public int get(){//读取一个int类型是原子操作,不需要同步
return count;
}
}
class AddThread extends Thread {
Counter counter; public AddThread(Counter counter) {
this.counter = counter;
} public void run() {
for (int i = 0; i < Main.LOOP; i++) {
counter.add(1);
}
}
}
class DecThread extends Thread{
Counter counter;
public DecThread(Counter counter){
this.counter = counter;
}
public void run(){
for(int i=0;i<Main.LOOP;i++){
counter.dec(1);
}
}
} public class Main{
static final int LOOP=10000;
public static void main(String[] args) throws InterruptedException{
Counter counter = new Counter();
Thread t1 = new AddThread(counter);
Thread t2 = new DecThread(counter);
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(counter.get());
}
}

3.读取方法是否需要同步?

在这个例子中,value是int类型,读取int类型是原子操作,是不需要同步的。

    public int get(){ //读取一个int类型是原子操作,不用同步
return count;
}

但是把代码改一下,把value变成一个长度为2的int数组。就会发现,读方法仍然需要同步。否则在执行完result[0]=this.value[0]时,如果其他线程改变了value数组,会导致读取的结果不一致,所以读方法通常也需要同步。

    public synchronized int[] get(){
int[] result = new int[2];
result[0] = this.value[0];
result[1] = this.value[1];//读取完result[0],如果其他线程改变了result[1]的值,会导致读取的结果不一致。
return result;
}

4.线程安全与非线程安全

4.1线程安全

如果一个类被设计为允许多线程正确访问, 这个类就是“线程安全”的(thread-safe),如java.lang.StringBuffer,其方法全部是用synchronized修饰的。



线程安全的类:

  • 不变类:String,Integer,LocalDate。不变类因为一旦创建,实例内部的成员变量无法改变,所以多线程只能读,不能写,不需要同步,就是安全的。
  • 没有成员变量的类:Math,这些工具类只提供了静态方法,没有成员变量。所以也是线程安全的。
  • 正确使用synchronized的类:StringBuffer

4.2 其他的类都是非线程安全的类:

  • 不能在多线程中共享实例并修改:ArrayList
  • 可以在多线程中以只读方式共享

5.总结:

  • 用synchronized修饰方法可以把整个方法变为同步代码块
  • synchronized方法加锁对象是this
  • 通过合理的设计和数据封装可以让一个类变为线程安全
  • 一个类没有特殊说明,默认不是thread-safe
  • 多线程能否访问某个非线程安全的实例,需要具体问题具体分析

最新文章

  1. Js的语法和循环
  2. 【zepto学习笔记02】零碎点
  3. HTML 通知公告练习
  4. 刨根问底U3D---如何退出Play模式后保留数据更改
  5. ahjesus 获取当前方法被调用执行的具体位置,包括命名空间和方法
  6. 随机四则运算 C语言
  7. ZigBee profile
  8. Tomcat 启动 Debug模式
  9. python优秀库 - 使用xmltodict解析xml文档
  10. Android SurfaceView实现静态于动态画图效果
  11. 用CATransform3D实现3D效果和制作简单3D动画
  12. Python生产环境部署(fastcgi,uwsgi)
  13. [LeetCode] Coin Change 2 硬币找零之二
  14. 【CF1151F】Sonya and Informatics(动态规划,矩阵快速幂)
  15. C#实现邮件发送的功能
  16. noip-2006普及组-数列- 【模拟-找规律-快速幂】
  17. Go语言实践_实现一(服务器端)对多(客户端)在线聊天室
  18. 通过my.ini修改mysql默认编码为gbk
  19. 最新Java校招面试题及答案
  20. 【转】【C#】实现依赖注入

热门文章

  1. 分类-回归树模型(CART)在R语言中的实现
  2. JVM内核-原理、诊断与优化学习笔记(十):Class文件结构
  3. JVM内核-原理、诊断与优化学习笔记(六):类装载器
  4. 『BASH』——Learn BashScript from Daniel Robbins——[001-002]
  5. Spring 源码学习——注册 BeanDefinition
  6. -bash: make: command not found
  7. Linux CPU负载状态:%us/%sy/%ni/%id/%wa/%hi/%si/%st含义
  8. arm-linux-readelf 的使用
  9. linux下svn 客户端使用方式
  10. jupyter|魔法函数问题| UsageError: Line magic function `%` not found