同步方法
同步方法接近于临界区的概念,同一时间只能运行一个线程,一个类中所有的同步方法同一时间内只能有一个线程在一个方法体内执行。例如:
public class Resource {
public synchronized String get(){
//..........
return null;
}
public synchronized void put(String str){
//..........
}
}
当一个线程执行get()时,其他线程既不能执行get(),也不能执行put()。
同步块
同步会造成系统性能的下降,为缩小同步的范围,java提供同步块功能。
public class Resource {
public String get(){
//..........
Synchronized(this){
//………….
}
return null;
}
public void put(String str){
//..........
Synchronized(this){
//………….
}
}
}
同步块与同步方法的意义是一样的,但同步块应用范围更广,使用起来更灵活。同步块类似于锁,可以把synchronized(this)中的this当成一个锁,进入同步块上锁,离开同步块时解锁。使用同一个锁的同步块同一时间内只能有一个同步块被执行。如上面的示例,get中的同步块被执行时,其他的线程既不能进入get同步块,也不能进入put同步块。
如果在Synchronized后面使用不同的对象,则相当于使用多个锁,只有使用相同锁的同步块不能同时执行,使用不同锁得同步块可以同时执行。比如:
private static Object obj1 = new Object();
private static Object obj2 = new Object();
public void test1() {
System.out.println("test1 enter");
synchronized (obj1) {
for (int i = 0; i < 4; i++) {
System.out.println("test1 excute");
try {
Thread.sleep(500);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
System.out.println("test1 exit");
}
public void test2() {
System.out.println("test2 enter");
synchronized (obj2) {
for (int i = 0; i < 4; i++) {
System.out.println("test2 excute");
try {
Thread.sleep(400);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
System.out.println("test2 exit");
}
以上代码执行结果
test1 enter
test1 excute
test2 enter
test2 excute
test2 excute
test1 excute
test2 excute
test1 excute
test2 excute
test1 excute
test2 exit
test1 exit
可以看到
test1
与
test2
是交互执行的。也就是说同时不会有两个线程在
test1
或是在
test2
中执行,但同时可以一个线程执行
test1
,一个线程执行
test2
。
等待和通知方法
在
JDK1.5
之前,
Java
提供的主要线程同步技术只有等待
/
通知函数配合同步方法或同步块,
suspend
或
resume
早就不提倡使用。
Wait
()和
notify
()
/notifyAll
()是
java.lang.Object
类的方法,
java
中任一个类都是
Object
的子类,换句话说,
Java
中任一个类都提供
wait()notify()/notifyAll()
方法。我们来看一下等待通知函数的使用。
我们以一个经典的多线程场景“生产者
/
消费者”来说明如何使用等待通知函数
1、
等待通知函数只能应用在同步方法体或同步块内。也就是说,调用某个对象的
wait()notify()
方法前必须获得这个对象的监视器(
monitor
)
2、
调用某个对象的
Wait()
方法时,相当于把当前线程放到这个对象的等待区中(可以假想这是对象的一个
Collection
类型的属性,把线程对象放到这个
Collection
中),然后挂起这个线程,最后释放这个对象的监视器。
Notify()/notifyAll()
方法则刚好相反,从对象的等待区中取出线程,恢复,最后释放这个对象的监视器。
Notify()notifyAll()
的区别是
notify
从等待区中拿出一个线程并且激活,而
notifyAll
是从等待区中拿出所有线程并且激活。
使用同步块和等待通知方法的“生产者/消费者”示例
public
class
SynchronizedSample
...
{
//模拟资源
private List<String> resource=new ArrayList<String>();
volatile int i=0;
boolean exit = false;
public SynchronizedSample()...{
Thread consumer = new Thread(new Consumer());
Thread producer = new Thread(new Producer());
consumer.start();
producer.start();
}
public void exit()...{
this.exit = true;
}
public static void main(String[]args)...{
SynchronizedSample sample = new SynchronizedSample();
try ...{
Thread.currentThread().sleep(2000);
} catch (InterruptedException e) ...{
return;
}
sample.exit();
}
public class Consumer implements Runnable ...{
public void run() ...{
while (!exit) ...{
synchronized (resource) ...{
while (resource.isEmpty()) ...{
try ...{
//调用resource的等待方法
resource.wait();
} catch (InterruptedException e) ...{
return;
}
}
System.out.println("Consumer take: "+resource.remove(0));
//调用resource的通知方法
resource.notifyAll();
}
}
}
}
public class Producer implements Runnable ...{
public void run() ...{
while (!exit) ...{
synchronized (resource) ...{
while (resource.size() >= 10) ...{
try ...{
resource.wait();
} catch (InterruptedException e) ...{
return;
}
}
String r = "Resource"+i++;
System.out.println("Producer put: "+r);
resource.add(r);
resource.notifyAll();
}
}
}
}
}
//模拟资源
private List<String> resource=new ArrayList<String>();
volatile int i=0;
boolean exit = false;
public SynchronizedSample()...{
Thread consumer = new Thread(new Consumer());
Thread producer = new Thread(new Producer());
consumer.start();
producer.start();
}
public void exit()...{
this.exit = true;
}
public static void main(String[]args)...{
SynchronizedSample sample = new SynchronizedSample();
try ...{
Thread.currentThread().sleep(2000);
} catch (InterruptedException e) ...{
return;
}
sample.exit();
}
public class Consumer implements Runnable ...{
public void run() ...{
while (!exit) ...{
synchronized (resource) ...{
while (resource.isEmpty()) ...{
try ...{
//调用resource的等待方法
resource.wait();
} catch (InterruptedException e) ...{
return;
}
}
System.out.println("Consumer take: "+resource.remove(0));
//调用resource的通知方法
resource.notifyAll();
}
}
}
}
public class Producer implements Runnable ...{
public void run() ...{
while (!exit) ...{
synchronized (resource) ...{
while (resource.size() >= 10) ...{
try ...{
resource.wait();
} catch (InterruptedException e) ...{
return;
}
}
String r = "Resource"+i++;
System.out.println("Producer put: "+r);
resource.add(r);
resource.notifyAll();
}
}
}
}
}