synchronized 具有原子性,可见性。
原子性:由java内存模型来直接保证的原子性变量操作包括read,load,assign,use,store,write,我们大致可以认为基本数据类型的访问读写是具备原子性的(例外就是long和double的非原子协定),如果应用场景需要一个更大范围的原子性保证,java内存模型还提供了lock和unlock的操作来满足这种需求,尽管jvm把lock操作和unlock操作直接开放给用户,但是却提供了更高层次的字节码指令monitorenter和monitorexit来隐式地使用这两个操作,这两个字节码指令反映到java代码之中就是同步块(synchronized),所以在synchronized块之间的操作也是具备有原子性的。
可见性 :可见性是指当一个线程修改了共享变量的值,其他线程能够立即得知这个修改。java内存模型是通过在变量修改后将新值同步回主内存,在变量读取前从主内存刷新变量值这种依赖主内存作为传递媒介的方式来实现可见性的,无论普通变量还是volatile变量都是如此,普通变量与volatile变量的区别在于:volatile的特殊规则保证了新值能立即同步回主内存,以及每次使用前立即从主内存刷新。因此,volatile保证了多线程操作时变量的可见性,而普通变量则不能保证。
来源自:https://www.aliyun.com/jiaocheng/1235.html
这个地方最好还是了解一下 java内存模型。
作用的范围:
1. 修饰代码块,大括号括起来的代码,作用于调用的对象。
2. 修饰方法,整个方法,作用于调用的对象。
3. 修饰静态方法,整个静态方法,作用于所有对象。
4. 修饰类,括号括起来的部分,作用于所有对象。
下面演示过程中所遇到的问题:
public void testSynchronizedMethodBlock() {
synchronized (this) {
for (int i =0; i <9; i++) {
log.info("test - 1 :{}", i);
}
}
}
/**
* 线程池的基本大小
*/
static int corePoolSize =10;
/**
* 线程池最大数量
*/
static int maximumPoolSizeSize =100;
/**
* 线程活动保持时间
*/
static long keepAliveTime =1;
/**
* 任务队列
*/
static ArrayBlockingQueueworkQueue =new ArrayBlockingQueue(10);
public static void main(String[] args) {
//推荐手动创建线程池
ThreadPoolExecutorexecutor =new ThreadPoolExecutor(
corePoolSize,
maximumPoolSizeSize,
keepAliveTime,
TimeUnit.SECONDS,
workQueue,
new ThreadPoolExecutor.AbortPolicy());
SynchronizedExample1 synchronizedExample1 =new SynchronizedExample1();
executor.execute(() -> {
synchronizedExample1.testSynchronizedMethodBlock();
});
executor.execute(()->{
synchronizedExample1.testSynchronizedMethodBlock();
});
}
在上面的方法执行完毕以后得到的输出答案是:
可以看到的两个线程分先后顺序执行了代码,没有出现乱序的情况。
那么怎么会出现乱序的情况? 可以参考上面所诉 作用范围 1
下面使用两个对象进行尝试 会不会出现乱序情况
public void testSynchronizedMethodBlock() {
synchronized (this) {
for (int i =0; i <9; i++) {
log.info("test - 1 :{}", i);
}
}
}
/**
* 线程池的基本大小
*/
static int corePoolSize =10;
/**
* 线程池最大数量
*/
static int maximumPoolSizeSize =100;
/**
* 线程活动保持时间
*/
static long keepAliveTime =1;
/**
* 任务队列
*/
static ArrayBlockingQueueworkQueue =new ArrayBlockingQueue(10);
public static void main(String[] args) {
//推荐手动创建线程池
ThreadPoolExecutorexecutor =new ThreadPoolExecutor(
corePoolSize,
maximumPoolSizeSize,
keepAliveTime,
TimeUnit.SECONDS,
workQueue,
new ThreadPoolExecutor.AbortPolicy());
SynchronizedExample1 synchronizedExample1 = new SynchronizedExample1();
SynchronizedExample1 synchronizedExample2 = new SynchronizedExample1();
executor.execute(() -> {
synchronizedExample1.testSynchronizedMethodBlock();
});
executor.execute(()->{
synchronizedExample2.testSynchronizedMethodBlock();
});
}
下面看输出情况:
上面是输出的情况,可以清晰的看到输出结果,也就是说有两个线程同时执行了代码。
说明synchronized加在方法块只作用于当前对象。
下面试下加在方法前是否也是只作用于当前对象,代码变化不大:
public synchronized void testSynchronizedMethod() {
for (int i =0; i <9; i++) {
log.info("test - Method :{}", i);
}
}
public static void main(String[] args) {
//推荐手动创建线程池
ThreadPoolExecutor executor = new ThreadPoolExecutor(
corePoolSize,
maximumPoolSizeSize,
keepAliveTime,
TimeUnit.SECONDS,
workQueue,
new ThreadPoolExecutor.AbortPolicy());
SynchronizedExample1 synchronizedExample1 = new SynchronizedExample1();
executor.execute(() -> {
synchronizedExample1.testSynchronizedMethod();
});
executor.execute(()->{
synchronizedExample1.testSynchronizedMethod();
});
}
可以看见上面我用了一个对象,线程池调用一个方法。我们看下输出
10:51:19.759 [pool-1-thread-1] INFO com.study.concurrency.example.SynchronizedExample1 - test - Method :0
10:51:19.762 [pool-1-thread-1] INFO com.study.concurrency.example.SynchronizedExample1 - test - Method :1
10:51:19.762 [pool-1-thread-1] INFO com.study.concurrency.example.SynchronizedExample1 - test - Method :2
10:51:19.762 [pool-1-thread-1] INFO com.study.concurrency.example.SynchronizedExample1 - test - Method :3
10:51:19.762 [pool-1-thread-1] INFO com.study.concurrency.example.SynchronizedExample1 - test - Method :4
10:51:19.762 [pool-1-thread-1] INFO com.study.concurrency.example.SynchronizedExample1 - test - Method :5
10:51:19.762 [pool-1-thread-1] INFO com.study.concurrency.example.SynchronizedExample1 - test - Method :6
10:51:19.762 [pool-1-thread-1] INFO com.study.concurrency.example.SynchronizedExample1 - test - Method :7
10:51:19.762 [pool-1-thread-1] INFO com.study.concurrency.example.SynchronizedExample1 - test - Method :8
10:51:19.762 [pool-1-thread-2] INFO com.study.concurrency.example.SynchronizedExample1 - test - Method :0
10:51:19.762 [pool-1-thread-2] INFO com.study.concurrency.example.SynchronizedExample1 - test - Method :1
10:51:19.762 [pool-1-thread-2] INFO com.study.concurrency.example.SynchronizedExample1 - test - Method :2
10:51:19.762 [pool-1-thread-2] INFO com.study.concurrency.example.SynchronizedExample1 - test - Method :3
10:51:19.763 [pool-1-thread-2] INFO com.study.concurrency.example.SynchronizedExample1 - test - Method :4
10:51:19.763 [pool-1-thread-2] INFO com.study.concurrency.example.SynchronizedExample1 - test - Method :5
10:51:19.763 [pool-1-thread-2] INFO com.study.concurrency.example.SynchronizedExample1 - test - Method :6
10:51:19.763 [pool-1-thread-2] INFO com.study.concurrency.example.SynchronizedExample1 - test - Method :7
10:51:19.763 [pool-1-thread-2] INFO com.study.concurrency.example.SynchronizedExample1 - test - Method :8
输出等一个线程执行完毕,另外线程才执行。 那如果是两个对象调用这一个方法呢?
直接上代码,测试结果:
public static void main(String[] args) {
//推荐手动创建线程池
ThreadPoolExecutorexecutor =new ThreadPoolExecutor(
corePoolSize,
maximumPoolSizeSize,
keepAliveTime,
TimeUnit.SECONDS,
workQueue,
new ThreadPoolExecutor.AbortPolicy());
SynchronizedExample1synchronizedExample1 =new SynchronizedExample1();
SynchronizedExample1synchronizedExample2 =new SynchronizedExample1();
executor.execute(() -> {
synchronizedExample1.testSynchronizedMethod();
});
executor.execute(()->{
synchronizedExample2.testSynchronizedMethod();
});
}
输出结果:
10:54:17.644 [pool-1-thread-2] INFO com.study.concurrency.example.SynchronizedExample1 - test - Method :0
10:54:17.644 [pool-1-thread-1] INFO com.study.concurrency.example.SynchronizedExample1 - test - Method :0
10:54:17.647 [pool-1-thread-2] INFO com.study.concurrency.example.SynchronizedExample1 - test - Method :1
10:54:17.647 [pool-1-thread-1] INFO com.study.concurrency.example.SynchronizedExample1 - test - Method :1
10:54:17.647 [pool-1-thread-2] INFO com.study.concurrency.example.SynchronizedExample1 - test - Method :2
10:54:17.648 [pool-1-thread-1] INFO com.study.concurrency.example.SynchronizedExample1 - test - Method :2
10:54:17.648 [pool-1-thread-1] INFO com.study.concurrency.example.SynchronizedExample1 - test - Method :3
10:54:17.648 [pool-1-thread-1] INFO com.study.concurrency.example.SynchronizedExample1 - test - Method :4
10:54:17.648 [pool-1-thread-2] INFO com.study.concurrency.example.SynchronizedExample1 - test - Method :3
10:54:17.648 [pool-1-thread-1] INFO com.study.concurrency.example.SynchronizedExample1 - test - Method :5
10:54:17.648 [pool-1-thread-1] INFO com.study.concurrency.example.SynchronizedExample1 - test - Method :6
10:54:17.648 [pool-1-thread-2] INFO com.study.concurrency.example.SynchronizedExample1 - test - Method :4
10:54:17.648 [pool-1-thread-1] INFO com.study.concurrency.example.SynchronizedExample1 - test - Method :7
10:54:17.648 [pool-1-thread-2] INFO com.study.concurrency.example.SynchronizedExample1 - test - Method :5
10:54:17.648 [pool-1-thread-1] INFO com.study.concurrency.example.SynchronizedExample1 - test - Method :8
10:54:17.648 [pool-1-thread-2] INFO com.study.concurrency.example.SynchronizedExample1 - test - Method :6
10:54:17.648 [pool-1-thread-2] INFO com.study.concurrency.example.SynchronizedExample1 - test - Method :7
10:54:17.648 [pool-1-thread-2] INFO com.study.concurrency.example.SynchronizedExample1 - test - Method :8
可以看到 两个线程之间输出的值又成了乱序 并没有等到一个线程执行完 另外一个线程才执行
上述的结果也证明了synchronized加在方法与加在代码块,确实只作用于当前调用对象。
下一篇将写加在静态方法前与修饰类。
本文仅限本人小白学习参考,不足之处请大佬指正。