一,簡介
1,什麼是線程
線程是程序執行的一條路徑, 一個進程中可以包含多條線程
多線程併發執行可以提高程序的效率, 可以同時完成多項工作
有時稱爲輕量級進程,是CPU使用的基本單元;
2,線程組成
由線程ID、程序計數器、寄存器集合和堆棧組成。
3,多線程的應用場景
迅雷開啓多條線程一起下載
QQ同時和多個人一起視頻
服務器同時處理多個客戶端請求
4,併發和並行的區別
並行就是兩個任務同時運行,就是甲任務進行的同時,乙任務也在進行。(需要多核CPU)
併發是指兩個任務都請求運行,而處理器只能按受一個任務,就把這兩個任務安排輪流進行,由於時間間隔較短,使人感覺兩個任務都在運行。
比如我跟兩個網友聊天,左手操作一個手機跟甲聊,同時右手用另一臺電腦跟乙聊天,這就叫並行。
如果用一部手機先給甲發個消息,然後立刻再給乙發消息,然後再跟甲聊,再跟乙聊。這就叫併發。
二,線程分類
Java線程主要分爲:用戶線程(User Thread)和守護線程(Daemon Thread)
2.1,守護線程
該線程不會單獨執行, 當其他非守護線程(用戶線程)都執行結束後, 自動退出;
設置守護線程的方法:
//設置該線程爲守護線程
thread.setDaemon(true);//注意必須在線程啓動start()方法之前調用。
應用:垃圾回收線程
2.2,用戶線程
主要包括:主線程和子線程(用戶線程),子線程設置了setDaemon(true);就是守護線程;
注意:不能把主線程設置爲守護線程,不然會報IllegalThreadStateException異常;
2.3,區別
用戶線程的優先級高,守護線程的優先級低;
如果JVM中還存在用戶線程,那麼JVM就會一直存活,不會退出;但是JVM中只有守護線程的時候,JVM就會退出;
守護線程依賴於用戶線程,JVM中所有用戶線程退出了,所有的守護線程也就會退出 ;
總之,只要有用戶線程存在,JVM就不會退出;所有用戶線程都退出了,JVM也就退出了,JVM都退出了,守護線程也就退出了;
注意:以下代碼的邏輯讓守護線程提前於用戶線程消亡的情況下,守護線程並不會主動延長生命和用戶線程一起消亡。
public static void main(String[] args) {
final Thread t = new Thread(){
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("守護線程");
};
};
t.setDaemon(true);
t.start();
Thread t2 = new Thread(){
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("用戶線程 " + " 守護線程是否存活" + t.isAlive());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
};
};
t2.start();
}
但是讓守護線程延遲於用戶線程消亡的情況下,守護線程會提前和用戶線程一起消亡。
三,多線程實現方式
3.1,通過繼承Thread類,開啓一個線程,實現多線程;
public class ThreadDemo {
public static void main(String[] args) {
ThreadTest thTest = new ThreadTest();
thTest.start();
for (int i = 0; i < 1000; i++) {
System.out.println("我是主線程");
}
}
}
class ThreadTest extends Thread{
@Override
public void run() {
// TODO Auto-generated method stub
super.run();
for (int i = 0; i < 1000; i++) {
System.out.println("子線程");
}
}
}
以上通過打印信息可以看到,主線成和子線程是交替執行的,說明有多線程在運行;
3.2,通過實現Runnable接口
public class ThreadDemo2 {
private void mian() {
// TODO Auto-generated method stub
MyRunnable r = new MyRunnable();
Thread t= new Thread(r);
t.start();
}
}
class MyRunnable implements Runnable{
public void run() {
// TODO Auto-generated method stub
for(int i=0;i<1000;i++){
System.out.println("我是子線程");
}
}
}
3.3,匿名內部類實現,就是以上兩種方式的另一種寫法;
public class ThreadDemo3 {
public static void main(String[] args) {
new Thread(){
@Override
public void run() {
// TODO Auto-generated method stub
super.run();
System.out.println("匿名內部類繼承Thread,開啓一個線程");
}
}.start();
new Thread(new Runnable() {
public void run() {
// TODO Auto-generated method stub
System.out.println("匿名內部類實現Runnable接口,開啓一個線程");
}
}).start();
}
}
四,線程常用方法
4.1,獲取線程的名字:getName();
public static void main(String[] args) {
new Thread(){
@Override
public void run() {
// TODO Auto-generated method stub
super.run();
System.out.println(this.getName() + " aaaaaa");
}
}.start();
}
4.2,爲線程設置名字,不設置有默認的名稱:setName()
//第一種方式
public static void main(String[] args) {
new Thread(){
@Override
public void run() {
// TODO Auto-generated method stub
super.run();
this.setName("android ");
System.out.println("當前線程名字:"+this.getName())
}
}.start();
//第二種方式,構造方法中爲線程設置一個名字
new Thread("Windows"){
@Override
public void run() {
// TODO Auto-generated method stub
super.run();
System.out.println("當前線程名字:"+this.getName())
}
}.start();
}
4.3,獲取當前線程對象:Thread.currentThread
public static void main(String[] args) {
new Thread(new Runnable() {
public void run() {
Thread.currentThread().setName("Window");
}
}).start();
//獲取主線程的名字
Thread.currentThread.getName();
}
4.4,讓線程休眠的方法:Thread.sleep(休眠時間,單位毫秒);
4.5,join()線程加入方法:讓當前線程暫停, 等待指定的線程執行結束後, 當前線程再繼續執行;
public static void main(String[] args) {
final Thread t1 = new Thread(){
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(this.getName()+ " aaaaaaa");
}
};
};
Thread t2 = new Thread(){
public void run() {
for (int i = 0; i < 10; i++) {
if(i==2){
try {
t1.join();//t1線程加入,t2等待t1執行完成再繼續執行;
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.println(this.getName()+ " bbbbbbbbbbbbbbbbbb");
}
};
};
t1.start();
t2.start();
}
打印輸出:
Thread-0 aaaaaaa
Thread-1 bbbbbbbbbbbbbbbbbb
Thread-0 aaaaaaa
Thread-1 bbbbbbbbbbbbbbbbbb
Thread-0 aaaaaaa
Thread-0 aaaaaaa
Thread-0 aaaaaaa
Thread-0 aaaaaaa
Thread-0 aaaaaaa
Thread-0 aaaaaaa
Thread-0 aaaaaaa
Thread-0 aaaaaaa
Thread-1 bbbbbbbbbbbbbbbbbb
Thread-1 bbbbbbbbbbbbbbbbbb
Thread-1 bbbbbbbbbbbbbbbbbb
Thread-1 bbbbbbbbbbbbbbbbbb
Thread-1 bbbbbbbbbbbbbbbbbb
Thread-1 bbbbbbbbbbbbbbbbbb
Thread-1 bbbbbbbbbbbbbbbbbb
Thread-1 bbbbbbbbbbbbbbbbbb
4.6,setPriority()設置線程優先級
優先級從0(Thread.MIN_PRIORITY)到10(Thread.MAX_PRIORITY)範圍內;
默認優先級:Thread.NORM_PRIORITY=5
4.7,yield() 設置線程禮讓,讓出cpu,讓其他線程執行;
五,線程同步和線程安全
https://blog.csdn.net/ezconn/article/details/100060812
六,線程間通信
6.1,什麼時候需要通信
多個線程併發執行時, 在默認情況下CPU是隨機切換線程的
如果我們希望他們有規律的執行, 就可以使用通信, 例如每個線程執行一次打印
6.2,兩個線程間的通信
通過wait()和notify()方法實現,wait()方法讓當前線程等待,notify()喚醒正在等待的線程;
例如:兩個線程實現循環重複打印兩句詩
實現:
public class ThreadDemo1 {
public static void main(String[] args) {
final Printer p = new Printer();
new Thread(){
@Override
public void run() {
// TODO Auto-generated method stub
super.run();
int i =0;
while (i<1000) {
i++;
try {
p.print1();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}.start();
new Thread(){
@Override
public void run() {
// TODO Auto-generated method stub
super.run();
int i =0;
while (i<1000) {
i++;
try {
p.print2();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}.start();
}
}
class Printer{
int flag = 0;
public synchronized void print1() throws InterruptedException{//同步方法默認的鎖對象是this
if(flag!=0){
this.wait();//同步鎖對象調用wait()
}
System.out.print("窗");
System.out.print("前");
System.out.print("明");
System.out.print("月");
System.out.print("光");
System.out.print("\r\n");
flag = 2;
this.notify();//同步鎖對象調用notify()
}
public synchronized void print2() throws InterruptedException{//同步方法默認的鎖對象是this
if(flag!=2){
this.wait(); //同步鎖對象調用wait()
}
System.out.print("疑");
System.out.print("是");
System.out.print("地");
System.out.print("上");
System.out.print("霜");
System.out.print("\r\n");
flag = 1;
this.notify();//同步鎖對象調用notify()
}
}
注意:wait()和notify()這兩個方法必須在同步代碼中執行, 並且使用同步鎖對象來調用
6.3,三個或者三個以上的線程通信
通過wait()讓當前線程等待,notifyAll()方法是喚醒所有線程;
public class ThreadDemo2 {
public static void main(String[] args) {
final Printer2 p = new Printer2();
new Thread(){
@Override
public void run() {
// TODO Auto-generated method stub
super.run();
int i =0;
while (i<1000) {
i++;
try {
p.print1();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}.start();
new Thread(){
@Override
public void run() {
// TODO Auto-generated method stub
super.run();
int i =0;
while (i<1000) {
i++;
try {
p.print2();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}.start();
new Thread(){
@Override
public void run() {
// TODO Auto-generated method stub
super.run();
int i =0;
while (i<1000) {
i++;
try {
p.print3();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}.start();
new Thread(){
@Override
public void run() {
// TODO Auto-generated method stub
super.run();
int i =0;
while (i<1000) {
i++;
try {
p.print4();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}.start();
}
}
class Printer2{
int flag = 0;
public synchronized void print1() throws InterruptedException{
while(flag!=0){
this.wait();
}
System.out.print("窗");
System.out.print("前");
System.out.print("明");
System.out.print("月");
System.out.print("光");
System.out.print("\r\n");
flag = 1;
this.notifyAll(); //
}
public synchronized void print2() throws InterruptedException{
while(flag!=1){
this.wait();
}
System.out.print("疑");
System.out.print("是");
System.out.print("地");
System.out.print("上");
System.out.print("霜");
System.out.print("\r\n");
flag = 2;
this.notifyAll();
}
public synchronized void print3() throws InterruptedException{
while(flag!=2){
this.wait();
}
System.out.print("舉");
System.out.print("頭");
System.out.print("望");
System.out.print("明");
System.out.print("月");
System.out.print("\r\n");
flag = 3;
this.notifyAll();
}
public synchronized void print4() throws InterruptedException{
while(flag!=3){
this.wait();
}
System.out.print("低");
System.out.print("頭");
System.out.print("思");
System.out.print("故");
System.out.print("鄉");
System.out.print("\r\n");
flag = 0;
this.notifyAll();
}
}
6.4,JDK1.5之前通過上面的方式,實現多個線程間通信,1.5版本增加了一個互斥鎖ReentrantLock實現;
使用ReentrantLock類中的lock()和unLock()方法替代synchronized實現同步;然後再ReentrantLock創建Condition(監視器)子類對象,每個線程中放一個Condition;通過Condition的await()和signal() 方法實現當前線程等待和喚醒指定的線程;await()相當於Object中的wait()方法;signal()和Object中的notify()和notifyAll()方法有點不一樣;
例如:
public class fThreadDemo3 {
public static void main(String[] args) {
final Printer3 p = new Printer3();
new Thread() {
@Override
public void run() {
// TODO Auto-generated method stub
super.run();
int i = 0;
while (i < 1000) {
i++;
try {
p.print1();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}.start();
new Thread() {
@Override
public void run() {
// TODO Auto-generated method stub
super.run();
int i = 0;
while (i < 1000) {
i++;
try {
p.print2();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}.start();
new Thread() {
@Override
public void run() {
// TODO Auto-generated method stub
super.run();
int i = 0;
while (i < 1000) {
i++;
try {
p.print3();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}.start();
new Thread() {
@Override
public void run() {
// TODO Auto-generated method stub
super.run();
int i = 0;
while (i < 1000) {
i++;
try {
p.print4();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}.start();
}
}
class Printer3 {
int flag = 0;
ReentrantLock rl = new ReentrantLock();
Condition c1 = rl.newCondition();
Condition c2 = rl.newCondition();
Condition c3 = rl.newCondition();
Condition c4 = rl.newCondition();
public void print1() throws InterruptedException {
rl.lock();
try {
if (flag != 0) {
c1.await();
}
System.out.print("窗");
System.out.print("前");
System.out.print("明");
System.out.print("月");
System.out.print("光");
System.out.print("\r\n");
flag = 1;
c2.signal();
} finally{
rl.unlock();
}
}
public void print2() throws InterruptedException {
rl.lock();
try {
if (flag != 1) {
c2.await();
}
System.out.print("疑");
System.out.print("是");
System.out.print("地");
System.out.print("上");
System.out.print("霜");
System.out.print("\r\n");
flag = 2;
c3.signal();
} finally {
rl.unlock();
}
}
public void print3() throws InterruptedException {
rl.lock();
try {
if (flag != 2) {
c3.await();
}
System.out.print("舉");
System.out.print("頭");
System.out.print("望");
System.out.print("明");
System.out.print("月");
System.out.print("\r\n");
flag = 3;
c4.signal();
} finally{
rl.unlock();
}
}
public void print4() throws InterruptedException {
rl.lock();
try {
if (flag != 3) {
c4.await();
}
System.out.print("低");
System.out.print("頭");
System.out.print("思");
System.out.print("故");
System.out.print("鄉");
System.out.print("\r\n");
flag = 0;
c1.signal();
} finally {
rl.unlock();
}
}
}