Java——多執行緒基本使用(三) 餓漢式和懶漢式的單例設計模式,多執行緒之間的通訊
這一則部落格主要寫的是單例設計模式,與實現多執行緒之間的通訊等等~
1.單例設計模式:保證類在記憶體中只有一個物件
2.保證類在記憶體中只有一個物件
(1)控制類的建立,不讓其他類來建立本類的物件。用private私有構建函式
(2)在本類中定義一個本類的物件。
(3)提供公共的訪問形式
3.單例寫法
(1)餓漢式
(2)懶漢式
(3)直接用final修飾 (private static final Single3 s=new Single3();)
餓漢式與懶漢式的區別:
(1)餓漢式是空間換時間,懶漢式是時間換空間
(2)在多執行緒訪問時,餓漢式不會建立多個物件,懶漢式有可能會建立多個物件
4.Runtime類:
5.Timer類(計時器):一種工具,執行緒用其安排以後在後臺執行緒中執行的任務。可安排任務執行一次,或者定期重複執行
schedule(TimeTask task,Date time),安排在指定的時間執行指定的任務
t.schedule(new MyTimerTask(), new Date(118,9,11,18,52,50),3000); 安排指定任務在指定時間開始進行重複的固定延遲執行
第一個引數是安排的任務,第二個引數是執行時間,第三個引數是過多長時間再重複執行
6.兩個執行緒間的通訊
什麼時候需要通訊?
在多個執行緒併發執行時,在預設情況下cpu是隨機切換執行緒的;如果我們希望它們有規律的執行,就可以使用通訊,例如每一次執行緒執行一次列印
如何進行通訊?
如果希望執行緒等待,就呼叫wait();如果希望喚醒等待的執行緒,就呼叫notify();這兩個方法必須再同步程式碼中執行,並且使用同步鎖物件來呼叫
wait(),notify()和notifyAll()方法
7.三個或三個以上間的執行緒通訊
(1)notify()方法是隨機喚醒一個執行緒
(2)notifyAll()方法是喚醒所有執行緒
如果多個執行緒之間通訊,需要使用notifyAll()通知所有執行緒,用while來反覆判斷條件
8.執行緒之間通訊需要注意的問題
(1)在同步程式碼塊中,用哪個物件鎖就用哪個物件呼叫wait()方法
(2)因為鎖物件可以是任意物件,且Object類是所有類的基類所有將wait()和notify()方法定義在Object類中
(3)sleep方法和wait方法的區別
1)sleep方法必須傳入引數,引數是時間,時間道理自動醒來;wait方法可以傳入引數,在引數的時間結束後等待, 不傳入引數就是直接等待
2)sleep方法在同步函式或同步程式碼塊中不釋放鎖;wait方法在同步函式或者同步程式碼塊中,釋放鎖
9.互斥鎖
(1)同步:使用ReentrantLock類的lock()和unlock()方法進行同步
(2)通訊
1)使用ReentrantLock類的newCondition()方法可以獲取Condition物件(使用await與signal方法)
2)需要等待的時候使用Condition的await()方法,喚醒的時候使用signal()方法
3)不同的執行緒使用不同的Condition,這樣就能區分喚醒的時候找哪個執行緒
在執行下面測試程式碼中,可以一個案例一個案例執行,因為會有可能會出一些問題,比如說前面的死迴圈無法執行下一個例子要實現的函式等等....
package pra_21;
import java.io.IOException;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class J_40 {
/**
* @param args
* @throws IOException
* @throws InterruptedException
*/
public static void main(String[] args) throws IOException, InterruptedException {
/*
//1.單例設計模式
//Single s1=new Single();
//Single s2=Single.s; //成員變數被私有,不能通過類名點去呼叫,解決了呼叫改變類的成員變數s
Single s3=Single.getS();
Single s4=Single.getS(); //此時不能修改
System.out.println(s3==s4);
//2.Runtime類
Runtime rt=Runtime.getRuntime(); //獲取執行是物件
//在單獨的程序中執行指定的字串命令
//rt.exec("shutdown -s -t 300");
//rt.exec("shutdown -a");
//3.Timer類
Timer t=new Timer(); //建立計時器
t.schedule(new MyTimerTask(), new Date(118,9,11,18,52,50));//安排指定時間執行指定任務
//第一個引數是安排的任務,第二個引數是執行時間,第三個引數是過多長時間再重複執行
//t.schedule(new MyTimerTask(), new Date(118,9,11,18,52,50),3000);//安排指定任務在指定時間開始進行重複的固定延遲執行
while(true){
Thread.sleep(1000);
System.out.println(new Date());
}
*/
//4.兩個執行緒之間的通訊
//等待喚醒機制
final Pr2 p2=new Pr2();
new Thread(){
public void run(){
while(true){
try {
p2.pri();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
new Thread(){
public void run(){
while(true){
try {
p2.pri2();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
//5.多個執行緒之間的通訊
final Pr3 p3=new Pr3();
new Thread(){
public void run(){
while(true){
try {
p3.pri();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
new Thread(){
public void run(){
while(true){
try {
p3.pri2();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
new Thread(){
public void run(){
while(true){
try {
p3.pri3();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
//9.互斥鎖
final Pr3 pr4=new Pr3();
new Thread(){
public void run(){
while(true){
try {
pr4.pri();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
new Thread(){
public void run(){
while(true){
try {
pr4.pri2();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
new Thread(){
public void run(){
while(true){
try {
pr4.pri3();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
}
}
//餓漢式寫法,看法常用
class Single{
//1.私有構造方法,其他類不能訪問該構造方法
private Single(){}
//2.建立本類物件
private static Single s=new Single(); //如果是非靜態的成員變數就只能用物件名點去呼叫,加了靜態以後可以用類名點,但是被私有就不行
//3.對外提供公告的訪問方法(get set方法),主要是不讓其改變s的值
public static Single getS(){ //獲取物件(例項)
return s;
}
}
//懶漢式,單例的延遲載入模式,會出現多執行緒不安全的問題
class Single2{
//1.私有構造方法,其他類不能訪問該構造方法
private Single2(){}
//2.宣告引用
private static Single2 s; //
//3.對外提供公告的訪問方法(get set方法)
public static Single2 getS(){ //獲取物件(例項)
if(s==null){
s=new Single2();
}
return s;
}
}
//第三種
class Single3{
//1.私有構造方法,其他類不能訪問該構造方法
private Single3(){}
//2.宣告引用
private static final Single3 s=new Single3(); //直接用final修飾,使其不能不改變
}
//Timer
class MyTimerTask extends TimerTask{
@Override
public void run() {
System.out.println("走你!");
}
}
//等待喚醒機制:兩個方法 wait(),notify()方法,notifyAll()方法
class Pr2{
private int flag=1;
public void pri() throws InterruptedException{
synchronized(this){
if(flag!=1){
this.wait(); //當前執行緒等待
}
System.out.print("p");
System.out.print("q");
System.out.print("r");
System.out.print("s");
System.out.print("1");
System.out.println();
flag=2;
this.notify(); //隨機喚醒單個等待執行緒
}
}
public void pri2() throws InterruptedException{
synchronized(this){
if(flag!=2){
this.wait();
}
System.out.print("a");
System.out.print("b");
System.out.print("c");
System.out.print("d");
System.out.print("2");
System.out.println();
flag=1;
this.notify();
}
}
}
//多個執行緒
class Pr3{
private int flag=1;
public void pri() throws InterruptedException{
synchronized(this){
while(flag!=1){
this.wait(); //當前執行緒等待
}
System.out.print("p");
System.out.print("q");
System.out.print("r");
System.out.print("s");
System.out.print("1");
System.out.println();
flag=2;
this.notifyAll(); //隨機喚醒單個等待執行緒
}
}
public void pri2() throws InterruptedException{
synchronized(this){
while(flag!=2){
this.wait();
}
System.out.print("a");
System.out.print("b");
System.out.print("c");
System.out.print("d");
System.out.print("2");
System.out.println();
flag=3;
this.notifyAll();
}
}
public void pri3() throws InterruptedException{
synchronized(this){
while(flag!=3){
this.wait(); //執行緒3再此等待,if語句是在哪裡等待,就在哪裡起來,while則可以
//while迴圈是迴圈判斷,每一次都會判斷標記
}
System.out.print("u");
System.out.print("i");
System.out.print("o");
System.out.print("p");
System.out.print("3");
System.out.println();
flag=1;
this.notifyAll();
}
}
}
//使用互斥鎖實現多執行緒
class Pr4{
private ReentrantLock r=new ReentrantLock();
private Condition c1=r.newCondition();
private Condition c2=r.newCondition();
private Condition c3=r.newCondition();
private int flag=1;
public void pri() throws InterruptedException{
r.lock(); //獲取鎖
if(flag!=1){
c1.await(); //當前執行緒等待
}
System.out.print("p");
System.out.print("q");
System.out.print("r");
System.out.print("s");
System.out.print("1");
System.out.println();
flag=2;
c2.signal();
r.unlock(); //釋放鎖
}
public void pri2() throws InterruptedException{
r.lock();
if(flag!=2){
c2.await();
}
System.out.print("a");
System.out.print("b");
System.out.print("c");
System.out.print("d");
System.out.print("2");
System.out.println();
flag=3;
c3.signal();
r.unlock();
}
public void pri3() throws InterruptedException{
r.lock();
if(flag!=3){
c3.await();
}
System.out.print("u");
System.out.print("i");
System.out.print("o");
System.out.print("p");
System.out.print("3");
System.out.println();
flag=1;
c1.signal();
r.unlock();
}
}