首頁  >  文章  >  Java  >  Java基礎之對線程的理解

Java基礎之對線程的理解

零下一度
零下一度原創
2017-07-18 17:54:201273瀏覽

在理解多執行緒之前我們要先理解行程和執行緒:

程式指每個獨立程式在電腦上的一次執行活動,例如QQ,360管家

線程就是一個程式內部的一條執行路徑,如果一個程式中可以同一時間執行多個線程,我們就說這個程式是支援多線程的;例如迅雷下載軟體可以同時下載多個任務。

什麼是多執行緒?

多線程可以理解成多任務,一個程式在同一時間內執行多個任務,每個任務通常可以理解成一個線程,這種能執行多個線程的程式稱之為多線程程序。

為什麼需要多執行緒?

多執行緒作為一種多任務並發的工作方式,具有以下特點:

提高應用程式回應

提高電腦CPU的使用率

改善程式結構。一個既長又複雜的進程可以考慮分為多個線程,成為幾個獨立或半獨立的運行部分,這樣的程式會利於理解和修改。

建立多執行緒程式的方法及步驟: 

#方法一:用Thread 類別建立執行緒

1) 一寫個類別,繼承自Thread 類別

2) 重寫run方法//這個方法裡放的變是要執行的內容

3) new 這個自訂的Thread 類別

4) 呼叫start方法,啟動這個執行緒(start 方法有兩個作用,一個是開啟執行緒,一個是呼叫run方法)

public class Test12 {public static void main(String[] args) {
        ThreadDemo t = new ThreadDemo();
        t.start();// 啓動ThreadDemo t2 = new ThreadDemo();
        t2.start();

        ThreadDemo t3 = new ThreadDemo();
        t3.start();for (int i = 0; i < 20; i++) {
            System.out
                    .println("线程" + Thread.currentThread().getName() + "正在运行");
        }
    }
}class ThreadDemo extends Thread {
    @Overridepublic void run() {for (int i = 0; i < 200; i++) {
            System.out
                    .println("线程" + Thread.currentThread().getName() + "正在运行");
            ;
        }
    }

}

要點總結:

要將一段程式碼放在一個新的執行緒上運行,這段程式碼應該放在一個類別的run 函數中, 並且,ran函數所在的類別是Thread類別的子類別

倒過來說,我們要實現多線程,必須編寫一個繼承了Threa類的的子類,並重寫其run方法

啟動一個線程,我們不是調用Thread子類對象的run方法,而是呼叫Thread子類別物件的start 方法( 從Thread類別繼承到的),該方法將

產生一個新的執行緒並在該執行緒上執行子類別物件中的run方法

由於執行緒的程式碼片段放在run 方法中,那麼該方法執行完後,執行緒就結束了

public class Test {public static void main(String[] args) throws InterruptedException {
                    ThreadDemo t=new ThreadDemo();
                    t.start();                    while(true){
                        System.out.println("这是线程一输出的内容");
                        Thread.sleep(10);
                    }
                }
            }            
            class ThreadDemo extends Thread{public void run() {for (int i = 0; i < 50; i++) {
                        System.out.println("这是线程二输出的内容");
                    }
                }
            }
//例子,创建三个线程,和主线程同时运行public class Test2 {public static void main(String[] args) {
                MyThread t1=new MyThread();
                t1.start();
                
                MyThread t2=new MyThread();
                t2.start();
                
                MyThread t3=new MyThread();
                t3.start();                    for (int i = 0; i < 200; i++) {
                    System.out.println("主线程"+Thread.currentThread().getName()+"正在运行");
                }    
            }    
        }
#

方法二:使用Runable接口实现多线程

1)自定义一个类,implements Runnable接口

2)重写run方法

3)new出来这个自定的类//该类对象会作为Thread类构造函数的参数

4)new出Thread类

5)调用start方法

继承Thread类创建线程和实现Ruanble接口创建线程的区别

使用 Runable 接口创建多线程

1.适合多个相同的程序代码去处理同一资源的情况 把虚拟CPU (线程)同程序的代码 数据有效分离,较好的体现了面象对象

2.可以避免java单继承带来的局限 例如:当我们将已经继承了某类的一个类的子类放入多线程中,由于不能同时继承两个类,所以

什么情况下,不能采用继承Thread的方式,只好通过实现Ruanable?

1.当线程被构造时,需要的代码和数据通过一个对象作为构造函数实参传递进去,这个对象就是一个实现了Runable接口类的实例

2.事实上,几乎所有的多线程都可以用Runnable接口方式

三、自定义线程

1) 线程的名字,一个运行中的线程总是有名字的,名字有两个来源,一个是虚拟机自己给的名字,

一个是你自己的定的名字。在没有指定线程名字的情况下,虚拟机总会为线程指定名字,并且主线程的名字总是mian,非主线程的名字不确定。

//Thread(String name) 可以在构造线程的时候,直接传入名字

//getName () 可以得到线程的名字

2) 获取当前线程的对象的方法是:Thread.currentThread();

3) 在上面的代码中,只能保证:每个线程都将启动,每个线程都将运行直到完成。一系列线程以某种顺序启动并不意味着将按该顺序执行。

对于任何一组启动的线程来说,调度程序不能保证其执行次序,持续时间也无法保证。

4) 当线程目标run()方法结束时该线程完成

5) 一旦线程启动,它就永远不能再重新启动。只有一个新的线程可以被启动,并且只能一次。一个可运行的线程或死线程可以被重新启动。

//t1.start(); 即这个操作只能一次,如果多次,将引发 java.lang.IllegalThreadStateException

class MyThread extends Thread{public void run() {for (int i = 0; i < 20000; i++) {
                    System.out.println("线程"+Thread.currentThread().getName()+"正在运行");
                }
            }
}

四、线程的状态

新建 (Born) : 新建的线程处于新建状态

就绪 (Ready) : 在创建线程后,start() 方法被调用它将处于就绪状态

运行 (Running) : 线程在开始执行时(run)进入运行状态

睡眠 (Sleeping) : 线程的执行可通过使用 sleep() 方法来暂时中止。在睡眠结束后,线程将进入就绪状态

等待 (Waiting) : 如果调用了 wait() 方法,线程将处于等待状态。用于在两个或多个线程并发运行时。

挂起 (Suspended) : 在临时停止或中断线程的执行时,线程就处于挂起状态。 //suspend() 已过时,有固定的死锁倾向

恢复 (Resume) : 在挂起的线程被恢复执行时,可以说它已被恢复。

阻塞 (Blocked) – 在线程等待一个事件时(例如输入/输出操作),就称其处于阻塞状态。

死亡 (Dead) – 在 run() 方法已完成执行或其 stop() 方法被调用之后,线程就处于死亡状态。 //stop 方法有两个重载,均已过时,有固定的不安全性

****

Java 中的线程优先级是在 Thread 类中定义的常量

NORM_PRIORITY : 值为 5

MAX_PRIORITY : 值为 10

MIN_PRIORITY : 值为 1

缺省优先级为 NORM_PRIORITY

****

有关优先级的方法有两个:

final void setPriority(int newp) : 修改线程的当前优先级

final int getPriority() : 返回线程的优先级

五、sleep 和 yield 的区别

1) sleep是Thread类的一个静态方法,该方法会让当前正在 执行的线程暂停执行,从而将执行机会让给其他线程执行。

sleep(long mills)参数指定当前线程暂停执行的时间,经过这段阻塞时间后,该线程会进入就绪状态,等候线程调度器的调度

sleep方法声明抛出了InterruptedException异常,所以调用sleep方法时要么在方法开始处抛出异常要么使用try{}..catch{}块进行捕获。

2) yield 方法只会给优先级相同或更高优先级的线程执行机会。yield不会将线程转入阻塞状态,只是强制当前线程进入就绪状态。

因此完全有可能某个线程调用yield方法暂停后,立即又获得处理器资源被执行。yield方法没有声明抛出任何异常。

// 通俗地说 yield()方法只是把线程的状态 由执行状态打回准备就绪状态

public void run() {for (int i = 0; i < 50; i++) {
        System.out.println(getName() + "--->" + i);if (i == 20) {
            Thread.yield();
            }
            }
            }public static void main(String[] args) {
                YieldThread t1 = new YieldThread("高级");
                t1.start();// 若当前线程优先级最高,那么即使调用了yield()方法,线程调度器又会将这个线程调度出来重新执行// t1.setPriority(Thread.MAX_PRIPORITY);YieldThread t2 = new YieldThread("低级");
                t2.start();
                t2.setPriority(Thread.MIN_PRIORITY);
                }

 六、线程同步 synchronized

原理讲解:任何对象都有一个标志位.是0或1 程序执行到这里.会检查这个对象的标志位,如果是1,那么向下执行,同时将标志信置为0

如果标志位为0,则线程发生阻塞.一直等到标志位为1 标志位又叫锁旗标

synchronized 又叫锁定了监视器 一个线程可以再进入线程锁定临视器.就象蓝球运动员再拿到球一样。

class SaleThread2  implements Runnable{//static int ticket=1000;private int ticket;public SaleThread2(int ticket){this.ticket=ticket;
            }
            
            String lockStr="";//Object obj=new Object();public void run() {while(true){synchronized (lockStr) {  //锁 ,锁旗标if(ticket>0){try{
                                    Thread.sleep(10);    
                            }catch(Exception ex){
                                ex.printStackTrace();
                            }
                            
                            System.out.println("线程"+Thread.currentThread().getName() +"正在卖第"+ticket--+"张票");
                        }else{break;
                        }
                    }
                }    
            }
        }

 

以上是Java基礎之對線程的理解的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn