Java多線程(一):
https://blog.csdn.net/Veer_c/article/details/103842078
Java多線程(二):
https://blog.csdn.net/Veer_c/article/details/103842263
Java多線程(三):
https://blog.csdn.net/Veer_c/article/details/103842317
Java多線程(四):
https://blog.csdn.net/Veer_c/article/details/103842602
多線程
線程是依賴於進程而存在的。
進程:正在運行的應用程序,每一正在運行的程序都會對應一個進程。
線程:進程的執行路徑,執行單元
單線程和多線程的區別:
比如說有如下代碼:
public class Test {
public static void main(String[] args) {
代碼1;
show1();
代碼2;
show2();
代碼3...
}
public static void show1(){
代碼11;
代碼12;
}
public staic void show2(){
代碼22;
代碼23;
}
}
在代碼執行的時候,由於多線程的存在,效率會很高
多線程的兩種方案:
(1)繼承Thread類:
public class MyThread extends Thread{
//1.繼承Thread類
//2.重寫run方法,重寫run方法中的代碼之後,當我們啓動了這個線程之後,我們的這個線程就會執行run方法中的代碼
@Override
public void run() {
//需求:開啓該線程之後,執行一個for循環
for (int i = 0; i < 10; i++) {
System.out.println(i);
}
}
}
(2)實現Runable接口:
public class MyThread implements Runnable{
//實現runnable接口
@Override
public void run() {
//啓動該線程對象之後,需要執行的代碼
for (int i = 0; i < 10; i++) {
System.out.println(i);
}
}
}
多線程的問題:
啓動線程我們可以利用線程對象調用start()方法
start()和run()的區別
start()是在啓動線程後執行run方法裏裏面的代碼,而直接利用調用run()方法來執行方法,是在主線程裏面運行該方法
start():1.開啓線程 2.執行run()方法裏面的代碼
run():執行的是線程裏面執行的代碼,並不會開啓線程
爲什麼要重寫run()
因爲每個線程需要執行的代碼都是都是不一樣的,
我們需要將每個線程自己獨立執行的代碼寫到run()方法中執行
線程不可以被多次啓動,否則會出現:java.lang.IllegalThreadStateException
因爲在執行同一個線程的時候,一個線程剛開始執行,但是同時又重新開始,所以會拋出異常;
注意:如果是倆個不同的線程,可以同時執行
public class MyThread extends Thread{
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(i);
}
}
}
public class Test {
public static void main(String[] args) {
//B:start()和run()的區別
// 創建一個線程獨享
MyThread mt = new MyThread();
MyThread mt2 = new MyThread();
//mt.start();//1.首先開啓了一個獨立的線程 2.讓這個獨立的線程執行run方法中的代碼
System.out.println("--------");
//mt.run();//1.普通的創對象,調方法 2.代碼實在主線程執行,不會開闢新線程
//D:線程可以多次啓動嗎
mt.start();
//mt2.start();//如果是不同的線程對象,是可以同時開啓的
//mt.start();//線程是不能多次啓動的
}
}
線程的調度和控制
線程休眠(Thread.sleep(毫秒值))
線程名稱(setName(),getName();)
線程的調度及優先級setPriority(10)(注意默認值是5,區間在1-10之間)
線程優先級:
當我們多個線程開始運行的時候,線程會槍戰CPU的執行權,線程優先級就是你搶到CPU執行權的概率。
public class MyThread extends Thread{
@Override
public void run() {
for (int i = 0; i < 100; i++) {
//線程休眠(Thread.sleep(毫秒值))
try {
Thread.sleep(1000);//在此處出現的異常我們只能抓取,不能拋出
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//獲取執行線程的姓名
System.out.println(this.getName()+i);
}
}
}
public class Test {
public static void main(String[] args) {
//線程名稱(setName(),getName();)
//創建3個線程對象
MyThread t1 = new MyThread();
MyThread t2 = new MyThread();
MyThread t3 = new MyThread();
//給三個線程設置姓名
t1.setName("劉備");
t2.setName("張飛");
t3.setName("關羽");
//設置線程的優先級
//線程的調度及優先級setPriority(10)(注意默認值是5,區間在1-10之間)
//t1.setPriority(100);//設置的區間必須在1-10之間
t1.setPriority(10);
//開啓線程
t1.start();
t2.start();
t3.start();
}
}
多線程案例
1.繼承Thread賣票。
public class MyThread extends Thread{
//共有100張票,將ticket改爲靜態之後,被類的所有對象所共享
static int ticket = 100;
@Override
public void run() {
//用一個while true循環模擬三個窗口一直處於打開的狀態
while (true) {
//只有當ticket>0的時候,纔可以出售票
if (ticket>0) {
System.out.println(getName()+"正在出售第:"+ticket--+"張票");
}
}
}
}
//5.1繼承Thread賣票
public class Test {
public static void main(String[] args) {
//創建三個線程模擬三個售票窗口
MyThread mt1 = new MyThread();
MyThread mt2 = new MyThread();
MyThread mt3 = new MyThread();
//給線程設置名稱
mt1.setName("窗口一");
mt2.setName("窗口二");
mt3.setName("窗口三");
//啓動線程,開啓售票
mt1.start();
mt2.start();
mt3.start();
}
}
2.實現Runnable賣票:
public class MyThread implements Runnable{
int ticket = 100;
@Override
public void run() {
while (true) {
if (ticket>0) {
System.out.println(Thread.currentThread().getName()+"正在出售第:"+ticket--+"張票");
}
}
}
}
public class Test {
public static void main(String[] args) {
//創建MyThread這個對象
MyThread mt = new MyThread();
//創建3個窗口
Thread t1 = new Thread(mt);
Thread t2 = new Thread(mt);
Thread t3 = new Thread(mt);
//給線程對象設置名稱
t1.setName("窗口一");
t2.setName("窗口二");
t3.setName("窗口三");
//啓動窗口開始售票
t1.start();
t2.start();
t3.start();
}
}
注意:在實現runnable藉口的時候,我們可以不用把ticket設置成static,因爲在實現runnable的時候,我們本來就創建了一個實現類對象,然後將是實現類對象轉換成三個不同的thread對象,所以我們的ticket本開就是共享的。
在實際生活中,我們買票的時候跟定會有時間上的延遲,所以我們給線程中加入延遲,按照真實的情景加入了延遲,卻發現出現了這樣的兩個問題:
相同的票賣了多次:CPU的一次操作必須是原子性的(操作是CPU執行一次就可以直接完成的)
出現了負數的票:隨機性和延遲導致的
出現上面的問題稱爲線程安全問題。
出現多線程安全問題的條件:
是否是多線程環境
是否有共享數據
是否有多條語句操作共享數據
如何解決多線程安全問題
線程安全執行效率會有所降低
同步代碼塊:
synchronized(對象) {
需要被同步的代碼。
}
需求:
1.測試不是同一把鎖的時候線程安全嗎?
答:當我們每個線程進入程序的時候,我們都會出創建一個把鎖,這樣執行還是會出錯的。
2.如果是同一把鎖線程安全嗎?
如果我們利用同一把鎖,則不會出現上面的問題,線程安全
1.synchronized的對象是什麼
答:任意對象 ,相當於是一把鎖,只要線程進去就把鎖鎖上
2.需要同步的代碼?
答:被線程執行的代碼
鎖對象問題:
同步代碼塊(定義一個抽象類,裏面專門定義一個鎖),任意對象
public class MyThread implements Runnable{
//定義100張票
int ticket = 100;
Object obj = new Object();
@Override
public void run() {
while (true) {
//同步代碼塊
//synchronized (new Object()) {//t1,t2,t3三個線程不共享同一把鎖每個線程都有自己的議案鎖
synchronized (obj) {//這樣3個線程纔可以共享同一把鎖
if (ticket>0) {
//考慮到實際的生活中,我們需要給每一個線程加入一定的延遲,模擬一下這種效果
try {
Thread.sleep(100);
/**
* 分析:爲什麼會出現兩張100張票
* t1搶佔到cpu的執行權,此時ticket=100,但是此刻休眠了
* 此時被t2搶佔到了cpu的執行權,此時ticket=100,
* t1,t2分別睡了100毫秒之後,分別醒來了。。
* t1此時出售第100張票
* t2此時出售第100張票
*/
/**
* 分析:爲什麼會出現0張票和-1張票
* 假設此時票池中僅剩1張票了,
* t1進來休眠了
* t2進來休眠了
* t3進來休眠了 */
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"正在出售第:"+ticket--+"張票");
/* t1醒來,出售的是第1張票,此時tickt=0
* t2醒來,出售第0張票,此時ticket=-1
* t3醒來,出售第-1張票,此時ticket=-2 */
/* ticket--這個動作一共包含幾步:
* 1.打印出ticket此刻本身的值
* 2.ticket自減1
* 3.將自減之後的ticket的最新的值賦值給變量ticket */
}
}
//當被同步的代碼執行完畢之後,t1手裏拿着的obj這個鎖纔會被釋放,
//t1,t2,t3重新搶佔cpu的執行權,誰搶到了繼續拿着obj這個鎖,執行同步代碼塊中的內容
}
}
同步方法(僅適用於實現runable接口)
public synchronized void sellTicket(){同步代碼}
//this
private static synchronized void sellTicket() {
if (ticket>0) {
//考慮到實際的生活中,我們需要給每一個線程加入一定的延遲,模擬一下這種效果
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} System.out.println(Thread.currentThread().getName()+"正在出售第:"+ticket--+"張票");
}
}
}
將synchronized關鍵字加到方法上
靜態同步方法
類的字節碼對象
public static synchronized void sellTicket() {
需要同步的代碼
}
匿名內部類的方式使用多線程
new Thread() {
public void run() {
...
}
}.start();
new Thread(new Runnable(){
public void run() {
...
}
}).start();
利用匿名內部類啓動多線程
public void run() {
while (true) {
if (x%2==0) {
synchronized (MyThread.class) {//這樣3個線程纔可以共享同一把鎖
if (ticket>0) {
//考慮到實際的生活中,我們需要給每一個線程加入一定的延遲,模擬一下這種效果
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} System.out.println(Thread.currentThread().getName()+"正在出售第:"+ticket--+"張票");
}
}
Java多線程(一):
https://blog.csdn.net/Veer_c/article/details/103842078
Java多線程(二):
https://blog.csdn.net/Veer_c/article/details/103842263
Java多線程(三):
https://blog.csdn.net/Veer_c/article/details/103842317
Java多線程(四):
https://blog.csdn.net/Veer_c/article/details/103842602