1. 程式人生 > 程式設計 >JavaScript canvas實現七彩時鐘效果

JavaScript canvas實現七彩時鐘效果

多執行緒

基礎概念

  1. 程式(program):一段靜態程式碼,一組指令的集合

  2. 程序(process):程式的一次執行過程,或是一個正在執行的程式,資源分配的單位

  3. 執行緒(thread):一個程序可以有多個執行緒,是獨立排程和分派的基本單位

  4. 執行緒共享方法區和堆,stack和program couter Register(程式計數器)單獨

  5. 並行:多個cpu同時執行多個任務

  6. 併發:一個cpu採用時間片輪轉法執行多個任務

  7. 多執行緒優點:提高程式響應性,提高cpu利用率,改善程式結構(單核cpu多執行緒執行有可能速度反而變慢)

什麼時候需要多執行緒

  1. 需要同時執行兩個貨多個任務

  2. 程式需要實現一些要等待的任務

  3. 需要一些後臺執行程式

實現多執行緒方式

繼承Thread類
  1. 不可以直接在主執行緒呼叫run方法,這樣並沒有啟動新的執行緒

  2. 同一個子類執行緒物件只能執行一次start()方法,否則會丟擲異常,

  3. 要多個執行緒只能建立多個物件

if (threadStatus != 0)
throw new IllegalThreadStateException();
/**
* @description:
* @author: liSen
* @time: 2021/6/4 19:40
*/
public class ThreadTest {
public static void main(String[] args) {
//3.建立子類物件執行Start方法
new MyThread().start();//start()方法執行步驟1.啟動當前的執行緒2.執行run方法
for (int i = 0; i <100; i++) {
System.out.println(Thread.currentThread().getName());
System.out.println("主執行緒");
}

//匿名子類
new Thread() {
@Override
public void run() {
System.out.println("這是子執行緒");
}
}.start();

}
}

//1.建立繼承Thread子類
class MyThread extends Thread{
//2.重寫run方法
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName());
System.out.println("子執行緒執行");
}
}
}
實現Runnable介面
package com.company.Thread;

/**
* @description:
* @author: liSen
* @time: 2021/6/4 22:07
*/
public class ThreadTest2 {
public static void main(String[] args) {
//3.建立實現類物件
MThread mThread = new MThread();
//4.建立Thread物件
Thread thread = new Thread(mThread);
//5.呼叫start方法
thread.start();
}
}

//1.建立一個時間Runnable介面的類
class MThread implements Runnable{

//2.生成run方法
@Override
public void run() {
System.out.println("子執行緒1 ");
}
}

比較實現Runnable更好

  1. 沒有單繼承限制

  2. 能解決多執行緒資料共享問題

實現Callable介面
/**
* @description: 使用Callable實現多執行緒,jdk5.0
* @author: liSen
* @time: 2021/6/9 16:13
*/
public class ThreadNew {
public static void main(String[] args) {
//3.生成sum例項
Sum sum = new Sum();
//4.生成FutureTask例項
FutureTask<Integer> futureTask = new FutureTask(sum);
//5.new 一個新的執行緒
new Thread(futureTask).start();
Integer o = null;

//6.可以獲取call的返回值
try {
o = futureTask.get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
System.out.println(o);
}
}
//1。實現Callable介面(可以使用泛型)
class Sum implements Callable<Integer>{
private Integer sum = 0;

//2.實現call方法(可以有返回值)
@Override
public Integer call() throws Exception {
for (int i = 0; i < 100; i++) {
if(i % 2 == 0){
sum +=i;
System.out.println(i);
}
}
return sum;
}
}

比Runnable好

  1. 可以使用泛型

  2. call可以有返回值

  3. 可以丟擲異常被捕獲

執行緒池

方法

  1. String getName()獲取某個執行緒的名字

    Thread.currentThread().getName()//獲取當前執行緒的名字
    Thread thread1 = new MyThread()
    thrad1.getName();//獲取thread1執行緒的名稱
  2. void start() 啟動執行緒,並執行執行緒的run方法

    new Mythread().start();
  3. run()執行緒在被排程時的執行操作

  4. void setName() 設定該執行緒的名稱

    1.給主線成命名
    Thread.currentThread().setName("123");
    2.執行緒子類實現構造器,
    //Thread類自帶的命名構造器
    public Thread(String name) {
    init(null, null, name, 0);
    }
    //在自己子類實現就好
    public MyThread(String threadName){
    super(threadName);
    }

  5. static Thread currentThread() 返回當先執行緒,在子類中就是this,通常在主線下和Runnable實現

  6. yield()釋放當前cpu的執行權,重新分配執行執行緒,可能被一個執行緒連續多次搶到執行權

    //1.建立繼承Thread子類
    class MyThread extends Thread{
    //2.重寫run方法
    @Override
    public void run() {
    for (int i = 0; i < 100; i++) {

    System.out.println(Thread.currentThread().getName());
    System.out.println("子執行緒執行");
    if(i % 5 == 0){
    yield();
    }
    }
    }
    /* public MyThread(String threadName){
    super(threadName);
    }*/
    }
  7. join()線上程A呼叫執行緒B的join(),執行緒A進入阻塞狀態,直到執行緒B執行完後,執行緒A才結束阻塞

  8. stop()已過時,強制結束執行緒生命

  9. sleep(long millitme)讓當前執行緒數目指定毫秒,指定時間內執行緒時阻塞的

  10. isAlive()判斷執行緒是否存在

  11. setPriority/getPriority()設定/獲取執行緒的優先順序,高優先順序搶佔只是概率問題(執行緒的優先權高度依賴於系統,例如win有七個優先權,java的優先順序對映到作業系統的優先順序中,但是在Oracle未linux提供的ava虛擬機器會忽略執行緒優先順序---所有執行緒優先順序相同。現在不推薦使用優先順序)

  12. Thread.State getState()獲取當前執行緒的狀態,NEW、RUNNABLE...

執行緒排程策略

  1. 排程策略:時間片,特點:搶佔式(高優先順序的執行緒搶佔cpu)

  2. Java排程方式:

    1. 同級執行緒組成先進先出的佇列(先到先服務),使用時間片策略

    2. 對高優先順序,室友優先順序的搶佔策略

  3. 執行緒的優先順序:

    1. MAX_PRIORITY =10

    2. MIN_PRIORITY = 1

    3. NORM_PRIORITY = 5

    4. 設定優先順序:

      1. setPriority()

      2. getPriority()

執行緒安全問題

  1. 多個執行緒方位同一個變數如買票時票的總數,如果使用extends Thread實現多執行緒,應將該變數設定為static,多個執行緒訪問同一份變數,Runnble是實現是因為只有子類一份實現類,所以無需使用。

執行緒的生命週期

public enum State {
NEW,//新建:new Thewad(r),執行緒剛建立還未啟動

RUNNABLE,//可執行的,呼叫start()方法就是處於可執行狀態,因為作業系統採用的是搶佔式的排程,時間片用完剝奪執行權給其他執行緒,所以是執行緒是在執行,也可能沒在執行

BLOCKED,//阻塞:當一個執行緒試圖獲取一個物件鎖,而該物件鎖被其他的執行緒持有,則該執行緒進入Blocked狀態;當該執行緒持有鎖時,該執行緒將變成Runnable狀態

WAITING,//等待:一個執行緒在等待另一個執行緒執行一個(喚醒)動作時,該執行緒進入Waiting狀態。進入這個狀態後是不能自動喚醒的,必須等待另一個執行緒呼叫notify或者notifyAll方法才能夠喚醒

TIMED_WAITING,//計時等待:同waiting狀態,有幾個方法有超時引數,呼叫他們將進入Timed Waiting狀態,這一狀態將一直保持到超時期滿或者接收到喚醒通知,帶有超時引數的常用方法有Thread.sleep、鎖物件.wait()

TERMINATED;//終止:因為run方法正常退出而死亡,或者因為沒有捕獲的異常終止了run方法而死亡
}
  1. 新建(new):執行緒被建立時

  2. 就緒:新建執行緒被start()後,執行緒進入執行緒佇列等待cpu時間片,此時已經具備執行條件,只是沒有分配資源

  3. 執行:就緒執行緒被排程獲得cpu資源,進入執行狀態,run()定義了執行緒的操作和功能

  4. 阻塞:在某種特殊情況下,被人為掛起或者執行輸入輸出操作時,讓出cpu並臨時終止自己的執行,進入阻塞

  5. 死亡:執行緒完成了他的全部工作或者執行緒被提前強制性終止或異常導致結束

執行緒同步

同步程式碼塊
//1.建立一個時間Runnable介面的類
class MThread implements Runnable{
private Integer t =100;
Object obj = new Object();//多個執行緒必須用同意把鎖


//2.生成run方法
@Override
public void run() {
whlie(true){
synchronized(obj){//同步監視器:鎖,任何一個物件,類都可以當鎖
//synchronized(this){//同步監視器:鎖,任何一個物件,類都可以當鎖
synchronized(MThread.class){//類只會載入一此所以可以當鎖
//被同步的程式碼(操作共享資料的程式碼)
//共享資料:多個執行緒操作的資料
t--;
System.out.println(t);
}
}
System.out.println("子執行緒1 ");
}
}
//1.建立繼承Thread子類
class MyThread extends Thread{

private static Integer t =100;
//2.重寫run方法
@Override
public void run() {
while (true){
synchronized(MyThread.class){//保證鎖的唯一性(繼承不能用this,new 物件,因為會有多個例項)
t--;
}

System.out.println(Thread.currentThread().getName());
System.out.println("子執行緒執行");

}
}

}

被同步的程式碼塊相當於單執行緒

同步方法
//1.建立繼承Thread子類
class MyThread extends Thread{

private static Integer t =100;//static多個物件訪問同一個元素

@Override
public void run() {
while(true){
show();
}
}


public static synchronized void show(){//加上static 同步鎖是類本身即MyThread.class 反之為this即生成物件(失敗)

if(t > 0){
t--;
System.out.println(t);
}
}
}
//1.建立一個時間Runnable介面的類
class MThread implements Runnable{
private Integer t =100;
@Override
public void run() {
while(true){
show();
}
}

public synchronized void show(){//預設是this,繼承Runnable只有一個例項物件(可以)

if(t > 0){
t--;
System.out.println(t);
}
}
}
Lock(鎖)
class Window implements Runnable{

private Integer t = 100;
private ReentrantLock rt = new ReentrantLock(true);//使用可重用鎖 (fair:ture,執行緒按照先進先出的結構使用資源,而非搶佔,預設fair:false)

@Override
public void run() {
while (true){
try{
rt.lock();//新增鎖
if(t>0){
t--;
System.out.println(Thread.currentThread().getName()+"----------"+t);
}
}finally {
rt.unlock();//解除鎖
}
}
}
}
synchronized與Lock
  1. 推薦使用順序:Lock---同步程式碼塊---同步方法

  2. 不同:Lock需要手動新增同步鎖(lock),手動釋放同步鎖(unlock)

同步鎖解決懶漢式執行緒安全問題

//同步方法
class Person{

private static Person p1 = null;

private Person(){//私有不讓在外部new物件
System.out.println("物件");
}

public static synchronized Person getPerson(){//同步方法,預設指向this
if(p1 == null){
p1 = new Person();
}
return p1;
}
}
//同步程式碼塊
class Person{

private static Person p1 = null;

private Person(){
System.out.println("物件");
}

public static Person getPerson(){
if(p1 == null){//防止已經生曾物件,多個物件還在等待進去
synchronized(Person.class){
if(p1 == null){//有多個物件在排隊到同步區域,判斷是否已經存在
p1 = new Person();
}
}
}
return p1;
}
}

死鎖

  1. 不同的執行緒分別佔用對方需要同步的資源不放棄,都在等對方放棄自己同步的資源,造成死鎖

執行緒通訊

class Number implements Runnable{

private Integer t = 100;
Object obj = new Object();
@Override
public void run() {
while (true){
// synchronized(this){
synchronized(obj){
//notify(); //預設是this,當鎖不是this時候因為兩個不同會報異常 (java.lang.IllegalMonitorStateException)
obj.notify(); //方法繼承於object,指向和鎖相同即可
if(t>0){
System.out.println(Thread.currentThread().getName()+"-------------"+t);
t--;
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}

}

}
}

}
}
方法
  • wait():等待,一但執行此方法執行緒進入組賽狀態,釋放同步監視器

  • notify():執行此方法,有一個執行緒被喚醒,有多個執行緒的時候根據優先順序喚醒一個

  • notifyAll():執行此方法,喚醒所有等待的執行緒

說明
  1. 三個方法必須在同步程式碼塊中使用

  2. 三個方法必須與同步鎖指向同一個物件,否則出現異常

  3. 三個方法繼承自Object物件

wait()和sleep()異同

相同:

  1. 兩者都可以使得執行緒進入阻塞狀態

不同:

1. sleep()屬於Thread()方法,wait()屬於Object()
2. sleep()不依賴與同步程式碼監視器synchronized,wait()依賴與synchronized
3. sleep()不會釋放lock,wait()會釋放lock
4. sleep()不用手動喚醒(到時間自動喚醒),wait()需要手動喚醒

生產者消費者問題

package com.company.Thread;

/**
* @description:
* @author: liSen
* @time: 2021/6/9 15:42
*/
public class ProductTest{
public static void main(String[] args) {
Clerk clerk = new Clerk();
Product product = new Product(clerk);
Consumer consumer = new Consumer(clerk);
product.setName("生產者1");
consumer.setName("消費者1");
product.start();
consumer.start();
}
}

class Product extends Thread{
private Clerk clerk = null;

public Product(Clerk clerk) {
this.clerk = clerk;
System.out.println("開始生產");
}

@Override
public void run() {
while (true){
try {
sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
clerk.addProductCount();
}

}
}

class Consumer extends Thread{
private Clerk clerk = null;

public Consumer(Clerk clerk) {
this.clerk = clerk;
System.out.println("開始消費");
}

@Override
public void run() {
while (true){
try {
sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
clerk.subStractProductCount();
}

}
}

class Clerk{
private int productCount =0;

public synchronized void addProductCount(){
if(productCount < 20){
productCount++;
System.out.println(Thread.currentThread().getName()+"---------"+"生產:"+productCount);
notify();//生產好就喚醒
}else {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

public synchronized void subStractProductCount(){
if(productCount > 0){
System.out.println(Thread.currentThread().getName()+"---------"+"消費:"+productCount);
productCount--;
notify();
}else {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}