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();
}
}
}
}