public class ListHelper {
public List list = Collections
.synchronizedList(new ArrayList());
// private List list = Collections.synchronizedList(new ArrayList());
public synchronized boolean put(Integer i) {
list.add(i);
System.out.println(Thread.currentThread().getName() + " --- " + i);
return true;
}
}
2)ListHepler的方法都用了synchronized来进行加锁,用来同步。
3)注意list变量的访问权限是public!
public class A extends Thread {
private ListHelper lh;
public A(ListHelper lh) {
super("A");
this.lh = lh;
}
public void run() {
synchronized (lh) {
for (int i = 0; i < 9; i++) {
lh.put(i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
客户端的代码:
public class Client {
public static void main(String args[]) throws InterruptedException{
ListHelper lh = new ListHelper();
A a = new A(lh);
B b = new B(lh);
a.start();
b.start();
}
}
运行结果如下:
从运行结果来看,一切都是那么顺利,当线程A执行的时候,B阻塞;然后A执行完毕释放锁,B获取锁并运行。看起来很安全的样子。
但是下面在提供一个thread C:
public class C extends Thread {
private ListHelper lh;
public C(ListHelper lh){
super("C");
this.lh = lh;
}
public void run() {
synchronized (lh.list) {//添加了list锁
for (int i = 0; i < 9; i++) {
System.out.println(Thread.currentThread().getName() + "------" + i);
lh.list.add(i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
public static void main(String args[]) throws InterruptedException{
ListHelper lh = new ListHelper();
A a = new A(lh);
B b = new B(lh);
C c = new C(lh);
a.start();
b.start();
c.start();
}
A --- 0
C------0
C------1
C------2
C------3
C------4
C------5
C------6
C------7
C------8
A --- 1
A --- 2
A --- 3
A --- 4
A --- 5
A --- 6
A --- 7
A --- 8
B --- 0
B --- 1
B --- 2
B --- 3
B --- 4
B --- 5
B --- 6
B --- 7
B --- 8
发现线程A在执行的时候,由于A获取了ListHelper的锁,导致B线程的阻塞,当线程A在执行完的时候释放锁,然后B获取锁得到执行。但是有如下问题出现了
1)变量list不是被Collectoion.synchronizedList加过锁了么?
2)在A获取锁并执行的时候C怎么可以执行呢?
3)并且A只执行了一次put方法之后等着C执行完毕后才得到运行呢?
解答:
A获取的是ListHelper对象锁,而Collection.synchronizedList为list添加的锁是另外一个锁,也就是说两个锁不是一回事儿。所以问题3就可以迎刃而解了:
1)A执行一次put方法,释放了list锁。此时A仍然拥有ListHelper锁,B在等待获取ListHelper锁,所以B仍然阻塞
2)C获取到了list锁执行完for循环并释放list锁,A得到list锁并运行完for循环,释放ListHelper锁。
3)B得到ListHelper锁,并运行完毕,程序退出
所以A线程获取ListHelper对象锁执行的并执行的时候是没法阻塞C线程的执行的,除非A线程也获取了list上的锁!!!
所以可以把A和B的run方法代码改成:
public void run() {
synchronized (lh) {
synchronized (lh.list) {//添加了list锁
for (int i = 0; i < 9; i++) {
lh.put(i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
当然要把ListHelper设置成线程安全的类,最简单的是不发布list变量,可以把list的改成private同时不提供getList()方法,不过这样的话Collection.synchronizedList就失去了它的作用。怎么在public访问权限不变的情况下,只利用Collection.synchronizedList提供的锁来控制线程同步的呢?上面的代码A和B明显用到了两个锁,一个是ListHelper锁,另一个是Collection.synchronizedList提供的锁,其实完全可以利用后者而不用ListHelper锁来完成上述的更能。
修改ListHelper代码如下:
public class ListHelper {
public List list = Collections.synchronizedList(new ArrayList());
// private List list = Collections.synchronizedList(new ArrayList());
public boolean put(Integer i) {
synchronized(list){//在list上加锁
list.add(i);
System.out.println(Thread.currentThread().getName() + " --- " + i);
return true;
}
}
}
public void run() {
//synchronized (lh) {
synchronized (lh.list) {
for (int i = 0; i < 9; i++) {
lh.putIfAbsent(i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
//}
}