廖雪峰Java11多线程编程-2线程同步-2synchronized方法
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
- 多线程能否访问某个非线程安全的实例,需要具体问题具体分析
廖雪峰Java11多线程编程-2线程同步-2synchronized方法的更多相关文章
- 廖雪峰Java11多线程编程-2线程同步-3死锁
1.线程锁可以嵌套 在多线程编程中,要执行synchronized块: 必须首先获得指定对象的锁 Java的线程锁是可重入的锁.对同一个对象,同一个线程,可以多次获取他的锁,即同一把锁可以嵌套.如以下 ...
- 廖雪峰Java11多线程编程-2线程同步-4wait和notify
wait和notify synchronized解决了多线程竞争的问题 我们可以在synchronized块中安全的对一个变量进行修改,但是它没有解决多线程协调的问题. 例如设计一个TaskQueue ...
- 廖雪峰Java11多线程编程-2线程同步-1同步代码块
1.线程安全问题 多个线程同时运行,线程调度由操作系统决定,程序本身无法决定 如果多个线程同时读写共享变量,就可能出现问题 class AddThread extends Thread{ public ...
- 廖雪峰Java11多线程编程-1线程的概念-1多线程简介
多任务 现代操作系统(windows,MacOS,Linux)都可以执行多任务: 多任务就是同时运行多个任务,例如同时开启钉钉.百度网盘.火狐.谷歌.ps等 操作系统执行多任务就是让多个任务交替执行, ...
- 廖雪峰Java11多线程编程-1线程的概念-2创建新线程
Java语言内置多线程支持: 一个Java程序实际上是一个JVM进程 JVM用一个主线程来执行main()方法 在main()方法中又可以启动多个线程 1.创建新线程 1.1 方法一:使用Thread ...
- 廖雪峰Java11多线程编程-1线程的概念-3线程的状态
1线程的状态 线程终止的的原因: run()或call()方法执行完成,线程正常结束 线程抛出一个未捕获的Exception或Error 直接调用该线程的stop()方法来结束该线程--该方法容易导致 ...
- 廖雪峰Java11多线程编程-4线程工具类-1ThreadLocal
多线程是Java实现多任务的基础: Thread ExecutorService ScheduledThreadPool Fork/Join Thread对象代表一个线程:调用Tread.curren ...
- 廖雪峰Java11多线程编程-1线程的概念-5中断线程
1.中断线程: 如果线程需要执行一个长时间任务,就可能需要中断线程.场景:从网络上下载一个100M的文件,用户在下载过程中中断下载任务的执行. 中断线程就是其他线程给该线程发一个信号,该线程收到信号后 ...
- 廖雪峰Java11多线程编程-3高级concurrent包-4Concurrent集合
Concurrent 用ReentrantLock+Condition实现Blocking Queue. Blocking Queue:当一个线程调用getTask()时,该方法内部可能让给线程进入等 ...
随机推荐
- (转)VS2010-MFC编程入门教程之目录和总结
目前该教程可以到鸡啄米编程课堂去学习,阅读体验更好,更适合在线学习. 原文目录及链接: 一.VS2010/MFC编程入门教程之目录 第一部分:VS2010/MFC开发环境 VS2010/MFC编程入 ...
- Aliyun 安装NPM 总是3.5.2 解决方案
由于默认的命令 阿里云安装的 Node 是 8.x 版本 导致NPM 一直安装的都是 3.5.2 版本,死活升级不上去 最后手动安装指定版本解决 wget -qO- https://deb.nodes ...
- 记录一次像github开源项目提交pull request(Hexo Next)
文章目录 背景 fork到自己github 像往常一样的操作 克隆到本地 与上游建立连接 创建分支 修改项目代码 收尾工作 提交pull request 个人博客:https://mmmmmm.me ...
- Caused by: java.sql.SQLSyntaxErrorException: ORA-00932: 数据类型不一致: 应为 NUMBER, 但却获得 BINARY
at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvo ...
- jQuery 表单域选中选择器
复选框.单选按钮.下拉列表 /***********************************************/ <script type="text/javascrip ...
- 基于三角形与位置指纹识别算法的WiFi定位比较
文章来着:https://wenku.baidu.com/view/55d1f4146edb6f1aff001fec.html
- python学习4—数据结构之列表、元组与字典
python学习4—数据结构之列表.元组与字典 列表(list)深灰魔法 1. 连续索引 li = [1,1,[1,["asdsa",4]]] li[2][1][1][0] 2. ...
- 【转】Java程序CPU飙升问题排查方法
windows环境下cpu飙升问题 线上某台runtime机器(windows Server)cpu报警,这种情况初步就是代码里面死循环了,先把机器下线了保证不再有新的任务分配进来,然而cpu使用依然 ...
- JS对象 返回星期方法 getDay() 返回星期,返回的是0-6的数字,0 表示星期天。如果要返回相对应“星期”,通过数组完成
返回星期方法 getDay() 返回星期,返回的是0-6的数字,0 表示星期天.如果要返回相对应"星期",通过数组完成,代码如下: <script type="te ...
- pd.Panel转化成json,然后再还原回来
在使用tornado的write时候有一个需求,是将panel转化成json;而接收端再将json还原成panel格式. 尝试了很久,终于实现了. panel1 =pd.Panel({"on ...