線程

一.多線程

1.基本概念

進程:正在運行中的程序,一個進程中至少包含一個線程

線程:進程的任務,執行任務的一個通道,一個進程中可以包含多個線程

2.多線程執行的特點:

兩種方式:分時調度/搶佔式調度(java屬於搶佔)

二.Thread 類(java.lang)

1.概述:使用該類表示多線程對象,只要創建一個Thread對象,就是一個線程對象產生了

2.定義:public class Thread extends Object implements Runnable

3.構造方法:

Thread():分配新的 Thread 對象

Thread(String name):使用指定的名稱分配新的 Thread 對象

Thread(Runnable target):接收Runnable接口子類對象,實例化Thread對象

Thread(Runnable target, String name):接收Runnable接口子類對象,實例化Thread對象,並設置線程名稱

4.常用方法:

public void run(){}:如果該線程是使用獨立的 Runnable 運行對象構造的,則調用該 Runnable 對象的 run 

方法;否則,該方法不執行任何操作並返回。<所以該方法應自覺重寫!!!>

public void start(){}:使該線程開始執行;Java 虛擬機調用該線程的 run 方法

public static Thread currentThread(){}:返回目前正在執行的線程

public final String getName(){}:返回線程的名稱

public final int getPriority(){}:返回線程優先級

public final void setName(String name){}:設定線程名稱

public final void setPriority(int newPriority){}:設定線程的優先值

public static void sleep(long millis) throws InterruptedException{}:使當前線程休眠多少毫秒

public static void yield(){}:將目前執行的線程暫停,允許其它線程執行

public final ThreadGroup getThreadGroup(){}:Returns the thread group to which this thread belongs. 

This method returns null if this thread has died (been stopped).

代碼演示:

//多線程基本練習-Thread類
class MyThread extends Thread{
//重寫run()
@Override
public void run(){
for(int i = 0; i < 100; i++){
System.out.println(Thread.currentThread()+"Jack"+i);
}
}
}
public class ThreadDemo{
public static void main(String[] args){
//創建一個線程
MyThread my = new MyThread();
//啓動線程
my.start();
//主線程執行如下任務
for(int i = 0; i<100; i++){
System.out.println(Thread.currentThread()+"肉絲"+i);
}
//返回當前運行的線程名稱
String s = my.getName();
System.out.println("當前線程名稱爲:"+s);//Thread-0
//修改線程名稱
my.setName("Smith--");
System.out.println("修改後-當前線程名稱爲:"+my.getName());
//返回線程優先級
int i = my.getPriority();
System.out.println("當前線程優先級爲:"+i);//5
}
}

三.Runnable 接口(java.lang)

1.概述:Runnable接口只有一個方法,run方法,因此實現Runnable接口的類,必須重寫run方法,否則,語法報錯;

2.實現接口的好處:

實現類可以繼承其他類,不佔用繼承的位置;

可以多實現,可以將編寫線程類和寫任務代碼的工作分離開;

3.定義:

@FunctionalInterface

public interface Runnable

4.方法:

public void run() 使用實現接口 Runnable 的對象創建一個線程時,啓動該線程將導致在獨立執行的線程中調用對象的 run 方法

代碼演示:

//通過Runnable接口實現多線程
class MyRunnable implements Runnable{
@Override
public void run(){
for(int i = 0; i<100; i++){
System.out.println("Smith----------"+i+Thread.currentThread());
}
}
}
public class RunnableDemo{
public static void main(String[] args){
MyRunnable my = new MyRunnable();
Thread myThread = new Thread(my,"smith");
myThread.start();
//返回當前線程名稱
System.out.println("當前線程:"+Thread.currentThread());
for(int i = 0;i<100;i++){
System.out.println("格林:==="+i+Thread.currentThread());
}
}
}


四.線程安全問題

原因:當多個線程使用共享的資源時,容易發生數據安全的問題;

解決方案:

Java 提供了3種解決安全問題的方式;

1:同步代碼塊

2:同步方法

3:Lock接口

1.同步代碼塊

/*使用3個線程,模擬3個窗口,賣100張票;
要求每個線程賣出去的票,不能重複且不能是無效票;
使用一個變量表示100張票,每個窗口賣出去一張票,就將該變量的值減一,直到0爲止;
使用同步代碼塊
*/
class MyRunnable implements Runnable{
int ticket = 100;//總票數,只能new一次該類,因爲每創建一次對象就會有100張票
@Override
public void run(){
String name = Thread.currentThread().getName();//獲取當前線程名
while(true){
synchronized(this){
if(ticket<=0){
break;
}else{
System.out.println(name+"賣出第"+ticket+"號票");
ticket--;
}
}
}
}
}
public class SellTicket{
public static void main(String[] args){
//創建任務對象
MyRunnable my = new MyRunnable();
//創建線程
Thread t1 = new Thread(my,"窗口1");
Thread t2 = new Thread(my,"窗口2");
Thread t3 = new Thread(my,"窗口3");
//開啓線程
t1.start();
t2.start();
t3.start();
}
}


2.同步方法

class MyRunnable implements Runnable{
//定義總票數爲成員變量
int ticket = 100;
String name;
@Override
public void run(){
name = Thread.currentThread().getName();
//賣票任務
while(true){
sell();
if(ticket<=0){
break;
}
}
}
//同步方法
public synchronized void sell(){
if(ticket > 0){
System.out.println(name+"賣出第"+ticket+"號票");
ticket--;
}
}
}
public class SellTicket01{
public static void main(String[] args){
//創建任務對象
MyRunnable my = new MyRunnable();
//創建多線程
Thread t1 = new Thread(my,"窗口1");
Thread t2 = new Thread(my,"窗口2");
Thread t3 = new Thread(my,"窗口3");
//開啓多線程
t1.start();
t2.start();
t3.start();
}
}

3.Lock接口

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
class MyRunnable implements Runnable{
//定義票數爲成員變量
int ticket = 100;
//創建鎖對象
private static final Lock lock = new ReentrantLock();
@Override
public void run(){
String name = Thread.currentThread().getName();
while(true){
lock.lock();
try{
if(ticket > 0){
System.out.println(name+"賣出了第"+ticket+"號票");
ticket--;
}else{
break;
}
}finally{
lock.unlock();
}
}
}
}
public class SellTicket02{
public static void main(String[] args){
//創建任務對象
MyRunnable my = new MyRunnable();
//創建多線程
Thread t1 = new Thread(my,"窗口1");
Thread t2 = new Thread(my,"窗口2");
Thread t3 = new Thread(my,"窗口3");
//開啓多線程
t1.start();
t2.start();
t3.start();
}
}


五.使用匿名內部類實現多線程

/*編寫程序,創建兩個線程對象,一根線程循環輸出“播放背景音樂”,另一根線程循環輸出
“顯示畫面”,要求線程實現Runnable接口,且使用匿名內部類實現*/
public class ThreadDemo003{
public static void main(String[] args){
new Thread(new Runnable(){
@Override
public void run(){
for(int i = 0; i < 100; i++){
System.out.println("播放背景音樂");
}
}
}).start();
new Thread(new Runnable(){
@Override
public void run(){
for(int i = 0; i <100; i++){
System.out.println("顯示畫面");
}
}
}).start();
}
}


/*編寫程序,創建兩個線程對象,一根線程循環輸出“播放背景音樂”,另一根線程循環輸出
“顯示畫面”,要求使用Thread類,且使用匿名內部類實現*/
public  class ThreadDemo03{
public static void main(String[] args){
new Thread(){
@Override
public void run(){
for(int i = 0; i<100; i++){
System.out.println("播放背景音樂");
}
}
}.start();
new Thread(){
@Override
public void run(){
for(int i = 0; i<100; i++){
System.out.println("顯示畫面");
}
}
}.start();
}
}


六.ThreadGroup 類(java.lang)

簡介:線程組:java允許對一批線程進行管理,使用ThreadGroup表示線程組,所有線程均有指定線程組,如果沒有顯式指定,則爲默認線程組.默認情況下,子線程和創建他的父線程

屬於同一線程組.一旦某線程加入指定線程組內,該線程會一直屬於該組,直至死亡,運行過程中不可改變.

繼承關係:java.lang.Object--java.lang.ThreadGroup

定義:public class ThreadGroup extends Object implements Thread.UncaughtExceptionHandler

構造器:

ThreadGroup(String name): Constructs a new thread group.

ThreadGroup(ThreadGroup parent, String name): Creates a new thread group.

常用方法:

public int activeCount(){}:Returns an estimate of the number of active threads in this thread group and its subgroups

public int activeGroupCount(){}:Returns an estimate of the number of active groups in this thread group and its subgroups. 

Recursively iterates over all subgroups in this thread group.

public final void checkAccess() Throws SecurityException{}:Determines if the currently running thread has permission to modify this thread group.

public int enumerate(Thread[] list) Throws SecurityException{}:

public int enumerate(Thread[] list,boolean recurse)Throws SecurityException{}:Copies into the specified array every active thread 

in this thread group. If recurse is true, this method recursively enumerates all subgroups of 

this thread group and references to every active thread in these subgroups are also included. 

If the array is too short to hold all the threads, the extra threads are silently ignored.

public final String getName(){}:Returns the name of this thread group.

public final ThreadGroup getParent()Throws SecurityException{}:Returns the parent of this thread group.

public final boolean isDaemon(){}:Tests if this thread group is a daemon thread group

public final void setDaemon(boolean daemon)Throws SecurityException{}:Changes the daemon status of this thread group.

代碼演示:獲取當前系統內運行的所有線程組及線程名

import java.util.List;
import java.util.ArrayList;
public class ThreadListDemo{
public static void main(String[] args){
for(String s : getThreadGroups(getRootThreadGroups())){
System.out.println(s);
}
}
//getRootThreadGroups()
public static ThreadGroup getRootThreadGroups(){
//get current threadgroup
ThreadGroup rootGroup = Thread.currentThread().getThreadGroup();
while(true){
if(rootGroup.getParent() != null){
rootGroup = rootGroup.getParent();
}else{
break;
}
}
return rootGroup;
}
//getThreadGroups()傳入一個線程組,獲取該組內所有子線程組
public static  List<String> getThreadGroups(ThreadGroup group){
List<String> threadList = getThreads(group);//存子線程組名,調用getThreads方法,返回線程組內所有線程名
ThreadGroup[] groups = new ThreadGroup[group.activeGroupCount()];//活動的線程組名
int count = group.enumerate(groups,false);//複製子線程組到線程組數據,不遞歸複製
for(int i = 0; i< count; i++){
threadList.addAll(getThreads(groups[i]));
}
return threadList;
}
//傳入一個線程組,返回該組內所有線程名
public static List<String> getThreads(ThreadGroup group){
List<String> threadList = new ArrayList<>();//存線程名
Thread[] list = new Thread[group.activeCount()];//活動線程
int count = group.enumerate(list,false);//複製當前進程到list中
for(int i = 0; i< count; i++){
threadList.add("名爲:"+group.getName()+"的線程組,線程名爲:"+list[i].getName());
}
return threadList;
}
}


七.Executor 接口(java.io.concurrent)

簡介:線程池:由於線程涉及到與操作系統交互,所以啓動一個新線程的成本比較高,因此,java提供了線程池機制來提高性能,尤其是當程序中需要大量生存期很短暫的線程時,應該考慮

使用線程池.所謂線程池是指在系統啓動時即創建大量空閒的線程,程序將一個Runnable對象或Callable對象傳給線程池,線程池就會啓動一個線程來執行他們的run或call方法,當方法

結束時,線程並不會死亡,而是返回線程池成爲空閒狀態,等待執行下一個任務

定義: public interface Executor

方法:public void execute(Runnable command) Throws RejectedExecutionException | NullPointerException {}:Executes the given command at some time in the future

實現類:AbstractExecutorService, ForkJoinPool, ScheduledThreadPoolExecutor, ThreadPoolExecutor

子接口:ExecutorService, ScheduledExecutorService


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章