一基本概念:
线程:处理器调度的基本单位,一个程序内部的顺序控制流,本身无法运行,必须在程序中运行。
线程与进程的比较:(1)两者是两个不同层次上的概念。进程是由操作系统来管理的,而线程则是在一个程序(进程)内。(2)不同进程的代码,内部数据和状态都是完全独立的,而一个程序内的多线程是共享同一块内存空间和同一组系统资源,有可能互相影响。(3)线程本身的数据只有寄存器数据,以及一个程序执行时使用的堆栈,所以线程的切换比进程切换的负担要小。
二线程的创建:
Java提供了类java.lang.Thread支持多线程。
创建线程的两种方式:
a.继承Thread;
通过继承java.lang.Thread类,并覆盖Thread类的run方法来完成线程的创建。创建一个Thread类的子类,重写run方法。
public class MyThread extends Thread
{
private static int count=0;
public MyThread(String name)
{
super(name);
}
public static void main(String[] args)
{
MyThread p=new MyThread("t1");
p.start();
for(int i=0;i<5;i++)
{
count++;
System.out.println(count+":main");
}
}
public void run()
{
for(int i=0;i<5;i++)
{
count++;
System.out.println(count+":"+this.getName());
}
}
}
通过调用start方法启动线程,而不能直接调用run方法。在调用start()方法时,start方法会首先进行与多线程相关的初始化,然后再调用run方法。
b.实现接口Runnable
public class MyThread implements Runnable
{
int count=1,number;
public MyThread(int i)
{
number=i;
System.out.println("创建线程"+number);
}
public void run()
{
while(true)
{
System.out.println("线程:"+number+":计数"+count);
if(++count==6)return;
{
}
}
}
public static void main(String[] args)
{
for(int i=0;i<5;i++)
new Thread(new MyThread(i+1)).start();
}
}
三线程的生命周期
1各种状态解释:
创建状态:利用new关键字创建线程对象实例后,它仅仅作为一个对象实例存在,Jvm没有为其分配CPU时间片等线程运行资源。
就绪状态:创建状态的线程调用start方法将线程状态转化为就绪状态,线程已得到除CPU时间之外的其他系统资源,一旦获得CPU即进入运行状态。运行的线程可调用yield()自动放弃CPU,从而回到就绪状态。
阻塞状态:暂停一个线程的执行以等待某个条件发生,此时,调度机制直接跳过此线程,不分配任何CPU时间。
死亡状态:当线程体(run方法)运行结束或调用线程对象的stop方法后线程将终止运行,由JVM收回线程占用的资源。
2状态转换的一些支持阻塞的方法
sleep方法:允许指定以毫秒为单位的一段时间作为参数,使得线程在指定时间内进入阻塞状态,不能得到CPU时间,不释放占用资源前提下停止运行。指定时间一过,线程重新进入可执行状态。
suspend方法和resume方法:使得线程进入阻塞状态,释放占用的所有资源,由JVM调度转入临时存储空间,并且不会自动恢复,必须其对应的resume被调用,才能使得线程重新进入可执行状态。
wait和notify方法:wait使得线程进入阻塞状态,它有两种形式:一种是允许指定以毫秒为单位的一段时间作为参数,当对应的notify被调用或超出指定时间线程重新进入就绪;另一种没有参数,必须对应的notify被调用。
3线程调度和优先级
调度:当系统中只有一个CPU时,以某种顺序在某CPU情况下执行多线程。
Java采用固定优先级调度(抢先式调度)。这种算法是根据处于可运行态线程的相对优先级来实行调度。当线程产生时,它继承原线程的优先级,在需要时可对优先级进行修改。任何时刻,优先选择优先级最高的可运行线程运行,只有当它停止,自动放弃或由于某种原因成为非运行态,低优先级的线程才能运行。如果两个线程具有相同优先级,则交替
进行。任何时刻,如果一个比其他线程优先级都高的线程的状态变为可执行态,实时系统都将选择该线程来运行。
Java将线程的优先级分为10个等级,分别用1-10;MIN_PRIORITY:1; MAX_PRIORITY:10; NORMAL:5;
当一个线程对象被创建时,其默认线程优先级是5; setPriority:改变线程运行优先级 getPriority:获取当前线程优先级
守护线程:具有最低优先级,用于为系统中的其他对象和线程提供服务。setDaemon:设置为守护线程。JVM中的系统资源自动回收线程,始终在低级别的状态中运行,用于实时监控和管理系统中的可回收资源。
public class TestThreadPriority extends Thread
{
public TestThreadPriority(String name)
{
super(name);
}
public static void main(String[] args)
{
TestThreadPriority t1=new TestThreadPriority("Thread1");
t1.setPriority(Thread.MIN_PRIORITY);
t1.start();
TestThreadPriority t2=new TestThreadPriority("Thread2");
t2.setPriority(Thread.NORM_PRIORITY);
t2.start();
TestThreadPriority t3=new TestThreadPriority("Thread3");
t3.setPriority(Thread.MAX_PRIORITY);
t3.start();
}
public void run()
{
for(int i=0;i<3;i++)
System.out.println(this.getName()+"is running.");
}
}
四线程互斥
临界资源:对多线程共享的数据或资源
临界代码段:每个线(进)程中访问临界资源的那一段代码
互斥对象:设置“互斥锁”标记的对象,保证在每一时刻,只能有一个线程拥有该互斥锁,其他线程如果申请该互斥锁,必须等待当前线程释放。
synchronized(互斥对象)
{
临界代码段
}
五线程同步
生产-消费者模型
约束条件:
(1)生产者负责产品,并将其保存到仓库中。
(2)消费者从仓库中取得产品
(3)由于库房容量有限,因此只有当库房还有空间时,生产者才可以将产品加入库房;否则只能等待。
(4)只有库房存在满足数量的产品时,消费者才能取走产品,否则只能等待。
实例:
public class account
{
double balance;
public account()
{
balance=0;
System.out.println("Totle Money:"+balance);
}
public synchronized void withdraw(double money)
{
if(balance==0)
try
{
wait();
}
catch(InterruptedException e)
{
}
balance=balance-money;
System.out.println("withdraw 100 success.");
notify();
}
public synchronized void deposite(double money)
{
if(balance)
try
{
wait();
}
catch(InterruptedException e)
{
}
balance=balance+money;
System.out.println("deposite 100 success");
notify();
}
}
public class WithdrawThread extends Thread
{
account Account;
public WithdrawThread(account Account)
{
this.Account=Account;
}
public void run()
{
for(int i=0;i<5;i++)
Account.withdraw(100);
}
}
public class DepositThread extends Thread
{
account Account;
public DepositThread(account Account)
{
this.Account=Account;
}
public void run()
{
for(int i=0;i<5;i++)
Account.deposite(100);
}
}
public class TestProCon
{
public static void main(String[] args)
{
account Account =new account();
WithdrawThread withdraw=new WithdrawThread(Account);
DepositThread deposite=new DepositThread(Account);
withdraw.start();
deposite.start();
}
}
关于wait和notify方法:
必须位于临界代码段中。(sleep方法并不操作互斥锁);wait方法是当一个线程执行了该方法后,则该线程进入阻塞状态同时让出同步对象的互斥锁,并自动进入互斥对象的等待队列。notify则是当一个线程执行了该方法后,则拥有该方法的互斥对象的等待队列中的第一个线程被唤醒,同时拥有该互斥对象的互斥锁,并进入就绪状态等待调度。
必须配对使用。
notifyall()唤醒互斥对象等待队列中所有处于阻塞状态的线程,使其进入就绪状态。
六进程间通信
管道通信特点:
管道是单向的,一个线程充当发送者,必须有另一个线程充当接收者。
管道通信是面向连接的。因此在程序设计中,一方线程必须建立起对应的端点,由另一方线程来建立连接。
严格按照发送顺序传送。接收方收到的数据和发送方在顺序上一致。
Java语言把管道看作是一种特殊的I/O流,提供了PipedOutputStream,PipedInputStream用于建立基于字节的管道通信;以及PipedWritet,Pipedreader用于建立基于字符的管道通信,都位于java.io类中。
实例:
import java.io.*;
public class SenderThread extends Thread{
PipedWriter pipedWriter;
public SenderThread()
{
pipedWriter=new PipedWriter();
}
public PipedWriter getPipedWriter()
{
return pipedWriter;
}
public void run()
{
for(int i=0;i<5;i++)
{
try
{
pipedWriter.write(i);
}
catch(IOException e)
{
}
System.out.println("Send:"+i);
}
}
}
import java.io.*;
public class ReceiverThread extends Thread{
PipedReader pipedReader;
public ReceiverThread(SenderThread sendThread) throws IOException
{
pipedReader=new PipedReader(sendThread.getPipedWriter());
}
public void run()
{
int i=0;
while(true)
{
try
{
i=pipedReader.read();
System.out.println("Received:"+i);
}
catch(IOException e)
{
}
if(i==4)
break;
}
}
}
import java.io.*;
public class ThreadComm {
public static void main(String[] args) throws Exception
{
SenderThread send =new SenderThread();
ReceiverThread receive=new ReceiverThread(send);
send.start();
receive.start();
}
}
七线程死锁
线程死锁必备四个条件:
(1)互斥条件:即至少存在一个资源,不能被多个线程同时共享;
(2)至少存在一个线程,它拥有一个资源,并等待获得另一个线程当前拥有的资源。
(3)线程拥有的资源不能被同时剥夺,只能由线程资源释放。
(4)线程对资源的请求形成一个圆环。
哲学家用餐问题:
5个哲学家围坐在一张圆桌旁,每人的两边放着一支筷子,共5双筷子。规定如下:
每个人只有拿起位于自己两边的筷子,合成一双才能吃饭。
用餐后每个人必须将两支筷子放回原地。
public class ChopStick {
private String name;
public ChopStick(String name)
{
this.name=name;
}
public String getNumber()
{
return name;
}
}
import java.util.*;
public class Philosopher extends Thread{
private ChopStick left;
private ChopStick right;
private String name;
private static Random random=new Random();
public Philosopher(String name,ChopStick left,ChopStick right)
{
this.name=name;
this.left=left;
this.right=right;
}
public String getNumber()
{
return name;
}
public void run()
{
try
{
sleep(random.nextInt(10));
}
catch(InterruptedException e)
{
}
synchronized(left)
{
System.out.println(this.getNumber()+"has "+left.getNumber()+" and wait for "+right.getNumber());
synchronized(right)
{
System.out.println(this.getNumber()+" is eating");
}
}
}
public static void main(String[] args)
{
ChopStick cs1=new ChopStick("ChopStick1");
ChopStick cs2=new ChopStick("ChopStick2");
ChopStick cs3=new ChopStick("ChopStick3");
Philosopher ps1=new Philosopher("philosopher1",cs1,cs2);
Philosopher ps2=new Philosopher("philosopher2",cs2,cs3);
Philosopher ps3=new Philosopher("philosopher3",cs3,cs1);
ps1.start();
ps2.start();
ps3.start();
}
}
八线程池
基本思想:在系统中开辟一块区域,其中一块存放一些待命的线程,这个区域称为线程池,如果需要执行任务,则从线程池中取一个待命的线程来执行指定的任务,任务结束将线程放回。避免大量重复创建线程对象,浪费cpu,内存等资源问题。
1固定尺寸线程池
import java.util.concurrent.*;
public class FixTest implements Runnable{
private String name;
public FixTest(String name)
{
this.name=name;
}
public void run()
{
System.out.println("\n----"+name+"开始执行");
for(int i=0;i<50;i++)
{
System.out.println("["+name+"]");
}
System.out.println("\n----"+name+"执行结束");
}
public static void main(String[] args)
{
//通过使用Executors类的静态方法创建固定尺寸为2的线程池
ExecutorService threadPool =Executors.newFixedThreadPool(2);
FixTest ft1=new FixTest("FT1");
FixTest ft2=new FixTest("FT2");
FixTest ft3=new FixTest("FT3");
//启动任务进行
threadPool.execute(ft1);
threadPool.execute(ft2);
threadPool.execute(ft3);
}
}
//所有任务执行结束后不会自动退出,线程池中的线程执行结束后并不死亡,而是等待新的任务。如果希望程序执行完所有的任务后退出,可以调用ExecutorService接口中的shutdown方法关闭线程池。
2可变尺寸线程池
import java.util.concurrent.*;
public class ShrinkTest implements Runnable{
private String name;
public ShrinkTest(String name)
{
this.name=name;
}
public void run()
{
System.out.println("\n----"+name+"开始执行");
for(int i=0;i<50;i++)
{
System.out.println("["+name+"]");
}
System.out.println("\n----"+name+"执行结束");
}
public static void main(String[] args)
{
//通过使用Executors类的静态方法创建可变尺寸的线程池
ExecutorService threadPool =Executors.newCachedThreadPool();
ShrinkTest ft1=new ShrinkTest("FT1");
ShrinkTest ft2=new ShrinkTest("FT2");
ShrinkTest ft3=new ShrinkTest("FT3");
//启动任务进行
threadPool.execute(ft1);
threadPool.execute(ft2);
threadPool.execute(ft3);
//所有任务执行结束后关闭线程池
threadPool.shutdown();
}
}
//执行任务时,先选取重用缓存中的空闲线程完成任务,如果没有空闲线程,则创建新线程,空闲超过60s,线程将从线程池中删除。

被折叠的 条评论
为什么被折叠?



