線程的安全問題
當一個線程在執行操作共享資料的多條代碼的過程中,其它線程參與運算,就會導緻線程問題的産生
解決思路:
将多條操作共享資料的線程代碼封裝起來,當線程在執行這些代碼的時候,
其他線程不可以參與運算.
必須要目前把這些代碼執行完畢後,其它線程才可以參與運算.
同步代碼
同步代碼塊的格式:
synchronized(對象)
{
需要被同步的代碼;
}
例子:
class Ticket implements Runnable //Ticket票
{
private int num = 100;
Object obj = new Object();
public void run()
{
while(true)
{
//同步代碼塊
synchronized(obj)
{
if (num > 0)
{
try
{
Thread.sleep(10);
}
catch (InterruptedException e)
{}
System.out.println(Thread.currentThread().getName() + "......總票數剩餘" + num--);
}
}
}
}
}
public class Demo{
public static void main(String[] args){
Ticket t = new Ticket(); //建立一個線程任務對象
Thread t1 = new Thread(t);
Thread t2 = new Thread(t);
Thread t3 = new Thread(t);
Thread t4 = new Thread(t);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
運作多次的結果:
(1)
(2)
同步的好處:解決了線程的安全問題
同步的弊端: 相對降低了效率....因為同步外的線程都會判斷同步鎖
(因為如果有線程在同步裡面,剛好CPU又突然給别的線程權限,但又進不去)
(等到CPU重新給到在同步代碼塊裡面的執行完畢出來後,才能給别的線程進去,是以降低了效率.)
同步的前提: 同步中必須有多線程并使用同一個鎖
.
.
.
.
同步函數
就是把同步代碼塊封裝起來
例子:
public synchronized void show(){ //把同步代碼塊封裝起來
if (num > 0) {
try {
Thread.sleep(10);
}
catch (InterruptedException e) {
}
System.out.println(Thread.currentThread().getName() + "...function...總票數剩餘" + num--);
}
這裡面的鎖是對象是 this 也就是 它本身
也就說在上面的例子中我們可以不用Object 建立一個對象來當鎖,而可以用自己的鎖 this
可以寫成這樣
synchronized (this) {
if (num > 0) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
}
}
System.out.println(Thread.currentThread().getName() + "....總票數剩餘" + num--);
}
同步函數使用的鎖是this
同步函數和同步代碼塊的差別:
同步函數的鎖是固定的this.
同步代碼塊的鎖是任意的對象
建議使用同步代碼塊
因為同步函數雖然簡化,但是鎖是唯一的…this
.
.
.
.
.
靜态的同步函數
靜态的同步函數使用的鎖:
該函數所屬位元組碼對象 可以用 getClass()方法擷取
也可以用目前 類名.class 表示
例子:
可以
this.getClass()
//this目前的
Ticket是上面的類名 也可以
Ticket.class
synchronized(Ticket.class)
{
if (num > 0)
{
try
{
Thread.sleep(10);
}
catch (InterruptedException e)
{}
System.out.println(Thread.currentThread().getName() + "......總票數剩餘" + num--);
}
一般隻要保證多個線程用同一個鎖就行!!!