1.進程和線程
進程和線程都是一個控制流程。
一個進程通常對應于一個程序。
一個程序可以由多個不同的線程構成。
一個進程就是一個應用程序
一個應用程序面對多個用戶則多個進程
一個進程則多個線程
多個線程共享資源且攜帶數(shù)據(jù)操作
多進程資源隔離
2.多線程的核心
多線程的核心在于多個代碼塊并發(fā)執(zhí)行,本質特點在于各代碼塊之間的代碼是亂序執(zhí)行的。
3.操作系統(tǒng)的多任務–以非常小的時間間隔交替執(zhí)行
進程:正在進行的程序
現(xiàn)在使用的操作系統(tǒng)都是多任務的,即能夠 同時 執(zhí)行多個應用程序。實際是操作系統(tǒng)負責對CPU等設備資源進行分配和管理,雖然這些設備某一時刻只能做一件事情,但以非常小的時間間隔交替執(zhí)行多個程序,就給人同時執(zhí)行多個程序的感覺
4.native 修飾的方法
Native Method 調(diào)用java外的程序包,其中包含
一個Native Method就是一個java調(diào)用非java代碼的接口
一個native method方法可以返回任何java類型,包括非基本類型,而且同樣可以進行異??刂?。
5.Thread
一個Thread類的對象代表一個線程,而且只能代表一個線程。
通過Thread類和它定義的對象,我們能獲得當前線程對象、獲取某一線程的名稱、可以實現(xiàn)控制線程暫停一段時間等功能
創(chuàng)建線程的兩種方式
共同點都是自定義線程類重寫run()方法 并且通過 new Thread (線程對象的實例).start() 方法啟動線程和創(chuàng)建一個實例線程啟動運行run()方法,真正實現(xiàn)多線程的方式實現(xiàn)
1.普遍采用實現(xiàn)Runnable接口的方式
public class MyThreadImp implements Runnable {
@Override
public void run() {
System.out.println("執(zhí)行實現(xiàn)Runable接口的MyThreadImp的類中run()方法");
}
public static void main(String[] args) {
MyThreadImp myThreadImp = new MyThreadImp();
Thread thread =new Thread(myThreadImp);
thread.start();//執(zhí)行實現(xiàn)Runable接口的MyThreadImp的類中run()方法
}
}
2.繼承Thread方式
public class MyThread extends Thread{
//等待線程調(diào)用
public void run (){
System.out.println("執(zhí)行了Mythread的run() 方法");
System.out.println(currentThread());
}
public static void main(String[] args) {
MyThread myThread = new MyThread();
Thread thread = new Thread(myThread);
thread.start();// 執(zhí)行了Mythread的run() 方法
}
}
6.自定義線程用 new Thread(Runnable target) 啟動源碼分析
6.1-new Thread(myThread)
主要
1啟動線程;
thread.start();
private native void start0(); 調(diào)用外部接口啟動線程 可能涉及到 硬件或者動態(tài)鏈接庫函數(shù)等待
public synchronized void start() {
/**
* This method is not invoked for the main method thread or "system"
* group threads created/set up by the VM. Any new functionality added
* to this method in the future may have to also be added to the VM.
*
* A zero status value corresponds to state "NEW".
*/
if (threadStatus != 0)
throw new IllegalThreadStateException();
/* Notify the group that this thread is about to be started
* so that it can be added to the group's list of threads
* and the group's unstarted count can be decremented. */
group.add(this);
boolean started = false;
try {
start0();
started = true;
} finally {
try {
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
/* do nothing. If start0 threw a Throwable then
it will be passed up the call stack */
}
}
}
6.2對實例 myThread 創(chuàng)建線程 并給 Thread類 成員變量 target 賦予當前對象實例
創(chuàng)建實例的線程(如果必要可以給線程等級,權限)
/* What will be run. */ 運行哪一個對象實例
private Runnable target;
public Thread(Runnable target) {
init(null, target, "Thread-" + nextThreadNum(), 0);
}
private void init(ThreadGroup g, Runnable target, String name,
long stackSize) {
init(g, target, name, stackSize, null, true);
}
private void init(ThreadGroup g, Runnable target, String name,
long stackSize, AccessControlContext acc,
boolean inheritThreadLocals) {
if (name == null) {
throw new NullPointerException("name cannot be null");
}
this.name = name;
//創(chuàng)建線程
Thread parent = currentThread()
。。。。。。
// 成員變量 target 賦予當前對象實例
this.target = target;
//獲取守護進程
this.daemon = parent.isDaemon();
//獲取進程權限
this.priority = parent.getPriority();
。。。。。。
//省略部分代碼
。。。。。。
/* Set thread ID */
//獲取下個線程id
tid = nextThreadID();
}
7.實現(xiàn)接口Runnable和繼承Thread類的區(qū)別
①處理和共享同一資源對多個相同的程序代碼的線程
②避免由于java單繼承特征帶來的局限性
③增強代碼的健壯性和靈活性可以多個線程共享,代碼和數(shù)據(jù)是獨立的
8.啟動線程通過start()和 run () 區(qū)別
start方法:
通過該方法啟動線程的同時也創(chuàng)建了一個線程.真正實現(xiàn)了多線程。并不等待run方法法中的代碼執(zhí)行完畢,就可以接著執(zhí)行下面的代碼。此時start0的這個線程處于就緒狀態(tài).等待到CPU的時間片后就會執(zhí)行其史的run方法。.這個run方法 含了要執(zhí)行的這個線程的內(nèi)容, run()方法運行結束,此線程也就終止了。
run方法:
通過run方法啟動線程其實就是調(diào)用一個類中的方法,當作普通的方法的方式調(diào)用。并沒有創(chuàng)建一個線程,程序中依舊只有一個主線程,必須等到run()方法里面的代碼執(zhí)行完畢,才會繼續(xù)執(zhí)行下面的代碼,這樣就沒有達到寫線程的目的。
public synchronized void start() {
if (threadStatus != 0)
throw new IllegalThreadStateException();
group.add(this);
boolean started = false;
try {
start0();
started = true;
} finally {
try {
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
}
}
}
private native void start0();
當一個start()線程啟動的時候,它的狀態(tài)〈threadStatus)被設置為О(即線程為NEW狀態(tài)),如果不為0,則拋出llegalThreadStateException異常。
正常的話,將該線程加入線程組,最后嘗試調(diào)用start0方法,而start0方法是私有的native方法(Native Method是一個java調(diào)用非java代碼的接口)。
start調(diào)用后,線程出于就緒狀態(tài)RUNNABLE,等分配到cpu時間片時系統(tǒng)會調(diào)用thread的run()方法,而這個方法其實只是調(diào)用runnable里面自己實現(xiàn)的run()方法。
public class Test7Thread extends Thread{
//
public void run (){
System.out.println("執(zhí)行了Test7Thread");
System.out.println(currentThread());
}
public static void main(String[] args) {
Test7Thread test7Thread = new Test7Thread();
test7Thread.start();//執(zhí)行了Test7Thread
}
}
public class test5 {
public static void main(String[] args) {
Thread thread = new Thread(){
public void run(){
pong();
}
};
thread.start();//啟動線程start方法 啟動run方法
thread.run();//啟動run方法 啟動線程start方法
System.out.println("啟動線程start方法");
}
static void pong (){
System.out.println("啟動run方法");
}
}
9.Thread類target
target - 其 run 方法被調(diào)用的對象。
首先run方法是接口 Runnable 中,而Thread 實現(xiàn)此接口并重寫run方法
與此同時創(chuàng)建創(chuàng)建新執(zhí)行線程有兩種方法。
一種方法是將類聲明為 Thread 的子類。該子類應重寫 Thread 類的 run 方法。接下來可以分配并啟動該子類的實例。
創(chuàng)建線程的另一種方法是聲明實現(xiàn) Runnable 接口的類。該類然后實現(xiàn) run 方法。然后可以分配該類的實例,在創(chuàng)建 Thread 時作為一個參數(shù)來傳遞并啟動。采用這種風格的同一個例子如下所示:
不管是繼承還是實現(xiàn)接口的都是調(diào)用了run() 方法 ,誰去調(diào)用的??是創(chuàng)建的類的對象調(diào)用,
故 target - 其 run 方法被調(diào)用的對象。
Thread類中
@Override
public void run() {
if (target != null) {
target.run();
}
}
10.1線程之一經(jīng)典的銀行轉賬線程問題
有一個銀行賬戶,還有余額1100元,現(xiàn)在A通過銀行卡從中取1000元,而同時另外一個人B通過存折也從這個賬戶中取1000元。取錢之前,要首先進行判斷:如果賬戶中的余額大于要取的金額,則可以執(zhí)行取款操作,否則,將拒絕取款。
我們假定有兩個線程來分別從銀行卡和存折進行取款操作,當A線程執(zhí)行完判斷語句后,獲得了當前賬戶中的余額數(shù)(1000元),因為余額大于取款金額,所以準備執(zhí)行取錢操作(從賬戶中減去1000元),但此時它被線程B打斷,然后,線程B根據(jù)余額,從中取出1000元,然后,將賬戶里面的余額減去1000元,然后,返回執(zhí)行線程A的動作,這個線程將從上次中斷的地方開始執(zhí)行:也就是說,它將不再判斷賬戶中的余額,而是直接將上次中斷之前獲得的余額減去1000。此時,經(jīng)過兩次的取款操作,賬戶中的余額為100元,從賬面上來看,銀行支出了1000元,但實際上,銀行支出了2000元。
10.2線程之二 經(jīng)典的買火車票問題
方法一
=========繼承Thread==========模擬4個售票窗口共同賣100張火車票的程序=============
public class TicketThread {
public static void main(String[] args){
// 執(zhí)行繼承Thread的線程
new MyThread().start();//售票窗口1
new MyThread().start();//售票窗口2
new MyThread().start();//售票窗口3
new MyThread().start();//售票窗口4
}
}
class MyThread extends Thread {
// 車票數(shù)量
private int tickets = 100;
public void run() {
while (tickets > 0) {
System.out.println(this.getName() + " 賣出第 " + tickets-- + "張火車票.");
}
}
}
方法二
==========實現(xiàn)Runnable接口=========模擬4個售票窗口共同賣100張火車票的程序=============
public class TicketRunnable {
public static void main(String[] args) {
//實現(xiàn)Runnable接口實現(xiàn)類
myRunnable myR = new myRunnable();
new Thread(myR).start();
new Thread(myR).start();
new Thread(myR).start();
new Thread(myR).start();
}
}
class myRunnable implements Runnable {
//火車票數(shù)量
private int tickets = 100;
public void run() {
while (tickets > 0) {
System.out.println(Thread.currentThread().getName()+"賣出第["+(tickets--) +"]張火車票.");
}
}
}
11工作中,幾乎所有的多線程應用都用實現(xiàn)Runnable這種方式
把虛擬CPU(線程)同程序的代碼、數(shù)據(jù)有效的分離
健壯、靈活
編寫簡單,可以直接操縱線程,無需使用Thread.currentThread()。
12關于線程安全問題
多線程共享數(shù)據(jù)時的問題
在售票程序中,System.out.println(Thread.currentThread().getName()+“賣出第[”+(tickets–) +“]張火車票.”);這段代碼執(zhí)行時候,極有可能出現(xiàn)一種意外。就是同一張票可能會被打印兩次或多次,也可能出現(xiàn)打印0或負數(shù)的情況。
假設:當tickets的值為1的時候,線程1剛執(zhí)行完if(tickets>0),正準備執(zhí)行下面的代碼,這時,操作系統(tǒng)將CPU切換到了線程2上執(zhí)行,此時tickets的值仍是1,線程2執(zhí)行完以下代碼,CPU切回線程1,
線程1不再執(zhí)行if(tickets>0),繼續(xù)往下執(zhí)行,此時意外出現(xiàn)。
可以使用sleep()來制造線程中這種切換的錯誤。
這種錯誤也就是我們常說的"線程安全
解決之道–思想–線程同步
多個線程都想對同一個資源或代碼塊的時,此時此刻只能被單獨一個線程執(zhí)行完畢再釋放出去待下一線程繼續(xù)使用這個代碼塊,這就是線程同步。
比如:一張火車票只有可能出售給一位旅客,下一個旅客只能等待下一個線程繼續(xù)操作申請另一張票
解決之道–具體實現(xiàn)
將具有原子性的代碼放入synchronize語句內(nèi),形成同步代碼塊。就可以保證線程安全了。
每個對象都有一個標志位(鎖旗標),該標志位有兩個狀態(tài)0、1,其開始狀態(tài)為1,當執(zhí)行synchronized(Object)語句后,Object對象的標志位變?yōu)?狀態(tài),直到執(zhí)行完整個synchronized語句中的代碼塊后才又回到1狀態(tài)。一個線程執(zhí)行到synchronized(Object)的時候,先檢查Object對象的標志位,0表示有線程在執(zhí)行,這個線程將暫時阻塞,讓出CPU資源,直到另外線程執(zhí)行完有關代碼,將Object對象狀態(tài)恢復到1狀態(tài),這個阻塞才被取消,然后線程開始執(zhí)行,同時將Object對象的標志位變?yōu)?,防止其他線程再進入有關的同步代碼塊中。
當線程執(zhí)行到synchronized的時候,如果得不到鎖標記,這個線程會被加入到一個與該對象的鎖標記相關連的等待線程池當中,等待鎖標記。當線程執(zhí)行完同步代碼,就會釋放鎖標記。一個剛釋放了鎖標記的線程可以馬上再次獲得鎖標記,當同步塊遇到break或拋出exception時,線程也會釋放鎖標記。
wait、notify、notifyAll這三個方法只能在synchronized方法中調(diào)用,即無論線程調(diào)用一個對象的wait還是notify方法,該線程必須先得到該對象的鎖旗標,這樣,notify只能喚醒同一對象監(jiān)視器中調(diào)用wait的線程。
所以,如果確定程序沒有安全性的問題,就沒有必要使用同步。
ArrayList Vector(線程同步)
同步是以犧牲程序的性能為代價的。
所以,如果確定程序沒有安全性的問題,就沒有必要使用同步。
synchronized還可以作用在方法上面,用來實現(xiàn)線程的同步
同步塊越短越好,鎖力度過粗容易導致同步嚴重,力度過細容易發(fā)生死鎖問題
Synchronized(this){--粗
System.out.println("hello");
System.out.println("world");
}
--------------------------------
Synchronized(this){--細
System.out.println("hello");
}
System.out.println("world");
--------------------------------
如果是靜態(tài)方法則沒有實例,則鎖類對象this.getClass
一個類不管有多少個實例,只有一個類對象.
Synchronized(this.getClass()){}
13.死鎖
是指兩個線程,都相互等待對方釋放lock
是不可測知或避開的
應采取措施避免死鎖的出現(xiàn)
14.線程間的通信
Object 類定義了 wait()、notify() 和 notifyAll() 方法??梢宰尵€程相互通知事件的發(fā)生。要執(zhí)行這些方法,必須擁有相關對象的鎖。
wait() 會讓調(diào)用線程休眠,直到用 Thread.interrupt() 中斷它 或者wait經(jīng)過了指定的時間 或者另一個線程用 notify() 或 notifyAll() 喚醒它。
當對某個對象調(diào)用 notify() 時,如果有任何線程正在通過 wait() 等待該對象,那么就會喚醒其中一個線程。當對某個對象調(diào)用 notifyAll() 時,會喚醒所有正在等待該對象的線程。
wait、notify、notifyAll這三個方法只能在synchronized方法中調(diào)用
,即無論線程調(diào)用一個對象的wait還是notify方法,該線程必須先得到該對象的鎖旗標,這樣,notify只能喚醒同一對象監(jiān)視器中調(diào)用wait的線程。
package com.thread;
class Product{
private String name;
private String price;
private int count;
public void setName(String name){
this.name = name;
}
public String getName(){
return this.name;
}
public void setPrice(String price){
this.price = price;
}
public String getPrice(){
return this.price;
}
public void setCount(int count){
this.count = count;
}
public int getCount(){
return this.count;
}
public String toString(){
return "產(chǎn)品名稱:"+this.name+" 價格:"+this.price+" 數(shù)量:"+this.count;
}
private boolean flag = false;
public synchronized void put(String name,String price){
try{
if(flag){
//如果有產(chǎn)品就不繼續(xù)生產(chǎn)
wait();
}
System.out.print("***生產(chǎn)者開始生產(chǎn)-->");
this.setName(name);
this.setPrice(price);
//生產(chǎn)產(chǎn)品數(shù)量加1
this.setCount(this.getCount()+1);
System.out.println(this);
flag = true;//生產(chǎn)完畢更改標志
notify();//通知消費者消費
}catch(Exception e){
e.printStackTrace();
}
}
public synchronized void get(){
try{
if(!flag){
wait();
}
System.out.print("--消費者開始消費產(chǎn)品-->");
//消費一個產(chǎn)品數(shù)量減 1
this.setCount(this.getCount()-1);
System.out.println(this);
flag = false;//消費完,更改標志;
notify();//通知生產(chǎn)者繼續(xù)生產(chǎn)
}catch(Exception e){
e.printStackTrace();
}
}
}
class Producter implements Runnable{
private Product p;
public Producter(Product p){
this.p = p;
}
public void run(){
int i=0,n=0;
while(n<5){
n++;
if(i==0){
p.put("包子","1");
}else{
p.put("饅頭","2");
}
//實現(xiàn)交替生產(chǎn)效果
i = (i+1)%2;
}
}
}
class Consumer implements Runnable{
private Product p;
public Consumer(Product p){
this.p = p;
}
public void run(){
int n = 0;
while(n<5){
n++;
p.get();
}
}
}
public class TestThread{
public static void main(String[] args){
Product p = new Product();
new Thread(new Producter(p)).start();
new Thread(new Consumer(p)).start();
}
}
15.同步實現(xiàn)方式–線程的狀態(tài)
synchronized,wait和notify,notityAll。文章來源:http://www.zghlxwxcb.cn/news/detail-430795.html
文章來源地址http://www.zghlxwxcb.cn/news/detail-430795.html
到了這里,關于Java進程線程介紹創(chuàng)建和執(zhí)行銷毀并理解線程安全和線程池 Native Method的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網(wǎng)!