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