一)進程與線程
進程: 正在進行的程序,是系統進行資源分配和調度的一個獨立單位,
擁有獨立的內存空間和系統資源
線程: 就是進程中一個負責程序執行的控制單元。
CPU調度和分派的基本單位。
多線程: 一個進程中可以有多個執行路徑 稱爲多線程。
多線程就是最大化的提高CPU的利用率,
其他:
線程可與同屬一個進程的其他的線程
共享進程所擁有資源;''
一個進程當中至少要有一個線程。
java程序至少兩條線程(垃圾回收機制GC)
二) 創建線程的兩種方法
1) extends Thread繼承java.lang.Thread
2) 實現 Runnable實現java.lang.Runnable接口
三) 同步
同一時刻,只能有一個線程執行對應的 代碼
synchronized (鎖對象) { //線程拿到的鎖對象是同一個
//同步的代碼
}
四)部分常見類的線程安全性
List 有序的不唯一
ArrayList 動態數組 線程不安全
LinkedList 鏈表
Vector 動態數組 線程安全的
Set 無序的唯一
HashSet 無序的唯一
TreeSet 唯一的,有序的
HashMap 線程不安全的
Hashtable 線程安全的
StringBuffer 線程安全的
StringBuilder 線程不安
五)常用api
start()
使該線程開始執行;Java 虛擬機調用該線程的 run 方法。
yield()
暫停當前正在執行的線程對象,並執行其他線程。
sleep()
在指定的毫秒數內讓當前正在執行的線程休眠(暫停執行),此操作受到系統計時器和調度程序精度和準確性的影響。
setPriority(int newPriority)
更改線程的優先級。
setDaemon(boolean on)
將該線程標記爲守護線程或用戶線程。
join()
等待該線程終止
六)代碼實現
package cn.edu.suda;
public class DuoXianchengTEst {
public static void main(String[] args) {
SellTickets mt1=new SellTickets();
mt1.setName("A窗口");
SellTickets mt2=new SellTickets();
mt2.setName("B窗口");
SellTickets mt3=new SellTickets();
mt3.setName("C窗口");
mt1.start();
mt2.start();
mt3.start();
/*AnotherThread at=new AnotherThread();
Thread t=new Thread(at);
t.start();*/
}
}
//設置線程類,繼承Thread
class SellTickets extends Thread{
//設置票數爲100
static int tickets=100;
//重寫run方法
@Override
public void run() {
while(tickets>0){
try {
//延長運行時間,方便觀察效果
sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//保證同步,第一個if提高效率,減少拿到同步鎖的次數
if(tickets>0){
//同步塊裏是類名。class確保鎖唯一
synchronized (SellTickets.class) {
//第二個if確保不會是負數
if(tickets>0){
System.out.println(getName()+"賣出了"+tickets+"票");
tickets--;
}
}
}
}
}
}
/**
*
* 後面加的這個類是接口實現,二者用法大同小異,不過Thread類默認實現了一些方法
* 我在上面的main方法中將接口線程的使用註釋掉,有興趣可以自己嘗試啓動幾個線程,main方法裏開啓一個主線程
* 然後你可能會發現兩個線程交叉運行。。。
* 我剛纔測試了一下,將SellTicket類裏的sleep方法註釋掉就能看見效果。
*
*/
class AnotherThread implements Runnable {
@Override
public void run() {
for (int i=0;i<1000;i++) {
System.out.println("輸出"+i);
}
}
}
這是第二個例子,主要測試了線程常用的方法:
package cn.edu.suda;
import java.io.IOException;
public class Thread_some_method {
public static void main(String[] args) {
Mythread mt1=new Mythread();
mt1.setName("線程1");
Mythread mt2=new Mythread();
mt2.setName("線程2");
Mythread mt3=new Mythread();
mt3.setName("線程3");
/**
* 上面設置了三個線程,下面我們來驗證多線程常用的幾個方法
*
*/
/**
* join方法,該線程執行完,後面的線程才執行
* 注意使用方式,開啓該線程後,先join,再開啓其他線程,否則可能沒效果
*/
// mt2.start();
// try {
// mt2.join();
// } catch (InterruptedException e) {
// // TODO Auto-generated catch block
// e.printStackTrace();
// }
//
// mt1.start();mt3.start();
/**
* yield()方法可以讓當前正在執行的線程暫停,但它不會阻塞該線程,
* 它只是將該線程從運行狀態轉入就緒狀態。只是讓當前的線程暫停一下,
* 讓系統的線程調度器重新調度一次。
* 很有可能,當某個線程調用了yield()方法暫停之後進入就緒狀態,
* 它又馬上搶佔了CPU的執行權,繼續執行。
* 【注意】
* 實際上,當某個線程調用了yield()方法暫停之後,只有優先級與當前線程相同,
* 或者優先級比當前線程更高的處於就緒狀態的線程纔會獲得執行的機會。
* https://www.cnblogs.com/HigginCui/p/5903652.html
*/
// mt1.start();mt2.start();mt3.start();
// mt1.yield();
/**
* 關於守護線程與用戶線程,我找了一篇博客
* http://blog.csdn.net/xyls12345/article/details/26256693
* 下面的例子也是按照博客上說的來,有興趣自己代碼實現
*
*/
mt1.setDaemon(true);
mt1.start();
try {
System.in.read();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
class Mythread extends Thread{
@Override
public void run() {
for (int i = 0; i < 100; i++) {
try {
sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(getName()+"第"+i+"次");
}
}
}