synchronized 解析使用
阿新 • • 發佈:2019-01-08
java鎖分為物件鎖,類鎖,
synchronized就是熟知的程式碼同步塊。
加鎖一個方法塊,根據鎖的機制上鎖的只是這個物件,
package hw.lock; /** * * * @author huangwei * 2017 10 20 */ public class Producer extends Thread{ int num=1; public synchronized int count() { while(num<10) { System.out.println(Thread.currentThread() +" "+num++);//輸出當前執行緒 } try { Thread.sleep(500); }catch (Exception e) { // TODO: handle exception } return num; } public void run() { count(); } public static void main(String[] args) { // TODO Auto-generated method stub Producer aProducer=new Producer(); Producer bProducer=new Producer(); aProducer.start(); bProducer.start(); } }
輸出為這樣
Thread[Thread-1,5,main] 1 Thread[Thread-0,5,main] 1 Thread[Thread-0,5,main] 2 Thread[Thread-0,5,main] 3 Thread[Thread-0,5,main] 4 Thread[Thread-0,5,main] 5 Thread[Thread-0,5,main] 6 Thread[Thread-0,5,main] 7 Thread[Thread-0,5,main] 8 Thread[Thread-0,5,main] 9 Thread[Thread-1,5,main] 2 Thread[Thread-1,5,main] 3 Thread[Thread-1,5,main] 4 Thread[Thread-1,5,main] 5 Thread[Thread-1,5,main] 6 Thread[Thread-1,5,main] 7 Thread[Thread-1,5,main] 8 Thread[Thread-1,5,main] 9
說明如果只是對方法上鎖的話,那麼實際上會對當前的物件上鎖,而我們實現了兩個物件,所以並沒有實現同步。
package hw.lock; /** * * * @author huangwei * 2017 10 20 */ public class Producer extends Thread{ int num=1; public int count() { synchronized (this) { while(num<10) { System.out.println(Thread.currentThread() +" "+num++);//輸出當前執行緒 } try { Thread.sleep(500); }catch (Exception e) { // TODO: handle exception } } return num; } public void run() { count(); } public static void main(String[] args) { // TODO Auto-generated method stub Producer aProducer=new Producer(); Producer bProducer=new Producer(); aProducer.start(); bProducer.start(); } }
對當前物件進行上鎖 輸出結果依舊為沒有上鎖的樣子
Thread[Thread-0,5,main] 1
Thread[Thread-1,5,main] 1
Thread[Thread-0,5,main] 2
Thread[Thread-1,5,main] 2
Thread[Thread-0,5,main] 3
Thread[Thread-0,5,main] 4
Thread[Thread-0,5,main] 5
Thread[Thread-0,5,main] 6
Thread[Thread-0,5,main] 7
Thread[Thread-0,5,main] 8
Thread[Thread-0,5,main] 9
Thread[Thread-1,5,main] 3
Thread[Thread-1,5,main] 4
Thread[Thread-1,5,main] 5
Thread[Thread-1,5,main] 6
Thread[Thread-1,5,main] 7
Thread[Thread-1,5,main] 8
Thread[Thread-1,5,main] 9
那對於兩個不同的執行緒物件 我們要上鎖的其實就是公有的屬性,或者說就是靜態屬性。
package hw.lock;
/**
*
*
* @author huangwei
* 2017 10 20
*/
public class Producer extends Thread{
static int num=1;
public int count() {
synchronized (Producer.class) {
while(num<10) {
System.out.println(Thread.currentThread() +" "+num++);//輸出當前執行緒
}
try {
Thread.sleep(500);
}catch (Exception e) {
// TODO: handle exception
}
}
return num;
}
public void run() {
count();
}
public static void main(String[] args) {
// TODO Auto-generated method stub
Producer aProducer=new Producer();
Producer bProducer=new Producer();
aProducer.start();
bProducer.start();
}
}
Thread[Thread-1,5,main] 1
Thread[Thread-1,5,main] 2
Thread[Thread-1,5,main] 3
Thread[Thread-1,5,main] 4
Thread[Thread-1,5,main] 5
Thread[Thread-1,5,main] 6
Thread[Thread-1,5,main] 7
Thread[Thread-1,5,main] 8
Thread[Thread-1,5,main] 9
如果是兩個物件操作靜態變數,對XX.class進行上鎖就可以實現,對類上鎖同步靜態變數方法以及常量。
對於靜態方法的synchronized就相當於給這個方法所在的這個記憶體塊添加了一個監視器,
在類載入的時候,類的基本資訊和一些常量和今天變數都儲存在jvm記憶體的方法區,
對synchronized(XX.class) 就等於對這個類的所有資訊加上 監視器,一個執行緒進行訪問計數+1,釋放-1,不為0就一直阻塞其他執行緒訪問。
而類初始化的例項物件則放在了jvm記憶體的堆區
而對例項物件的上鎖就相當於監視這個例項物件,而不同的物件在不同塊 並不能實現同步。
對於同一個物件例項
未上鎖時
package hw.lock;
/**
*
*
* @author huangwei
* 2017 10 20
*/
public class Producer {
int num=1;
public int count() {
while(num<10) {
System.out.println(Thread.currentThread().getName() +" "+num++);//輸出當前執行緒
}
try {
Thread.sleep(1000);
}catch (Exception e) {
// TODO: handle exception
}
return num;
}
public static void main(String[] args) {
// TODO Auto-generated method stub
final Producer aProducer=new Producer();
//Producer bProducer=new Producer();
Thread test1=new Thread(new Runnable() {
@Override
public void run() {
aProducer.count();
}
});
Thread test2=new Thread(new Runnable() {
@Override
public void run() {
aProducer.count();
}
});
test1.start();
test2.start();
//bProducer.start();
}
}
Thread-0 1
Thread-1 1
Thread-0 2
Thread-1 3
Thread-0 4
Thread-1 5
Thread-0 6
Thread-1 7
Thread-0 8
Thread-1 9
對其當前物件上鎖
package hw.lock;
/**
*
*
* @author huangwei
* 2017 10 20
*/
public class Producer {
int num=1;
public int count() {
synchronized (this) {
while(num<10) {
System.out.println(Thread.currentThread().getName() +" "+num++);//輸出當前執行緒
}
try {
Thread.sleep(1000);
}catch (Exception e) {
// TODO: handle exception
}
}
return num;
}
public static void main(String[] args) {
// TODO Auto-generated method stub
final Producer aProducer=new Producer();
//Producer bProducer=new Producer();
Thread test1=new Thread(new Runnable() {
@Override
public void run() {
aProducer.count();
}
});
Thread test2=new Thread(new Runnable() {
@Override
public void run() {
aProducer.count();
}
});
test1.start();
test2.start();
//bProducer.start();
}
}
Thread-0 1
Thread-0 2
Thread-0 3
Thread-0 4
Thread-0 5
Thread-0 6
Thread-0 7
Thread-0 8
Thread-0 9
由於num一直由第一個執行緒執行沒有釋放,所以第二個執行緒由於大於10直接結束。
直觀一點
num宣告在方法內
package hw.lock;
/**
*
*
* @author huangwei
* 2017 10 20
*/
public class Producer {
public int count() {
int num=1;
synchronized (this) {
while(num<10) {
System.out.println(Thread.currentThread().getName() +" "+num++);//輸出當前執行緒
}
try {
Thread.sleep(1000);
}catch (Exception e) {
// TODO: handle exception
}
}
return num;
}
public static void main(String[] args) {
// TODO Auto-generated method stub
final Producer aProducer=new Producer();
//Producer bProducer=new Producer();
Thread test1=new Thread(new Runnable() {
@Override
public void run() {
aProducer.count();
}
},"test1");
Thread test2=new Thread(new Runnable() {
@Override
public void run() {
aProducer.count();
}
},"test2");
test1.start();
test2.start();
//bProducer.start();
}
}
輸出就為這樣
test2 1
test2 2
test2 3
test2 4
test2 5
test2 6
test2 7
test2 8
test2 9
test1 1
test1 2
test1 3
test1 4
test1 5
test1 6
test1 7
test1 8
test1 9
簡單的生產者消費者模型
package hw.lock;
/**
*
*
* @author huangwei
* 2017 10 20
*/
public class Producer {
int pnum=0;
public int count() {
int num=1;
synchronized (this) {
while(num<10) {
System.out.println(Thread.currentThread().getName() +" "+num++);//輸出當前執行緒
}
try {
Thread.sleep(5000);
}catch (Exception e) {
// TODO: handle exception
}
}
return num;
}
public void produce() {//生產者
synchronized (this) {
try {
if(pnum>10) {
this.wait();//數量上限等待
}
this.notify();//喚醒程序
pnum++;
System.out.println("生產者生產:1,還剩下:"+this.pnum);
Thread.sleep(1000);
}catch (Exception e) {
// TODO: handle exception
}
}
}
public void customer() {//消費者
synchronized (this) {
try {
if(pnum>0) {
this.notify();//可以取 喚醒
pnum--;
System.out.println("消費者消費:1,還剩下:"+this.pnum);
}else{
System.out.println("消費者消費:0,儲量不足");
this.wait();//否則等待
}
Thread.sleep(1000);
}catch (Exception e) {
// TODO: handle exception
}
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
final Producer aProducer=new Producer();
//Producer bProducer=new Producer();
Thread test1=new Thread(new Runnable() {
@Override
public void run() {
while(true) {
aProducer.produce();
}
}
},"test1");
Thread test2=new Thread(new Runnable() {
@Override
public void run() {
while(true) {
aProducer.customer();
}
}
},"test2");
test1.start();
test2.start();
//bProducer.start();
}
}
生產者生產:1,還剩下:1
消費者消費:1,還剩下:0
消費者消費:0,儲量不足
生產者生產:1,還剩下:1
生產者生產:1,還剩下:2
生產者生產:1,還剩下:3
生產者生產:1,還剩下:4
消費者消費:1,還剩下:3
生產者生產:1,還剩下:4
生產者生產:1,還剩下:5
生產者生產:1,還剩下:6
消費者消費:1,還剩下:5
消費者消費:1,還剩下:4
消費者消費:1,還剩下:3
生產者生產:1,還剩下:4
生產者生產:1,還剩下:5
生產者生產:1,還剩下:6
生產者生產:1,還剩下:7