synchronized使用
同步代码块:synchronized放在对象前面,限制一段代码的执行。锁可以是任意对象,但多个线程用的必须是同一把锁。
synchronized(锁对象){
需要同步的代码
}
同步方法:synchronized放在方法声明中,表示整个方法是同步方法。锁是this
public synchronized void method(){ }
静态同步方法:synchronized放在静态方法声明中,表示整个方法是同步方法。锁是字节码文件对象,类名.getclass()。
public static synchronized void method(){ }
同步特点:
好处:同步解决了多线程的安全问题
弊端:当线程很多时,每个线程都会判断同步上的锁,很耗费资源,降低程序的执行效率
容器类的线程安全:
可以用synchronized来锁住这个对象:
synchronized(list){
list.add(…);
}
可以使用java.uitl.Collections的synchronizedXXX()方法来返回一个同步化的容器对象
List list = Collections.synchronizedList(new ArrayList());
这种方式在迭代时仍要用synchronized修饰
List list = Collections.synchronizedList(new ArrayList());
...
synchronized(list) {
Iterator i = list.iterator();
while (i.hasNext()) {
foo(i.next());
}
}死锁
产生原因
有同步嵌套,两个线程都在等待对方已经锁定的资源。
public class TestDead {
public static void main(String[] args) {
RichMan man = new RichMan();
man.setName("富翁");
Kidnapper napper = new Kidnapper();
napper.setName("绑匪");
man.start();
napper.start();
}
}
class Lock {
public static Object obj1 = new Object();
public static Object obj2 = new Object();
}
//描述富翁
class RichMan extends Thread{
@Override
public void run() {
synchronized (Lock.obj1) {
System.out.println("富翁说:你放了我儿子");
try {
Thread.sleep(10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
synchronized (Lock.obj2) {
System.out.println("富翁说:我给你1000万");
}
}
}
}
//描述绑匪
class Kidnapper extends Thread{
@Override
public void run() {
synchronized (Lock.obj2) {
System.out.println("绑匪说:你给我1000万");
try {
Thread.sleep(10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
synchronized (Lock.obj1) {
System.out.println("绑匪说:我放了你儿子");
}
}
}
}如何避免死锁:
一个通用的经验法则是:当几个线程都要访问共享资源(锁)A、B、C时,保证使每个线程都按照同样的顺序去访问它们,比如都先访问A,再访问B和C。
此外,Thread类的suspend()方法也很容易导致死锁,因此这个方法已经被废弃了.
线程的等待与唤醒(生产者与消费者问题)
notify(),wait()等是Object中方法,因为锁是任意对象,所有这么方法通过锁来使用
wait和sleep的区别:
1、sleep任何时候都可以调用,但是wait只能用在同步中。
2、sleep可以在规定时间内醒过来,但是wait需要唤醒,等待的时间是不确定。
3、sleep睡眠中不会交出锁,但是wait会交出同步锁。
生产者和消费者模型
package com.qianfeng.producecustomer;
/*
* 生产者生产6个,消费者6个
* 包子。
* 一个生产者,一个消费者。
* String
* StringBuffer:同步的。效率不高
* StringBuilder:不同步的。效率高。很多时候是单线程。
* 扩展:
* 线程池的概念。
* 作业:
* 1:将生产多个和消费多个,改成集合和数组来实现。
*
*/
public class TestProduce2 {
public static void main(String[] args) {
MyCustomer customer = new MyCustomer();
customer.start();
MyProduce produce = new MyProduce();
produce.start();
}
}
class MyBaozi{
public static int count = 0;
public static final Integer SUM = 6;//总数。
}
class MyProduce extends Thread{
@Override
public void run() {
while(true){
//先判断。满了就等待。
synchronized (MyBaozi.SUM) {
if(MyBaozi.count>=MyBaozi.SUM){
//等待
try {
MyBaozi.SUM.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
//否则
MyBaozi.count++;
try {
Thread.sleep((int)(Math.random()*100));
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("生产者生产了一个包子,现有"+MyBaozi.count+"个包子");
MyBaozi.SUM.notify();
}
}
}
}
class MyCustomer extends Thread{
@Override
public void run() {
while(true){
synchronized (MyBaozi.SUM) {
if(MyBaozi.count==0){
try {
MyBaozi.SUM.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
//消费
MyBaozi.count--;
try {
Thread.sleep((int)(Math.random()*100));
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("消费者消费了一个包子,现有"+MyBaozi.count+"个包子");
MyBaozi.SUM.notify();
}
}
}
}