一.關於同步
1.什麼情況下需要同步
當多線程併發, 有多段代碼同時執行時, 我們希望某一段代碼執行的過程中CPU不要切換到其他線程工作. 這時就需要同步.
如果兩段代碼是同步的, 那麼同一時間只能執行一段, 在一段代碼沒執行結束之前, 不會執行另外一段代碼.
2.同步代碼塊使用synchronized關鍵字加上一個鎖對象來定義一段代碼, 這就叫同步代碼塊
多個同步代碼塊如果使用相同的鎖對象, 那麼他們就是同步的
3.非靜態方法的鎖對象是: this
4.靜態方法的鎖對象是:字節碼對象,也就是這個方法對應所在的那個類的.class對象,
package com.fenqing.duoxiancheng;
public class d11_Synchronized {
public static void main(String[] args) {
final Printer p=new Printer();
new Thread(){
public void run(){
while(true){
p.print1();
}
}
}.start();
new Thread(){
public void run(){
while(true){
p.print2();
}
}
}.start();
}
}
class Printer {
static demo d = new demo();
public static void print1() { //方法是static,所以鎖對象也要用static修飾
//synchronized(new demo()){ //不能用匿名對象,因爲匿名對象不是同一個對象,也就是說不是同一把鎖
synchronized(d){ //鎖對象可以是任意對象,但是被鎖的代碼需要保證是同一把鎖,
System.out.print("計");
System.out.print("算");
System.out.print("機");
System.out.print("\r\n");
}
}
public static void print2() {
synchronized(d){
System.out.print("軟");
System.out.print("件");
System.out.print("工");
System.out.print("程");
System.out.print("\r\n");
}
}
public synchronized void print3(){ //定義同步方法
System.out.print("專");
System.out.print("業");
System.out.print("\r\n");
}
}
class demo{
}
線程安全性
- 多線程併發操作同一數據時, 就有可能出現線程安全問題
- 使用同步技術可以解決這種問題, 把操作數據的代碼進行同步, 不要多個線程一起操作
關於同步的練習
四個窗口出售100張票的問題。
1,用Thread()的構造實現
package com.fenqing.duoxiancheng;
public class d12_sell {
public static void main(String[] args) {
TicketsSeller t1 = new TicketsSeller();
TicketsSeller t2 = new TicketsSeller();
TicketsSeller t3 = new TicketsSeller();
TicketsSeller t4 = new TicketsSeller();
t1.setName("窗口1");
t2.setName("窗口2");
t3.setName("窗口3");
t4.setName("窗口4");
t1.start();
t2.start();
t3.start();
t4.start();
}
}
class TicketsSeller extends Thread {
private static int tickets = 100;
static Object obj = new Object();
public TicketsSeller() {
super();
}
public TicketsSeller(String name) {
super(name);
}
public void run() {
while(true) {
synchronized(obj) {
if(tickets <= 0)
break;
try {
Thread.sleep(10);//線程1睡,線程2睡,線程3睡,線程4睡
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(getName() + "...這是第" + tickets-- + "號票");
}
}
}
}
2.用Runnable實現
package com.fenqing.duoxiancheng;
public class d13_sell {
public static void main(String[] args) {
Ticket t=new Ticket();
new Thread(t).start();
new Thread(t).start();
new Thread(t).start();
new Thread(t).start();
}
}
class Ticket implements Runnable {
private int tickets = 100;
public void run() {
while(true) {
synchronized(Ticket.class) {
if(tickets <= 0)
break;
try {
Thread.sleep(10);//線程1睡,線程2睡,線程3睡,線程4睡
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"...這是第" + tickets-- + "號票");
}
}
}
}
死鎖——哲學家進餐的問題
- 多線程同步的時候, 如果同步代碼嵌套, 使用相同鎖, 就有可能出現死鎖。
不清楚這個問題的可以看看哲學家就餐問題簡介
package com.fenqing.duoxiancheng;
public class d14_deathLock {
private static String s1 = "筷子左"; //定義兩個變量
private static String s2 = "筷子右";
public static void main(String[] args) {
new Thread() {
public void run() {
while(true) {
synchronized(s1) {
System.out.println(getName() + "...拿到" + s1 + "等待" + s2);
synchronized(s2) {
System.out.println(getName() + "...拿到" + s2 + "開喫");
}
}
}
}
}.start();
new Thread() {
public void run() {
while(true) {
synchronized(s2) {
System.out.println(getName() + "...拿到" + s2 + "等待" + s1);
synchronized(s1) {
System.out.println(getName() + "...拿到" + s1 + "開喫");
}
}
}
}
}.start();
}
}