今天本来写点其他东西,碰巧写了一下synchronized,没想到掉坑里面了,大佬别笑。
起初代码大概是这样的:
package com.ripplechan.part_1_2_3;
import java.util.concurrent.CountDownLatch;
/**
* @author RippleChan
* @date 2018-09-20
* @time 18:05
*/
public class DataTest {
public static void main(String[] args) throws InterruptedException {
int count = 10000;
CountDownLatch countDownLatch = new CountDownLatch(count * 1);
Cal cal = new Cal(countDownLatch);
for (int i = 0; i < count; i++) {
new Thread(cal).start();
}
countDownLatch.await();
System.out.println(cal.getSum());
}
}
class Cal extends Thread {
private Long sum = 0L;
CountDownLatch countDownLatch;
public Cal(CountDownLatch countDownLatch) {
this.countDownLatch = countDownLatch;
}
@Override
public void run() {
synchronized (sum) {
try {
this.sum = sum + 1;
} finally {
countDownLatch.countDown();
}
}
}
public Long getSum() {
return sum;
}
}
你觉得答案是多少?为什么会这样?如何避免这种情况?
看下如下代码,相信你会明白为什么了。
public class DatSalfByObject {
public static void main(String[] args) throws InterruptedException {
Long a = 1L;
int i = System.identityHashCode(a);
System.out.println(i);
a = a + 1L;
int i1 = System.identityHashCode(a);
System.out.println(i1);
}
}
这里做个解释吧,this.sum = sum + 1;后,sum还是以前的sum么?为什么会这样?关键词,不可变对象。通过System.identityHashCode也看出来了,变了。既然对象都变了,sychronized能锁住么?锁的是之前的对象,没有丝毫意义。
通过某大佬的指点,我得出一下集中方案:
1.直接锁this(锁方法和锁this本质一样);
2.将需要修改的对象再次封装;
方案1:
class MyCal5 extends Thread {
private Long sum = 0L;
CountDownLatch countDownLatch;
public MyCal5(CountDownLatch countDownLatch) {
this.countDownLatch = countDownLatch;
}
@Override
public void run() {
try {
synchronized (this) {
this.sum = sum + 1;
}
}finally {
countDownLatch.countDown();
}
}
public Long getSum() {
return sum;
}
}
方案2:
@Data
class User {
private Long id = 0L;
}
class UserThread extends Thread {
private User user;
private CountDownLatch countDownLatch;
public UserThread(User user,CountDownLatch countDownLatch) {
this.user = user;
this.countDownLatch = countDownLatch;
}
@Override
public void run() {
try {
synchronized (user) {
Long id = user.getId();
user.setId(id + 1);
}
}finally {
countDownLatch.countDown();
}
}
}
最终,因为锁this消耗太大,所以还是推荐锁对象,但如何避免对象被修改呢?是考验JavaSE基础的时候了,代码如下:
package com.ripplechan.part_1_2_3;
import lombok.Data;
import java.util.concurrent.CountDownLatch;
/**
* @author RippleChan
* @date 2018-09-20
* @time 17:38
*/
public class DatSalfByObject {
public static void main(String[] args) throws InterruptedException {
int count = 10000;
User user = new User();
CountDownLatch countDownLatch = new CountDownLatch(count);
UserThread userThread = new UserThread(user, countDownLatch);
for (int c = 0; c < 10000; c++) {
new Thread(userThread).start();
}
countDownLatch.await();
System.out.println(user.getId());
}
}
@Data
class User {
private Long id = 0L;
}
class UserThread extends Thread {
private final User user;
private final CountDownLatch countDownLatch;
public UserThread(User user,CountDownLatch countDownLatch) {
this.user = user;
this.countDownLatch = countDownLatch;
}
@Override
public void run() {
try {
synchronized (user) {
Long id = user.getId();
user.setId(id + 1);
}
}finally {
countDownLatch.countDown();
}
}
}
对,finnal修饰,哈哈,这是一把防止出错的锁。