多線程
目錄
-
進程和線程
- 進程:是正在運行的程序是系統進行資源分配和調用的獨立單位每一個進程都有它自己的內存空間和系統資源
- 線程:是進程中的單個順序控制流,是一條執行路徑
- 單線程:一個進程如果只有一條執行路徑,則稱爲單線程程序
- 多線程:一個進程如果有多條執行路徑,則稱爲多線程程序
-
多線程實現-方式一
多線程實現方式一:繼承Thread類,重寫run()方法(Thread類實現了Runnable接口)
直接調用run()方法,相當於普通方法的調用,要想啓動多個線程,需要使用start()方法,然後由jvm調用run()方法
方法名 |
說明 |
void run() |
在線程開啓後,此方法將被調用執行 |
void start() |
使此線程開始執行,Java虛擬機會調用run方法() |
public static void main(String[] args) {
ThreadTest t1 = new ThreadTest();
ThreadTest t2 = new ThreadTest();
t1.setName("線程1");
t2.setName("線程2");
/* public final synchronized void setName(String name) {
this.checkAccess();
if (name == null) {
throw new NullPointerException("name cannot be null");
} else {
this.name = name;
if (this.threadStatus != 0) {
this.setNativeName(name);
}
}
}*/
t1.getName();
/* public final String getName() {
return this.name;
}*/
t1.start();
t2.start();
//線程名稱爲 Thread-0 的原因
/* public Thread() {
//Thread-0,1,2,3...
this((ThreadGroup)null, (Runnable)null, "Thread-" + nextThreadNum(), 0L);
}
private static int threadInitNumber;
private static synchronized int nextThreadNum() {
return threadInitNumber++;
}
*/
}
-
設置獲取線程名稱
public static void main(String[] args) {
//給線程命令的方式一
/* ThreadTest t1 = new ThreadTest();
ThreadTest t2 = new ThreadTest();
t1.setName("線程1");
t2.setName("線程1");*/
//給線程命令的方式二
//ThreadTest使用帶參構造方法給name賦值
ThreadTest t1 = new ThreadTest("線程1");
ThreadTest t2 = new ThreadTest("線程1");
t1.start();
t2.start();
}
public class ThreadTest extends Thread {
public ThreadTest() {
}
public ThreadTest(String name) {
super(name);
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(this.getName() + "--" + i);
}
}
}
獲取當前方法所在線程的名稱
public static void main(String[] args) {
//獲取當前方法所在線程的名稱
// currentThread() 返回對當前正在執行的線程對象的引用。
//Thread thread = Thread.currentThread();
Thread.currentThread().setName("主線程");
System.out.println(Thread.currentThread().getName());
}
-
線程調度(設置線程優先級)
注意:優先級高的線程只是獲取cpu的時間相對多一些,不是絕對的佔有
public static void main(String[] args) {
// getPriority() 返回此線程的優先級。線程默認優先級爲5
// setPriority(int newPriority) 更改此線程的優先級。
//Thread thread = Thread.currentThread();
System.out.println(Thread.NORM_PRIORITY); //默認優先級5
System.out.println(Thread.MIN_PRIORITY); //最低優先級1
System.out.println(Thread.MAX_PRIORITY); //最高優先級10
ThreadTest t1 = new ThreadTest();
ThreadTest t2 = new ThreadTest();
ThreadTest t3 = new ThreadTest();
//設置線程名稱
t1.setName("飛機");
t2.setName("和諧號");
t3.setName("汽車");
//設置優先級 注意:優先級高的線程只是獲取cpu的時間相對多一些,不是絕對的佔有
t1.setPriority(10);
t2.setPriority(5);
t3.setPriority(1);
t1.start();
t2.start();
t3.start();
}
/*
飛機--0
...
飛機--16
飛機--17
和諧號--0
飛機--18
飛機--19
飛機--20
飛機--21
飛機--22
和諧號--1
飛機--23
和諧號--2
飛機--24
和諧號--3
飛機--25
飛機--26
和諧號--4
...
汽車--98
汽車--99
*/
注意:優先級高的線程只是獲取cpu的時間相對多一些,不是絕對的佔有,所以和諧號線程會出在飛機線程執行過程中也執行
-
線程控制
方法名 |
說明 |
static void sleep(long millis) |
使當前正在執行的線程停留(暫停執行)指定的毫秒數 |
void join() |
等待這個線程死亡 |
void setDaemon(boolean on) |
將此線程標記爲守護線程,當運行的線程都是守護線程時,Java虛擬機將退出 |
public static void main(String[] args) {
ThreadTest t1 = new ThreadTest();
ThreadTest t2 = new ThreadTest();
ThreadTest t3 = new ThreadTest();
//設置線程名稱
t1.setName("李淳風");
t2.setName("袁天罡");
t3.setName("李茂貞");
t1.start();
try {
//等待此線程執行完成之後其它線程才能開始執行
//位置只能放置t1.start()之後,且不能在其它的線程start之後
t1.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
t2.start();
t3.start();
}
public class ThreadTest extends Thread {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(this.getName() + "--" + i);
/*try {
//使當前正在執行的線程停留(暫停執行)指定的毫秒數
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}*/
}
}
}
public static void main(String[] args) {
ThreadTest t1 = new ThreadTest();
ThreadTest t2 = new ThreadTest();
Thread mainThread = Thread.currentThread();
mainThread.setName("李唐");
//設置線程名稱
t1.setName("李淳風");
t2.setName("袁天罡");
//設置t1、t2爲守護進程
//將此線程標記爲守護線程,當運行的線程都是守護線程時,Java虛擬機將退出
//當主線程退出後守護進程立刻也會退出
t1.setDaemon(true);
t2.setDaemon(true);
t1.start();
t2.start();
for (int i = 0; i < 10; i++) {
System.out.println(mainThread.getName() + "--" + i);
}
}
public class ThreadTest extends Thread {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(this.getName() + "--" + i);
/*try {
//使當前正在執行的線程停留(暫停執行)指定的毫秒數
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}*/
}
}
}
/*
李淳風--0
李淳風--1
李淳風--2
李淳風--3
李淳風--4
李淳風--5
李淳風--6
李淳風--7
李淳風--8
李淳風--9
李淳風--10
李淳風--11
李淳風--12
袁天罡--0
李淳風--13
袁天罡--1
李淳風--14
袁天罡--2
李唐--0
李淳風--15
李唐--1
袁天罡--3
李唐--2
李唐--3
李唐--4
李唐--5
李淳風--16
李淳風--17
李唐--6
李唐--7
李唐--8
李唐--9
袁天罡--4
袁天罡--5
袁天罡--6
袁天罡--7
袁天罡--8
袁天罡--9
Process finished with exit code 0
*/
-
線程的生命週期
-
多線程實現-方式二(推薦使用直接實現Runnable接口的方式)
多線程實現方式二:直接實現Runnable接口,重寫run()方法
方法名 |
說明 |
Thread(Runnable target) |
分配一個新的Thread對象 |
Thread(Runnable target, String name) |
分配一個新的Thread對象 |
所以推薦使用直接實現Runnable接口的方式實現多線程
public static void main(String[] args) {
RunnableTest runnableTest = new RunnableTest();
/*//創建Thread對象,將Runnable實現類的對象作爲構造方法的參數,Thread(Runnable target) 分配一個新的 Thread對象。
Thread thread1 = new Thread(runnableTest);
Thread thread2 = new Thread(runnableTest);
Thread thread3 = new Thread(runnableTest);*/
//創建Thread對象,將Runnable實現類的對象和線程名稱作爲構造方法的參數,Thread(Runnable target, String name) 分配一個新的 Thread對象。
Thread thread1 = new Thread(runnableTest, "線程1");
Thread thread2 = new Thread(runnableTest, "線程2");
Thread thread3 = new Thread(runnableTest, "線程3");
thread1.start();
thread2.start();
thread3.start();
}
public class RunnableTest implements Runnable {
@Override
public void run() {
for(int i=0;i<100;i++){
System.out.println(Thread.currentThread().getName() + "--" + i);
}
}
}
public class SellTicket implements Runnable{
private int ticketNum = 100;
@Override
public void run() {
boolean flag = true;
while(flag){
if (ticketNum>0){
try {
//模擬出票時間100毫秒
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "正在出售第" + ticketNum + "張票");
ticketNum--;
}else{
System.out.println(Thread.currentThread().getName() + "票已售完");
flag = false;
}
}
}
}
public static void main(String[] args) {
SellTicket sellTicket = new SellTicket();
Thread thread1 = new Thread(sellTicket, "窗口1");
Thread thread2 = new Thread(sellTicket, "窗口2");
Thread thread3 = new Thread(sellTicket, "窗口3");
thread1.start();
thread2.start();
thread3.start();
}
-
線程同步
注意:synchronized的鎖可以是任意一個對象,但是如果要讓其生效只能使用同一把鎖。
synchronized(任意對象) {
共享數據代碼塊
}
public class SellTicket implements Runnable{
private int ticketNum = 100;
private Object lock = new Object(); //多線程線程安全問題需要同一把鎖才能解決問題
@Override
public void run() {
boolean flag = true;
while(flag){
synchronized(lock) {
if (ticketNum > 0) {
try {
//模擬出票時間100毫秒
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "正在出售第" + ticketNum + "張票");
ticketNum--;
} else {
System.out.println(Thread.currentThread().getName() + "票已售完");
flag = false;
}
}
}
}
}
public static void main(String[] args) {
SellTicket sellTicket = new SellTicket();
Thread thread1 = new Thread(sellTicket, "窗口1");
Thread thread2 = new Thread(sellTicket, "窗口2");
Thread thread3 = new Thread(sellTicket, "窗口3");
thread1.start();
thread2.start();
thread3.start();
}
/*
窗口2正在出售第100張票
窗口1正在出售第99張票
...
窗口3正在出售第2張票
窗口1正在出售第1張票
窗口1票已售完
窗口2票已售完
窗口3票已售完
Process finished with exit code 0
*/
-
同步方法
普通同步方法格式:
public synchronized void methodName() {
代碼塊
}
普通同步方法格式:
public static synchronized void methodName() {
代碼塊
}
public class SellTicket implements Runnable {
private static int ticketNum = 100;
private Object lock = new Object(); //多線程線程安全問題需要同一把鎖才能解決問題
private int num = 0;
@Override
public void run() {
boolean flag = true;
while (flag) {
if (num % 2 == 0) {
//synchronized(lock) { //普通鎖爲任意對象
//synchronized (this) { //普通方法同步鎖爲當前類當前對象
synchronized (SellTicket.class) { //靜態方法同步鎖爲當前類
if (ticketNum > 0) {
try {
//模擬出票時間100毫秒
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "正在出售第" + ticketNum + "張票");
ticketNum--;
} else {
System.out.println(Thread.currentThread().getName() + "票已售完");
flag = false;
}
}
} else {
flag = sellTicket(flag);
}
num++;
}
}
public synchronized boolean sellTicket(boolean flag) {
if (ticketNum > 0) {
try {
//模擬出票時間100毫秒
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "正在出售第" + ticketNum + "張票");
ticketNum--;
} else {
System.out.println(Thread.currentThread().getName() + "票已售完");
flag = false;
}
return flag;
}
public static synchronized boolean sellTicketStatic(boolean flag) {
if (ticketNum > 0) {
try {
//模擬出票時間100毫秒
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "正在出售第" + ticketNum + "張票");
ticketNum--;
} else {
System.out.println(Thread.currentThread().getName() + "票已售完");
flag = false;
}
return flag;
}
}
public static void main(String[] args) {
SellTicket sellTicket = new SellTicket();
Thread thread1 = new Thread(sellTicket, "窗口1");
Thread thread2 = new Thread(sellTicket, "窗口2");
Thread thread3 = new Thread(sellTicket, "窗口3");
thread1.start();
thread2.start();
thread3.start();
}
-
線程安全的類
- StringBuffer
- Vector
- Hashtable
/*
線程安全的類:
StringBuffer
Vector
Hashtable
*/
public class ThreadDemo {
public static void main(String[] args) {
StringBuffer sb = new StringBuffer();
StringBuilder sb2 = new StringBuilder();
Vector<String> v = new Vector<String>();
ArrayList<String> array = new ArrayList<String>();
Hashtable<String,String> ht = new Hashtable<String, String>();
HashMap<String,String> hm = new HashMap<String, String>();
//多線程一般也不使用Vector,而是通過Collections.synchronizedList()方法返回由指定列表支持的同步(線程安全)列表
//static <T> List<T> synchronizedList(List<T> list) 返回由指定列表支持的同步(線程安全)列表
List<String> list = Collections.synchronizedList(new ArrayList<String>()); //list爲線程安全列表
//多線程一般也不使用Hashtable,而是通過Collections.synchronizedMap方法返回由指定map支持的同步(線程安全)映射。
//synchronizedMap(Map<K,V> m) 返回由指定map支持的同步(線程安全)映射。
List<String> syncMap = Collections.synchronizedMap(new HashMap<String, String>); //syncMap爲線程安全的HashMap
//同理還有方法:
// synchronizedCollection(Collection<T> c) 返回由指定集合支持的同步(線程安全)集合。
// synchronizedSet(Set<T> s) 返回由指定集合支持的同步(線程安全)集。
// synchronizedSortedSet(SortedSet<T> s) 返回由指定的排序集支持的同步(線程安全)排序集。
// synchronizedSortedMap(SortedMap<K,V> m) 返回由指定的排序映射支持的同步(線程安全)排序映射。
}
}
-
Lock鎖
雖然我們可以理解同步代碼塊和同步方法的鎖對象問題,但是我們並沒有直接看到在哪裏加上了鎖,在哪裏釋放了鎖,爲了更清晰的表達如何加鎖和釋放鎖,JDK5以後提供了一個新的鎖對象Lock
Lock是接口不能直接實例化,這裏採用它的實現類ReentrantLock來實例化
ReentrantLock構造方法
方法名 |
說明 |
ReentrantLock() |
創建一個ReentrantLock的實例 |
加鎖解鎖方法
方法名 |
說明 |
void lock() |
獲得鎖 |
void unlock() |
釋放鎖 |
public class SellTicket implements Runnable {
private static int ticketNum = 100;
private Lock lock = new ReentrantLock();
@Override
public void run() {
boolean flag = true;
while (flag) {
try {
lock.lock();
if (ticketNum > 0) {
try {
//模擬出票時間100毫秒
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "正在出售第" + ticketNum + "張票");
ticketNum--;
} else {
System.out.println(Thread.currentThread().getName() + "票已售完");
flag = false;
}
} finally {
lock.unlock();
}
}
}
}
public static void main(String[] args) {
SellTicket sellTicket = new SellTicket();
Thread thread1 = new Thread(sellTicket, "窗口1");
Thread thread2 = new Thread(sellTicket, "窗口2");
Thread thread3 = new Thread(sellTicket, "窗口3");
thread1.start();
thread2.start();
thread3.start();
}
-
生產者消費者
生產者和消費者模式概述
方法名 |
說明 |
void wait() |
導致當前線程等待,直到另一個線程調用該對象的 notify()方法或 notifyAll()方法 |
void notify() |
喚醒正在等待對象監視器的單個線程 |
void notifyAll() |
喚醒正在等待對象監視器的所有線程 |
public class Bok {
//定義一個成員變量,表示第x瓶奶
private int milkNum;
//定義一個成員變量,表示奶箱的狀態
private boolean status = false;
public synchronized void putMilk(int milkNum){
if (status){
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.milkNum = milkNum;
System.out.println("生產者生產第: " + this.milkNum + "牛奶");
//生產完畢之後,修改奶箱狀態
this.status = true;
//喚醒其他等待的線程
notifyAll();
}
public synchronized void getMilk(){
if (!status){
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.milkNum = milkNum;
System.out.println("消費者消費第: " + this.milkNum + "牛奶");
//消費完畢之後,修改奶箱狀態
this.status = false;
//喚醒其他等待的線程
notifyAll();
}
}
public class Producer implements Runnable{
private Bok b;
public Producer() {
}
public Producer(Bok b) {
this.b = b;
}
@Override
public void run() {
for(int i=0;i<10;i++){
b.putMilk(i);
}
System.out.println("牛奶以生產完成!");
}
}
public class Custumer implements Runnable{
private Bok b;
public Custumer() {
}
public Custumer(Bok b) {
this.b = b;
}
@Override
public void run(){
boolean flag = true;
while(flag){
b.getMilk();
}
}
}
public static void main(String[] args) {
//創建奶箱對象,這是共享數據區域
Bok b = new Bok();
//創建生產者對象,把奶箱對象作爲構造方法參數傳遞,因爲在這個類中要調用存儲牛奶的操作
Producer prod = new Producer(b);
//創建消費者對象,把奶箱對象作爲構造方法參數傳遞,因爲在這個類中要調用獲取牛奶的操作
Custumer cust = new Custumer(b);
//創建2個線程對象,分別把生產者對象和消費者對象作爲構造方法參數傳遞
Thread producer = new Thread(prod);
Thread custumer = new Thread(cust);
//啓動線程
producer.start();
custumer.start();
}
/*
生產者生產第: 0牛奶
消費者消費第: 0牛奶
生產者生產第: 1牛奶
消費者消費第: 1牛奶
生產者生產第: 2牛奶
消費者消費第: 2牛奶
生產者生產第: 3牛奶
消費者消費第: 3牛奶
生產者生產第: 4牛奶
消費者消費第: 4牛奶
生產者生產第: 5牛奶
消費者消費第: 5牛奶
生產者生產第: 6牛奶
消費者消費第: 6牛奶
生產者生產第: 7牛奶
消費者消費第: 7牛奶
生產者生產第: 8牛奶
消費者消費第: 8牛奶
生產者生產第: 9牛奶
消費者消費第: 9牛奶
牛奶以生產完成!
*/