同步的关键是多个线程对象竞争同一个共享对象,使无序的多个线程有序地排队。
Example 1:
public class Thread06_sync {
public static void main(String[] args) {
// new Thread06_sync().test01();
new Thread06_sync().new Test02(new Object()).test02();
}
// 同步失败,原因:不是同步同一个对象
public void test01() {
for (int i = 1; i <= 3; ++i) {
new Thread(new Runnable() {
public synchronized void run() {
for (int i = 1; i <= 5; ++i) {
System.out.println("id:"
+ Thread.currentThread().getId() + ",i:" + i);
}
}
}).start();
}
}
// 同步成功,原因:同步同一个对象
public class Test02 {
private Object thisThread;
public Test02(Object thisThread) {
this.thisThread = thisThread;
}
public void test02() {
for (int i = 1; i <= 3; ++i) {
new Thread(new Runnable() {
public void run() {
synchronized (thisThread) {
for (int i = 1; i <= 5; ++i) {
System.out.println("id:"
+ Thread.currentThread().getId() + ",i:" + i);
}
}
}
}).start();
}
}
}
}
第一个例子,如果不同的synchronized object, 则不能正确lock住代码片段。
Example 2:
public class Thread04_static {
// volatile 只是可见一致性,非原子性(也就是无写锁),仍然有机会有重复uniqueNumber的输出
public volatile static long uniqueNumber = 0;
static int threadNumber = 5;
static AtomicInteger testNum = new AtomicInteger(0);
static List<Long> resultList = new ArrayList<>(300);
private final static ReentrantLock lock = new ReentrantLock();
public static void main(String[] args) {
testThread01();
}
// CountDownLatch
public static void testThread01() {
try {
CountDownLatch doneSignal = new CountDownLatch(50);
for (int i = 0; i < threadNumber; i++) {
Thread thread = new Thread(new Thread04_static().new TestThread01(doneSignal));
thread.setName("t_" + i);
thread.start();
}
// 由于thread的无序性,不加await的话, 有可能运行下面语句时thread还没运行,resultList有可能是空值
doneSignal.await();
int i = 1;
for (Long result : resultList) {
System.out.println("result_" + i + ":" + result);
++i;
}
} catch (Exception e) {
e.printStackTrace();
}
}
// 静态类及非静态同步、锁测试
public class TestThread01 implements Runnable {
private CountDownLatch doneSignal;
public TestThread01(CountDownLatch doneSignal) {
this.doneSignal = doneSignal;
}
public void run() {
try {
// 第一个同步block/ReentrantLock:这里的同步无预期效果(即有重复的uniqueNumber出现),因为getUniqueNum是类静态方法
// synchronized (this) {
// lock.lock();
for (int i = 0; i < 10; i++) {
// System.out.println(testNum.addAndGet(1));
// 有重复的testNum出现, 原因:testNum是原子的,但下面的语句不原子
// System.out.println("Thread:" + Thread.currentThread().getName() + ", testNum:" + testNum + ", i:" + i);
resultList.add(getUniqueNum(2));
doneSignal.countDown();
}
// 空行不一定每十条记录输出一次,因为它不在下面的静态同步block
System.out.println("");
Thread.sleep(1000);
// }
} catch (Exception e) {
e.printStackTrace();
} finally {
// lock.unlock();
}
}
}
public static Long getUniqueNum(int len) {
// 第二个同步block/ReentrantLock:静态类同步block/lock,实现无重复uniqueNumber输出
// synchronized (TestThread01.class) {
try{
lock.lock();
if (len < 1) {
return null;
}
if (uniqueNumber < (Math.pow(10, len) - 1)) {
uniqueNumber++;
}
System.out.println("Thread:" + Thread.currentThread().getName()
+ ", uniqueNumber:" + uniqueNumber);
return uniqueNumber;
} finally {
lock.unlock();
}
// }
}
}
第二个例子,static 和 非static的不同,也会导致lock的失败。