多线程概念
进程:程序使用CUP资源运行的过程。
线程:在一个进程中可以有多个线程,形成多条执行线索。
线程之间可以共享某些内存,完成其任务。
多线程:在一个应用中同时存在多个执行体,他们各自执行不同的任务。
主线程:即mian方法下的线程,但是一个程序的结束是最后一个子线程的结束而结束,并不一定的main线程结束时。
多线程优势:
提高计算机系统的CPU利用率;
提高程序的响应速度;
改善程序机构。
线程的状态和生命周期
一个线程的生命周期科分为一下几个状态:
1. 新建状态(New):
调用线程的构造方法创建一个线程:
Thread thread1 = new Thread();
处于新建状态的线程没有分配到有关系统资源。
2. 可运行状态(Runnable):
处于新建状态的线程调用start()方法,使得该线程进入可运行状态。
但该线程不一定开始运行,还需要受到JVM的调度。
3. 运行状态(Running):
占用CPU资源,在实际运行中。
该状态下可能发生:
1). 线程正常结束,进入死亡状态;
2). 当前程序执行yield()方法,或当前线程由于调度策略,资源被占用,进入可运行状态;
3). 使用sleep()方法,join()方法,wait()方法,使用synchronized来获取对象锁,都可进入阻塞状态。
4. 阻塞状态:
1). sleep(), wait()方法进入其他阻塞状态;
2). wait()方法进入等待阻塞状态,需要notify() or notifyall() 方法来唤醒,进入可运行状态;
3). 使用synchronized请求对象但未获得对象锁时,进入对象锁阻塞状态。
5. 死亡状态:
自然死亡,强制退出程序死亡,异常未捕获死亡。
线程优先级和调度
多个线程都要执行时,优先级高的线程先执行。
线程优先级为1~10,默认为5。
线程的优先级通过setPriority()方法来进行设置。
public final void setPriority(int newPriority);
//Java定义的:
MAX_PRIORITY //10
NORM_PRIORITY// 5
MIN_PRIORITY // 1
实现多线程的方法
线程的创建需要调用Thread类的构造方法来实现:
public Thread();
public Thread(String name);
public Thread(Runnable target);
public Thread(runnable target, String name);
//参数说明
name:线程名
target: 实现了Runnable接口的对象,并重写其的run()方法。
//Runnable接口定义
public interface Runnable{
public void run();
}
有两种方法实现线程体:
- 继承Thread类;
//继承Thread类,并重写run()方法。
public class Hello extends Tnread{
int i;
public void run(){
while(true){
System.out.println("Hello, Peter!");
if(i==123)
break;
i++;
}
}
}
//测试类,直接用继承Thread类的Hello类创建对象并用t1指向他,t1在调用start(),方法启动该线程。
public class testHello{
public static void main(String[] args){
Hello t1 = new Hello();
t1.start();
}
}
- 实现Runnable接口。
//实现Runnable接口,并重写run()方法。
public class Hello implements Runnable{
int i;
public void run(){
while(true){
System.out.println("Hi, Bob!");
i++;
if(i==321)
break;
}
}
}
//测试类,创建Thread对象,并使用实现Runnable接口的类的对象作为Thread类的参数创建对象,
public class testHello{
public static void main(String[] args){
Thread t1 = new Thread(new Hello());
t1.start();
}
}
两种方法比较:
1. 继承Thread类:
优点:实现简单,在run()方法中若调用当前线程的其他方法,只需要this即可,无需使用Thread.currentThread()。
缺点:由于已经继承了Thread类,所以不能继承其他类。
2. 实现Runnable接口:
优点: 可以继承其他类,可以使用多个线程共享一个target对象。
**缺点:**实现相对复杂。
线程的常用方法
1. sleep()
try{
.....
Thread.sleep(long millis);//休眠单位为毫秒
}catch(InterruptedException e){
e.printStackTrace();
}
2. isAlive()
判断指定线程是否在执行中。
3. currentThread()
Thread.currentTHread(); // 获取当前的线程。
Thread.currentThread(); //获取当前线程名称。
4. yield()
暂停当前线程,让步给其他更高优先级或相同优先级的线程。
当其他线程执行完之后,在执行该线程。
5. interrupt()
interrupt()用来唤醒处于休眠的线程。当程序调用sleep()进入休眠后,interrupt()方法是其抛出异常,重新进入排队状态。
线程的同步
线程同步:
程序中多个线程需要调用同一个方法,这个方法被synchronized修饰,因此多个线程调用该方法必须遵循synchronized规则。
例子:会计收钱,出纳花钱,二者不可同时执行账本,否则发生错乱。
public class ThreadSynchronized{
public static void main(String[] args){
Bank bank = new bank();
bank.setMoney(200);
System.out.println("目前账上有" + bank.money + "亿");
Thread cashier, accountant;
cashier = new Thread(bank);
accountant = new Thread(bank);
cashier.setName("出纳");
accountant.setName("会计");
cashier.start();
accountant.start();
}
}
Class Bank implements Runnable{
int money;
public void setMoney(int m);
money = m;
public void run(){
if(Thread.currentThread().getName().equals("出纳"))
SaveorTack(150);
if(Thread.currentThread().getName().equals("会计"))
SaveorTack(300);
}
public SaveorTack(int amount){
if(Thread.currentThread().getName().equals("会计")){
for(int i=0; i<3; i++){
money += amount/3;
System.out.println(Thread,currentThread().getName() + "存入" + amount/3 + "账上有" + money + "休息一会儿!");
try{
Thread.sleep(100);//这时出纳不可以操作
}catch(InteruptedException e){
e.printStackTrace();
}
}
}
else if(Thread.currentThread().getName().equals("会计")){
for(int j=0; j<3; j++){
money -= amount/3;
System.out. println(Thread.currentThread().getName() + "支出" + amount + "账上有" + money + "休息一会儿!");
try{
Thread.sleep(100);//这时会计不可以操作
}catch(InterruptedException e){}
}
}
}
}
此外,还有 wait(), notify(), notifyall() 方法用来调整线程的同步问题。
如:有时线程A的某变量需要经过线程B处理子之后才符合要求,这时。就需要先让线程A在相关位置执行wait(),等线程B执行完之后再用notifyall()通知线程A即可。
线程的联合
有时线程需要接力来完成某项任务,这时候就需要线程间联合起来。
若线程A联合线程B,那么线程A立即中断,等到线程B执行完成之后,A再继续执行。
例:
线程1:顾客买蛋糕;线程2:蛋糕师制作蛋糕;
顾客到店之后需要到等待蛋糕师做完之后,才可以取走蛋糕。
public class ThreadJoinDD{
public static void main(String[] args){
ThreadJoin a = new ThreadJoin();
Thread customer = new Thread(a);
Thread cakemaker = new Thread(a);
customer.setName("顾客");
cakemaker.setName("蛋糕师");
a.setJoinThread(cakemaker);
customer.start();
}
}
//使用Runnable接口
public class ThreadJoin implements Runnable{
Cake cake;
Thread joinThread;
public void setJoinThread(Thread e){
joinThread = e;
}
public void run(){
if(Thread.currentThread().getName().equals("顾客")){
System.out.println(Thread.currentThread().getName() + "等待" + joinThread.getName() + "制作蛋糕!");
try{
joinThread.start();
joinThread.join();
}catch(InterruptedException e){}
System.out.println(Thread.currentThread().getName() + "买了" + cake.name + ", 价格" + cake.price);
}
else if(Thread.currentThread()==joinThread){
System.out.println(joinThread.getName() + "开始制作蛋糕,请等待。。。。");
try{
Thread.sleep(2000);
}catch(InterruptedException e){}
cake = new Cake("生日蛋糕", 288);
System.out.println(joinThread.getName() + "已经完成蛋糕店制作。");
}
}
//内部类,Cake
class Cake{
int price;
String name;
public Cake(int price, String name){
this.price = price;
this.name = name;
}
}
}