程序,進程,線程
- 程序:爲完成特定任務,使用某種語言編寫的一組指令的集合,是一段靜態的代碼。
- 進程:程序的一次運行過程,或者是正在運行的一個程序。進程是資源分配的基本單位。
- 線程:線程由進程進一步細化而來,是一個程序內部的一條執行路徑。線程是調度和執行的單位,每個線程擁有獨立的運行棧和程序計數器,線程開銷很小。
Thread生命週期
Thread類中的常用方法
- start():啓動當前線程;調用當前線程的run()。
- run():通常需要重寫Thread類中的此方法,將創建的線程要執行的操作聲明在此方法中。
- currentThread():靜態方法,返回執行當前代碼的線程。
- getName():獲取當前線程的名字。
- setName():設置當前線程的名字。
- yield():釋放當前cpu的執行權。
- join():在線程a中調用線程b的join()方法,此時線程a就進入阻塞狀態,直到線程b完全執行完以後,線程a才結束阻塞狀態。
- sleep(long millitime):讓當前線程“睡眠”指定的millitime毫秒。在指定的millitime毫秒時間內,當前線程是阻塞狀態。
- isAlive():判斷當前線程是否存活。
- getPriority():獲取線程的優先級。
- setPriority(int p):設置線程的優先級。
- 線程的優先級有以下三種,MAX_PRIORITY:10;MIN _PRIORITY:1;NORM_PRIORITY:5。
創建多線程的方式
繼承Thread類
步驟:
- 創建一個繼承於Thread類的子類;
- 重寫Thread類中的run()方法,將次線程執行的操作聲明在run()方法中。
- 創建Thread類的子類對象。
- 通過此對象調用start()。
舉例:三個窗口進行賣票(存在線程安全問題)
class MThread extends Thread{
private static int tickets = 100;
@Override
public void run() {
while(true){
if(tickets > 0){
System.out.println(getName() + "賣票,票號爲:" + tickets--);
}else{
break;
}
}
}
}
public class ThreadTest {
public static void main(String[] args) {
MThread t1 = new MThread ();
MThread t2 = new MThread ();
MThread t3 = new MThread ();
t1.setName("窗口1");
t2.setName("窗口2");
t3.setName("窗口3");
t1.start();
t2.start();
t3.start();
}
}
說明:侷限於類的單繼承性。
實現Runnable接口
步驟:
- 創建一個實現Runnable接口的類。
- 實現類去實現Runnable中的抽象方法:run()。
- 創建實現類的對象。
- 將此對象作爲參數傳遞到Thread類的構造器中,創建Thread類的對象。
- 通過Thread類的對象調用start()。
舉例:三個窗口進行賣票(存在線程安全問題)
class MThread implements Runnable{
private int tickets = 100;
@Override
public void run() {
while(true){
if(tickets > 0){
System.out.println(Thread.currentThread().getName() + "賣票,票號爲:" + tickets--);
}else{
break;
}
}
}
}
public class ThreadTest {
public static void main(String[] args) {
MThread m = new MThread();
Thread t1 = new Thread(m);
Thread t2 = new Thread(m);
Thread t3 = new Thread(m);
t1.setName("窗口1");
t2.setName("窗口2");
t3.setName("窗口3");
t1.start();
t2.start();
t3.start();
}
}
說明:不會侷限於類的單繼承性;適合處理多個線程共享數據的情況。
實現Callable接口
步驟:
- 創建一個實現Callable的實現類。
- 實現call方法,將此線程需要執行的操作聲明在call()中。
- 創建Callable接口實現類的對象。
- 將此Callable接口實現類的對象作爲參數傳遞到FutureTask構造器中,創建FutureTask的對象。
- 將FutureTask的對象作爲參數傳遞到Thread類的構造器中,創建Thread對象,並調用start()。
舉例:遍歷100以內的偶數,並且計算他們的和
class MThread implements Callable{
@Override
public Object call() throws Exception {
int sum = 0;
for (int i = 2; i < 101; i+=2) {
System.out.println(i);
sum += i;
}
}
return sum;
}
}
public class ThreadTest {
public static void main(String[] args) {
MThread thread = new MThread ();
FutureTask futureTask = new FutureTask(thread);
new Thread(futureTask).start();
try {
//get()返回值即爲FutureTask構造器參數Callable實現類重寫的call()的返回值。
Object sum = futureTask.get();
System.out.println("總和爲:" + sum);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
說明:call()方法有返回值,並且可以拋出異常;callable支持泛型。
線程池
步驟:
- 提供指定線程數量的線程池。
- 執行指定的線程的操作。需要提供實現Runnable接口或Callable接口實現類的對象。
- 關閉線程池。
舉例:兩個線程遍歷100以內的奇偶數
class NumberThread implements Runnable{
@Override
public void run() {
for(int i = 1;i < 101; i+=2){
System.out.println(Thread.currentThread().getName() + ": " + i);
}
}
}
class NumberThread1 implements Runnable{
@Override
public void run() {
for(int i = 2;i < 101; i+=2){
System.out.println(Thread.currentThread().getName() + ": " + i);
}
}
}
public class ThreadPool {
public static void main(String[] args) {
ExecutorService service = Executors.newFixedThreadPool(10);
ThreadPoolExecutor service1 = (ThreadPoolExecutor) service;
service.execute(new NumberThread());//適用於Runnable
service.execute(new NumberThread1());//適用於Runnable
//service.submit(Callable callable);//使用於Callable
service.shutdown();
}
}
說明:提高了響應速度;降低資源消耗;便於線程管理。
線程同步機制
解決線程安全問題
同步代碼塊
synchronized(對象){//需要被同步的代碼}
舉例:三個窗口賣票
class Windows extends Thread{
private static int tickets = 100;
@Override
public void run() {
while(true){
synchronized(Windows.class){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
if(tickets > 0){
System.out.println(getName() + "賣票,票號爲:" + tickets--);
}else{
break;
}
}
}
}
}
public class Test {
public static void main(String[] args) {
Windows t1 = new Windows();
Windows t2 = new Windows();
Windows t3 = new Windows();
t1.setName("窗口1");
t2.setName("窗口2");
t3.setName("窗口3");
t1.start();
t2.start();
t3.start();
}
}
同步方法
將synchronized放到方法的聲明中。
舉例:三個窗口賣票。
class Windows implements Runnable {
private int tickets = 100;
@Override
public void run() {
while(true){
show();
}
}
private synchronized void show(){
if(tickets > 0){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "賣票,票號爲:" + tickets--);
}
}
}
public class Test{
public static void main(String[] args) {
Windows t = new Windows();
Thread t1 = new Thread(t);
Thread t2 = new Thread(t);
Thread t3 = new Thread(t);
t1.setName("窗口1");
t2.setName("窗口2");
t3.setName("窗口3");
t1.start();t2.start();t3.start();
}
}
Lock
class A{
private final ReentrantLock lock = new ReentrantLock();
public void m(){
lock.lock();
try{
//保證線程安全的代碼
}
finally{
lock.unlock();
}
}
}
舉例:三個窗口賣票
class Window implements Runnable{
private int ticket = 100;
private ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
while(true){
try{
lock.lock();
if(ticket > 0){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "賣票,票號爲:" + ticket--);
}
}finally {
lock.unlock();
}
}
}
}
public class LockTest {
public static void main(String[] args) {
Window w = new Window();
Thread t1 = new Thread(w);
Thread t2 = new Thread(w);
Thread t3 = new Thread(w);
t1.setName("線程1");t2.setName("線程2");t3.setName("線程3");
t1.start();t2.start();t3.start();
}
}
synchronized和lock對比:
- lock是顯示鎖,手動開啓和關閉;synchronized是隱式鎖,出了作用域自動釋放。
- lock只有代碼塊鎖;synchronized有代碼塊鎖和方法鎖。
- 使用lock鎖,JVM將花費較少的時間來調度線程,性能更好;並且具有更好的擴展性。
線程通信
三個方法
- wait():一旦執行此方法,當前線程就進入阻塞狀態,並釋放同步監視器。
- )notify():一旦執行此方法,就會喚醒被wait的一個線程。如果有多個線程被wait,就喚醒優先級高的那個。
- notifyAll():一旦執行此方法,就會喚醒所有被wait的線程。
說明
- wait(),notify(),notifyAll()三個方法必須使用在同步代碼塊或同步方法中。
- wait(),notify(),notifyAll()三個方法的調用者必須是同步代碼塊或同步方法中的同步監視器。否則,會出現IllegalMonitorStateException異常。
- wait(),notify(),notifyAll()三個方法是定義在java.lang.Object類中。
舉例:生產者消費者問題
class Clerk{
private int productCount = 0;
public synchronized void produceProduct(){
if(productCount < 20){
productCount ++;
System.out.println(Thread.currentThread().getName() + "生產第" + productCount + "個產品");
notify();
}else {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public synchronized void consumeProduct() {
if(productCount > 0){
System.out.println(Thread.currentThread().getName() + "消費第" + productCount + "個產品");
productCount --;
notify();
}else {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class Productor extends Thread{
private Clerk clerk;
public Productor(Clerk clerk) {
this.clerk = clerk;
}
@Override
public void run() {
System.out.println("生產者" + Thread.currentThread().getName() + "開始生產");
while(true){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
clerk.produceProduct();
}
}
}
class Consumer extends Thread{
private Clerk clerk;
public Consumer(Clerk clerk) {
this.clerk = clerk;
}
@Override
public void run() {
System.out.println("消費者" + Thread.currentThread().getName() + "取走產品");
while(true){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
clerk.consumeProduct();
}
}
}
public class ProductTest {
public static void main(String[] args) {
Clerk clerk = new Clerk();
Productor p1 = new Productor(clerk);
p1.setName("生產者1");
Consumer c1 = new Consumer(clerk);
c1.setName("消費者1");
p1.start();c1.start();
}
}
最後
在文章的最後作者爲大家整理了很多資料!包括java核心知識點+全套架構師學習資料和視頻+一線大廠面試寶典+面試簡歷模板+阿里美團網易騰訊小米愛奇藝快手嗶哩嗶哩面試題+Spring源碼合集+Java架構實戰電子書等等!
全部免費分享給大家,歡迎關注公衆號:前程有光領取!