廖雪峰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()时,该方法内部可能让给线程进入等 ...
随机推荐
- 好文 | MySQL 索引B+树原理,以及建索引的几大原则
Java技术栈 www.javastack.cn 优秀的Java技术公众号 来源:小宝鸽 blog.csdn.net/u013142781/article/details/51706790 MySQL ...
- 《转》python基础下
转自http://www.cnblogs.com/BeginMan/archive/2013/04/12/3016323.html 一.数字 在看<Python 核心编程>的时候,我就有点 ...
- 解决OCX 在 非开发电脑上注册出错的问题
这几天遇到一个问题,就是在我自己电脑上开发的OCX 放在其他电脑上居然注册失败,管理员运行也不行,老是会蹦出这样的错误,最后呢终于让我找到一个线索就是在开发电脑上可以安装,在无开发环境上很大概率安装失 ...
- WinDbg神断点
https://blogs.msdn.microsoft.com/alejacma/2007/10/31/cryptoapi-tracer-script/ 我得多少年才能学会这种写法.
- centos安装与配置R语言
Linux下安装R语言 一.编译安装 由于采用编译安装,所以需要用到gcc编译环境,在编译前check文件时还会用到libXt-devel和readline-devel两个依赖,所以在编译R语言源码时 ...
- mac NTFS 关于错误-36,rm Input/output error
当传输文件时出现Mac错误代码36 当我向一个U盘或存储卡里传输文件时,出现了Mac Error Code36,我该如何解决? Mac,传输文件,错误代码36,U盘,卡片 Mac OS X Snow ...
- 随笔记录 grub引导故障修复 2019.8.7
系统备份: [root@localhost ~]# mkdir /abc [root@localhost ~]# mount /dev/sdb1 /abc [root@localhost ~]# dd ...
- 关于用Linux桌面版当工作系统这件事
Linux稳定性好,Linux软件开放--不过等到决定把Linux当作日常工作用系统时,就一言难尽了-- 我日常工作的需求有: 笔记本扩展屏幕 Golang开发 docker/kubernetes 输 ...
- Java 基础 - 比较方式选择(什么类型用equals()比较,什么类型用==比较)
ref: https://www.cnblogs.com/lori/p/8308671.html 在 java 中进行比较,我们需要根据比较的类型来选择合适的比较方式: 对象域,使用 equals 方 ...
- 最大流任务调度+离散化——hdu2883
思想就是把时间段离散化,然后用个点来表示一段时间 #include<iostream> #include<cstdio> #include<cstring> #in ...