一:線程概述
1,進程:正在運行的程序,是系統進行資源分配和調用的獨立單位。每一個進程都有它自己的內存空間和系統資源。
2,線程:是進程中的單個順序控制流,是一條執行路徑一個進程如果只有一條執行路徑,則稱爲單線程程序。一個進程如果有多條執行路徑,則稱爲多線程程序。
二:線程的實現:
1,方式一:
編寫一個類繼承Thread類
重寫run()方法
線程
線程類:
public class MyThread extends Thread {
@Override
public void run() {
for (int x = 0; x < 200; x++) {
System.out.println(x);
}
}
}
測試類:
public class MyThreadDemo {
public static void main(String[] args) {
MyThread my1 = new MyThread();
MyThread my2 = new MyThread();
my1.start();
my2.start();
}
}
a,注意:
run和start的區別:
run():僅僅是封裝被線程執行的代碼,直接調用是普通方法
start():首先啓動了線程,然後再由jvm去調用該線程的run()方法。
b,爲什麼要重寫run方法
不是類中的所有代碼都需要被線程執行的。而這個時候,爲了區分哪些代碼能夠被線程執行,java提供了Thread 類中的run()用來包含那些被線程執行的代碼。
C:創建對象
D:啓動線程
c,線程名的獲取和設置
設置名稱: public final String setName()或者構造方法來設置線程的名稱。
獲取名稱:
public final String getName():獲取線程的名稱。
public static Thread currentThread():返回當前正在執行的線程對象
d,線程優先級
線程優先級默認爲5,範圍爲1至10;
通過:
public final int getPriority()
獲取優先級
通過:
public final void setPriority(int newPriority)
設置優先級別
e,線程控制
線程休眠
public static void sleep(long millis)
public class ThreadSleepDemo {
public static void main(String[] args) {
ThreadSleep ts1 = new ThreadSleep();
ThreadSleep ts2 = new ThreadSleep();
ThreadSleep ts3 = new ThreadSleep();
ts1.setName("林青霞");
ts2.setName("林志玲");
ts3.setName("林志穎");
ts1.start();
ts2.start();
ts3.start();
}
}
public class ThreadSleep extends Thread {
@Override
public void run() {
for (int x = 0; x < 100; x++) {
System.out.println(getName() + ":" + x + ",日期:" + new Date());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
加入線程
public final void join():等待該線程終止。
public class ThreadJoinDemo {
public static void main(String[] args) {
ThreadJoin tj1 = new ThreadJoin();
ThreadJoin tj2 = new ThreadJoin();
ThreadJoin tj3 = new ThreadJoin();
tj1.setName("李淵");
tj2.setName("李世民");
tj3.setName("李元霸");
tj1.start();
try {
tj1.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
tj2.start();
tj3.start();
}
}
public class ThreadJoin extends Thread {
@Override
public void run() {
for (int x = 0; x < 100; x++) {
System.out.println(getName() + ":" + x);
}
}
}
禮讓線程
public static void yield():暫停當前正在執行的線程對象,並執行其他線程。
讓多個線程的執行更和諧,但是不能靠它保證一人一次。
public class ThreadYieldDemo {
public static void main(String[] args) {
ThreadYield ty1 = new ThreadYield();
ThreadYield ty2 = new ThreadYield();
ty1.setName("林青霞");
ty2.setName("劉意");
ty1.start();
ty2.start();
}
}
public class ThreadYield extends Thread {
@Override
public void run() {
for (int x = 0; x < 100; x++) {
System.out.println(getName() + ":" + x);
Thread.yield();
}
}
}
守護線程:
public final void setDaemon(boolean on):將該線程標記爲守護線程或用戶線程。
當正在運行的線程都是守護線程時,Java 虛擬機退出。 該方法必須在啓動線程前調用。
遊戲:坦克大戰。
public class ThreadDaemonDemo {
public static void main(String[] args) {
ThreadDaemon td1 = new ThreadDaemon();
ThreadDaemon td2 = new ThreadDaemon();
td1.setName("關羽");
td2.setName("張飛");
// 設置收穫線程
td1.setDaemon(true);
td2.setDaemon(true);
td1.start();
td2.start();
Thread.currentThread().setName("劉備");
for (int x = 0; x < 5; x++) {
System.out.println(Thread.currentThread().getName() + ":" + x);
}
}
}
public class ThreadDaemon extends Thread {
@Override
public void run() {
for (int x = 0; x < 100; x++) {
System.out.println(getName() + ":" + x);
}
}
}
線程中斷:
public final void stop():讓線程停止,過時了,但是還可以使用。
public void interrupt():中斷線程。 把線程的狀態終止,並拋出一個InterruptedException。
public class ThreadStopDemo {
public static void main(String[] args) {
ThreadStop ts = new ThreadStop();
ts.start();
// 你超過三秒不醒過來,我就乾死你
try {
Thread.sleep(3000);
// ts.stop();
ts.interrupt();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class ThreadStop extends Thread {
@Override
public void run() {
System.out.println("開始執行:" + new Date());
// 我要休息10秒鐘,親,不要打擾我哦
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
// e.printStackTrace();
System.out.println("線程被終止了");
}
System.out.println("結束執行:" + new Date());
}
}
2,線程實現方式二
a,步驟
- 創建自定義類MyRunnable,實現Runnable接口;
- 重寫run()方法
- 創建MyRunnable對象
- 創建Thread類對象,將MyRunnable對象作爲形參進行傳遞
- 啓動線程
代碼:
public class RunnableDemo {
public static void main(String[] args) {
// TODO Auto-generated method stub
//線程實現的第二種方法
//創建線程對象
MyRunnable mr1 = new MyRunnable();
//創建Thread對象,將mr1作爲形參作爲傳遞
/*
Thread t1 = new Thread (mr1);
Thread t2 = new Thread (mr1);
t1.setName("線程一");
t1.setName("線程二");
*/
Thread t1 = new Thread (mr1,"線程一");
Thread t2 = new Thread (mr1,"線程二");
t1.start();
t2.start();
}
}
class MyRunnable implements Runnable{
public void run(){
System.out.println(Thread.currentThread().getName()+":"+"yangjinbiao");
}
}
三:線程安全
1,線程的生命週期
2,產生線程不安全的原因?
:是否是多線程環境 ;是否有共享數據 ;是否有多條語句操作共享數據 。
3,解決線程安全:加鎖(synchronized )
a,同步代碼塊
格式synchronized(對象){需要同步的代碼};
注意:可以是任意對象,但是必須使用的是同一個對象。
b,同步方法
鎖對象爲;this
c,靜態同步方法
鎖對象:類的字節碼文件對象。
d:等待喚醒機制
Object類中提供了三個方法:
wait():等待
notify():喚醒單個線程
notifyAll():喚醒所有線程
4,生產消費者模式(等待喚醒機制)
共享的資源:
共同資源
public class Student {
private String name;
private int age;
private boolean flag; // 默認情況是沒有數據,如果是true,說明有數據
public synchronized void set(String name, int age) {
// 如果有數據,就等待
if (this.flag) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 設置數據
this.name = name;
this.age = age;
// 修改標記
this.flag = true;
this.notify();
}
public synchronized void get() {
// 如果沒有數據,就等待
if (!this.flag) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 獲取數據
System.out.println(this.name + "---" + this.age);
// 修改標記
this.flag = false;
this.notify();
}
}
生產者:
public class SetThread implements Runnable {
private Student s;
private int x = 0;
public SetThread(Student s) {
this.s = s;
}
@Override
public void run() {
while (true) {
if (x % 2 == 0) {
s.set("林青霞", 27);
} else {
s.set("劉意", 30);
}
x++;
}
}
}
消費者:
public class GetThread implements Runnable {
private Student s;
public GetThread(Student s) {
this.s = s;
}
@Override
public void run() {
while (true) {
s.get();
}
}
測試:
public class StudentDemo {
public static void main(String[] args) {
//創建資源
Student s = new Student();
//設置和獲取的類
SetThread st = new SetThread(s);
GetThread gt = new GetThread(s);
//線程類
Thread t1 = new Thread(st);
Thread t2 = new Thread(gt);
//啓動線程
t1.start();
t2.start();
}
}
四:線程池(第三種創建線程實現方式)
1,線程池的好處:
線程池的好處:線程池裏的每一個線程代碼結束後,並不會死亡,而是再次回到線程池中成爲空閒狀態,等待下一個對 象來使用。
2,創建線程池步驟:
A:創建一個線程池對象,控制要創建幾個線程對象:public static ExecutorService newFixedThreadPool(int nThreads)
B:這種線程池的線程可以執行:
可以執行Runnable對象或者Callable對象代表的線程
做一個類實現Runnable接口。
C:調用如下方法即可
Future<?> submit(Runnable task)
<T> Future<T> submit(Callable<T> task)
D:我就要結束,可以嗎?
可以。
public class ExecutorsDemo {
public static void main(String[] args) {
// 創建一個線程池對象,控制要創建幾個線程對象。
// public static ExecutorService newFixedThreadPool(int nThreads)
ExecutorService pool = Executors.newFixedThreadPool(2);
// 可以執行Runnable對象或者Callable對象代表的線程
pool.submit(new MyRunnable());
pool.submit(new MyRunnable());
//結束線程池
pool.shutdown();
}
}
public class MyRunnable implements Runnable {
@Override
public void run() {
for (int x = 0; x < 100; x++) {
System.out.println(Thread.currentThread().getName() + ":" + x);
}
}
}
3,線程的第三種實現方案-實現Callable<Integer> 接口:(使用線程池實現)
public class CallableDemo {
public static void main(String[] args) throws InterruptedException, ExecutionException {
// 創建線程池對象
ExecutorService pool = Executors.newFixedThreadPool(2);
// 可以執行Runnable對象或者Callable對象代表的線程
Future<Integer> f1 = pool.submit(new MyCallable(100));
Future<Integer> f2 = pool.submit(new MyCallable(200));
// V get()
Integer i1 = f1.get();
Integer i2 = f2.get();
System.out.println(i1);
System.out.println(i2);
// 結束
pool.shutdown();
}
}
線程類:
public class MyCallable implements Callable<Integer> {
private int number;
public MyCallable(int number) {
this.number = number;
}
@Override
public Integer call() throws Exception {
int sum = 0;
for (int x = 1; x <= number; x++) {
sum += x;
}
return sum;
}
4,創建線程池的方式;
①. newFixedThreadPool(int nThreads)
創建一個固定長度的線程池,每當提交一個任務就創建一個線程,直到達到線程池的最大數量,這時線程規模將不再變化,當線程發生未預期的錯誤而結束時,線程池會補充一個新的線程。
②. newCachedThreadPool()
創建一個可緩存的線程池,如果線程池的規模超過了處理需求,將自動回收空閒線程,而當需求增加時,則可以自動添加新線程,線程池的規模不存在任何限制。
③. newSingleThreadExecutor()
這是一個單線程的Executor,它創建單個工作線程來執行任務,如果這個線程異常結束,會創建一個新的來替代它;它的特點是能確保依照任務在隊列中的順序來串行執行。
④. newScheduledThreadPool(int corePoolSize)
創建了一個固定長度的線程池,而且以延遲或定時的方式來執行任務,類似於Timer。
五:線程組
public class ThreadGroupDemo {
public static void main(String[] args) {
// method1();
// 我們如何修改線程所在的組呢?
// 創建一個線程組
// 創建其他線程的時候,把其他線程的組指定爲我們自己新建線程組
method2();
// t1.start();
// t2.start();
}
private static void method2() {
// ThreadGroup(String name)
ThreadGroup tg = new ThreadGroup("這是一個新的組");
MyRunnable my = new MyRunnable();
// Thread(ThreadGroup group, Runnable target, String name)
Thread t1 = new Thread(tg, my, "林青霞");
Thread t2 = new Thread(tg, my, "劉意");
System.out.println(t1.getThreadGroup().getName());
System.out.println(t2.getThreadGroup().getName());
//通過組名稱設置後臺線程,表示該組的線程都是後臺線程
tg.setDaemon(true);
}
private static void method1() {
MyRunnable my = new MyRunnable();
Thread t1 = new Thread(my, "林青霞");
Thread t2 = new Thread(my, "劉意");
// 我不知道他們屬於那個線程組,我想知道,怎麼辦
// 線程類裏面的方法:public final ThreadGroup getThreadGroup()
ThreadGroup tg1 = t1.getThreadGroup();
ThreadGroup tg2 = t2.getThreadGroup();
// 線程組裏面的方法:public final String getName()
String name1 = tg1.getName();
String name2 = tg2.getName();
System.out.println(name1);
System.out.println(name2);
// 通過結果我們知道了:線程默認情況下屬於main線程組
// 通過下面的測試,你應該能夠看到,默任情況下,所有的線程都屬於同一個組
System.out.println(Thread.currentThread().getThreadGroup().getName());
}
}