java ee13_一口气了解多线程及其Java实现
進(jìn)程:進(jìn)程就是應(yīng)用程序在內(nèi)存中分配的空間,也就是正在運(yùn)行的程序,各個(gè)進(jìn)程之間不干擾。同時(shí)進(jìn)程保存著程序每一個(gè)時(shí)刻運(yùn)行的狀態(tài)。
程序:用某種編程語(yǔ)言(java、python等)編寫(xiě),能夠完成一定任務(wù)或者功能的代碼集合,是指令和數(shù)據(jù)的有序集合,是一段靜態(tài)代碼。
線程:通常一個(gè)進(jìn)程中可以包含若干個(gè)線程。進(jìn)程單獨(dú)占有一定的內(nèi)存地址空間,而線程共享所屬進(jìn)程占有的內(nèi)存地址空間和資源。
在Java中,我們是如何使用多線程的呢?
1.使用Thread 類和 Runnalble 接?來(lái)實(shí)現(xiàn)自己的“線程”類。
// 繼承Thread類
public class MyThread1 extends Thread {
@Override
public void run() {
System.out.println("MyThread1*********");
}
}
// 實(shí)現(xiàn)Runnable接口
public class MyThread2 implements Runnable {
@Override
public void run() {
System.out.println("MyThread2@@@@@@@@@");
}
}
public class ThreadTest {
public static void main(String[] args) {
Thread myThread1 = new MyThread1();
myThread1.start();
Thread myThread2 = new Thread(new MyThread2());
myThread2.start();
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("MyThread3########");
}
}).start();
new Thread(() -> {
System.out.println("MyThread4%%%%%%%%");
}).start();
}
}
2.我們使用Runnable和Thread來(lái)創(chuàng)建一個(gè)新的線程。但是他們有一個(gè)弊端,就是run方法是沒(méi)有返回值的。而有時(shí)候我們希望開(kāi)啟一個(gè)線程去執(zhí)行一個(gè)任務(wù),并且這個(gè)任務(wù)執(zhí)行完成之后有一個(gè)返回值。JDK提供了Callable接口和Future類為我們解決了這個(gè)問(wèn)題。
public class CallableTask implements Callable {
@Override
public String call() throws Exception {
Thread.sleep(1000);
return "中獎(jiǎng)啦~";
}
public static void main(String args[]) throws ExecutionException, InterruptedException {
// 1.使用Future接口
// ExecutorService可以使用submit方法來(lái)讓?個(gè)Callable接口執(zhí)行。
ExecutorService executor1 = Executors.newCachedThreadPool();
CallableTask task = new CallableTask();
Future result = executor1.submit(task);
// 注意調(diào)用get方法會(huì)阻塞當(dāng)前線程,直到得到結(jié)果。
// 所以實(shí)際編碼中建議使用可以設(shè)置超時(shí)時(shí)間的重載get方法。
System.out.println(result.get() + "@@");
// 2.使用FutureTask類:FutureTask 是實(shí)現(xiàn)的 RunnableFuture 接口的,
// 而 RunnableFuture 接口同時(shí)繼承了 Runnable 接口和 Future 接口。
ExecutorService executor2 = Executors.newCachedThreadPool();
FutureTask futureTask = new FutureTask<>(new CallableTask());
executor2.submit(futureTask);
System.out.println(futureTask.get() + "**");
}
}
什么是線程組和線程優(yōu)先級(jí)?
1.線程組:每個(gè)Thread必然存在于?個(gè)ThreadGroup中,Thread不能獨(dú)?于ThreadGroup存在。執(zhí)?main()?法線程的名字是main,如果在new Thread時(shí)沒(méi)有顯式指定,那么默認(rèn)將?線程(當(dāng)前執(zhí)?new Thread的線程)線程組設(shè)置為??的線程組。
public class ThreadGroupTest {
public static void main(String[] args) {
Thread testThread1 = new Thread(() -> {
System.out.println("testThread1當(dāng)前線程組名字:" +
Thread.currentThread().getThreadGroup().getName());
System.out.println("testThread1線程名字:" +
Thread.currentThread().getName());
});
Thread testThread2 = new Thread(() -> {
System.out.println("testThread2當(dāng)前線程組名字:" +
Thread.currentThread().getThreadGroup().getName());
System.out.println("testThread2線程名字:" +
Thread.currentThread().getName());
});
testThread1.start();
testThread2.start();
System.out.println("執(zhí)?main?法線程名字:" + Thread.currentThread().getName());
}
}
輸出結(jié)果:
執(zhí)?main?法線程名字:main
testThread2當(dāng)前線程組名字:main
testThread2線程名字:Thread-1
testThread1當(dāng)前線程組名字:main
testThread1線程名字:Thread-0
2.線程優(yōu)先級(jí):Java中線程優(yōu)先級(jí)可以指定,范圍是1~10 。但并不是所有的操作系統(tǒng)都支持10級(jí)優(yōu)先級(jí)的劃分,Java只是給操作系統(tǒng)一個(gè)優(yōu)先級(jí)的參考值,線程最終在操作系統(tǒng)的優(yōu)先級(jí)是多少還是由操作系統(tǒng)決定。通常情況下,?優(yōu)先級(jí)的線程將會(huì)?低優(yōu)先級(jí)的線程有更?的?率得到執(zhí)?。
public static void main(String[] args) {
Thread a = new Thread();
System.out.println("我是默認(rèn)線程優(yōu)先級(jí):"+a.getPriority());
Thread b = new Thread();
b.setPriority(10);
System.out.println("我是設(shè)置過(guò)的線程優(yōu)先級(jí):"+b.getPriority());
}
我是默認(rèn)線程優(yōu)先級(jí):5
我是設(shè)置過(guò)的線程優(yōu)先級(jí):10
線程狀態(tài)
線程狀態(tài)轉(zhuǎn)換圖
鎖與同步
無(wú)鎖的時(shí)候,如下代碼中兩個(gè)線程A、B各自執(zhí)行:
public class ThreadNoneLock {
static class ThreadA implements Runnable {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("Thread A " + i);
}
}
}
static class ThreadB implements Runnable {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("Thread B " + i);
}
}
}
public static void main(String[] args) {
new Thread(new ThreadA()).start();
new Thread(new ThreadB()).start();
}
}
Thread A 0
Thread A 1
Thread A 2
Thread B 0
Thread B 1
Thread B 2
Thread B 3
...
Thread B 97
Thread B 98
Thread B 99
Thread A 3
Thread A 4
Thread A 5
加鎖之后(用synchronized關(guān)鍵字加上了同一個(gè)對(duì)象鎖lock),A線程先執(zhí)行完,B隨后執(zhí)行:
public class ThreadWithLock {
private static Object lock = new Object();
static class ThreadA implements Runnable {
@Override
public void run() {
synchronized (lock) {
for (int i = 0; i < 100; i++) {
System.out.println("Thread A " + i);
}
}
}
}
static class ThreadB implements Runnable {
@Override
public void run() {
synchronized (lock) {
for (int i = 0; i < 100; i++) {
System.out.println("Thread B " + i);
}
}
}
}
public static void main(String[] args) throws InterruptedException {
new Thread(new ThreadA()).start();
Thread.sleep(10);
new Thread(new ThreadB()).start();
}
}
Thread A 97
Thread A 98
Thread A 99
Thread B 0
Thread B 1
Thread B 2
等待/通知機(jī)制
如下例子中,線程A和線程B首先打印出自己需要的東西,然后使用notify()方法叫醒另一個(gè)正在等待的線程,然后自己使用wait()方法陷入等待并釋放lock鎖。
public class WaitAndNotify {
private static Object lock = new Object();
static class ThreadA implements Runnable {
@Override
public void run() {
synchronized (lock) {
for (int i = 0; i < 5; i++) {
try {
System.out.println("Thread A: " + i);
lock.notify();
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
lock.notify();
}
}
}
static class ThreadB implements Runnable {
@Override
public void run() {
synchronized (lock) {
for (int i = 0; i < 5; i++) {
try {
System.out.println("Thread B: " + i);
lock.notify();
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
lock.notify();
}
}
}
public static void main(String[] args) throws InterruptedException {
new Thread(new ThreadA()).start();
Thread.sleep(1000);
new Thread(new ThreadB()).start();
}
}
Thread A: 0
Thread B: 0
Thread A: 1
Thread B: 1
Thread A: 2
Thread B: 2
Thread A: 3
Thread B: 3
Thread A: 4
Thread B: 4
信號(hào)量
public class Signal {
/**
* volatile關(guān)鍵字可以保證內(nèi)存的可見(jiàn)性,如果用volatile關(guān)鍵字聲明了一個(gè)變量,在一個(gè)線程里改變了這個(gè)變量值,那其他線程是立馬可見(jiàn)更改后的值的。
*/
private static volatile int i = 0;
static class ThreadA implements Runnable {
@Override
public void run() {
while (i < 10) {
if (i % 2 == 0) {
System.out.println("Thread A: "+ i);
synchronized (this) {
i++;
}
}
}
}
}
static class ThreadB implements Runnable {
@Override
public void run() {
while (i < 10) {
if (i % 2 == 1) {
System.out.println("Thread B: " + i);
synchronized (this) {
i++;
}
}
}
}
}
public static void main(String[] args) {
new Thread(new ThreadA()).start();
new Thread(new ThreadB()).start();
}
}
Thread A: 0
Thread B: 1
Thread A: 2
Thread B: 3
Thread A: 4
Thread B: 5
Thread A: 6
Thread B: 7
Thread A: 8
Thread B: 9
ThreadLocal
多線程訪問(wèn)同一個(gè)共享變量的時(shí)候容易出現(xiàn)并發(fā)問(wèn)題,特別是多個(gè)線程對(duì)同一個(gè)變量進(jìn)行寫(xiě)入的時(shí)候,為了保證線程安全,一般使用者在訪問(wèn)共享變量的時(shí)候需要進(jìn)行額外的同步措施才能保證線程安全性。TreadLocal是除了加鎖這種同步方式之外的一種保證規(guī)避多線程訪問(wèn)出現(xiàn)線程不安全的方法,當(dāng)我們?cè)趧?chuàng)建一個(gè)變量后,如果每個(gè)線程對(duì)其進(jìn)行訪問(wèn)的時(shí)候訪問(wèn)的都是線程自己的變量這樣就不會(huì)存在線程不安全的問(wèn)題了。
線程池
為什么要使用線程池:1.創(chuàng)建/銷(xiāo)毀線程需要消耗系統(tǒng)資源,線程池可以復(fù)用已創(chuàng)建的線程。2.控制并發(fā)的數(shù)量。并發(fā)數(shù)量過(guò)多,可能會(huì)導(dǎo)致資源消耗過(guò)多,從而造成服務(wù)器崩潰。3.可以對(duì)線程做統(tǒng)一管理。
ThreadPoolExecutor
public class ThreadPoolExecutorTest {
public static void main(String[] args) {
// ThreadPoolExecutor提供了四個(gè)構(gòu)造函數(shù): 1至5; 1至5 + 6; 1至5 + 7; 1至5 + 6 + 7
// 參數(shù)一:int corePoolSize 該線程池中核心線程數(shù)最大值,線程池新建的時(shí)候,如果當(dāng)前線程總數(shù)小于corePoolSize,則新建核心線程,如果超過(guò)corePoolSize,則新建的是非核心線程
// 參數(shù)二:int maximumPoolSize 該線程池中線程總數(shù)最大值
// 參數(shù)三:long keepAliveTime 該線程池中非核心線程閑置超時(shí)時(shí)長(zhǎng)
// 參數(shù)四:TimeUnit unit keepAliveTime的單位
// 參數(shù)五:BlockingQueue workQueue 該線程池中的任務(wù)隊(duì)列,維護(hù)著等待執(zhí)行的Runnable對(duì)象。常見(jiàn)類型:
// SynchronousQueue:這個(gè)隊(duì)列接收到任務(wù)的時(shí)候,會(huì)直接提交給線程處理,而不保留它,如果所有線程都在工作怎么辦?那就新建一個(gè)線程來(lái)處理這個(gè)任務(wù)!所以為了保證不出現(xiàn)的錯(cuò)誤,使用這個(gè)類型隊(duì)列的時(shí)候,maximumPoolSize一般指定成Integer.MAX_VALUE,即無(wú)限大
// LinkedBlockingQueue:這個(gè)隊(duì)列接收到任務(wù)的時(shí)候,如果當(dāng)前線程數(shù)小于核心線程數(shù),則新建線程(核心線程)處理任務(wù);如果當(dāng)前線程數(shù)等于核心線程數(shù),則進(jìn)入隊(duì)列等待。由于這個(gè)隊(duì)列沒(méi)有最大值限制,即所有超過(guò)核心線程數(shù)的任務(wù)都將被添加到隊(duì)列中,這也就導(dǎo)致了maximumPoolSize的設(shè)定失效,因?yàn)榭偩€程數(shù)永遠(yuǎn)不會(huì)超過(guò)corePoolSize
// ArrayBlockingQueue:可以限定隊(duì)列的長(zhǎng)度,接收到任務(wù)的時(shí)候,如果沒(méi)有達(dá)到corePoolSize的值,則新建線程(核心線程)執(zhí)行任務(wù),如果達(dá)到了,則入隊(duì)等候,如果隊(duì)列已滿,則新建線程(非核心線程)執(zhí)行任務(wù),又如果總線程數(shù)到了maximumPoolSize,并且隊(duì)列也滿了,則發(fā)生錯(cuò)誤
// DelayQueue:隊(duì)列內(nèi)元素必須實(shí)現(xiàn)Delayed接口,這就意味著你傳進(jìn)去的任務(wù)必須先實(shí)現(xiàn)Delayed接口。這個(gè)隊(duì)列接收到任務(wù)時(shí),首先先入隊(duì),只有達(dá)到了指定的延時(shí)時(shí)間,才會(huì)執(zhí)行任務(wù)
// 參數(shù)六:ThreadFactory threadFactory 給線程起名字
// 參數(shù)七:RejectedExecutionHandler handler 異常處理
ThreadFactory threadFactory = new ThreadFactory() {
@Override
public Thread newThread(Runnable r) {
return new Thread(r,"thread name...");
}
};
// lambda表達(dá)式寫(xiě)法
// ThreadFactory threadFactory = (Runnable r) -> new Thread(r,"thread name");
ThreadPoolExecutor executor = new ThreadPoolExecutor(5,5,0L, TimeUnit.SECONDS, new LinkedBlockingDeque<>(), threadFactory, new ThreadPoolExecutor.AbortPolicy());
executor.execute(new Runnable() {
@Override
public void run() {
System.out.println("@@@@");
}
});
}
}
總結(jié)
以上是生活随笔為你收集整理的java ee13_一口气了解多线程及其Java实现的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: java session 同步_sess
- 下一篇: java构造函数重载继承_Java基础-