多线程的应用
一、进程和线程的概念
每个程序运行至少需要一个进程,都有一个执行的路径,在这个进程中又至少有一个线程。我们在电脑上同时开启许多个程序一样,
如果是单核cpu处理器的情况,那么同一时刻cpu只会调度一个线程,而我们视觉上感觉都在并发执行,那是因为cpu在完成快速的切换。
在main函数中,进行着一个主线程,也就是一个程序至少有一个线程。
二、线程的生命周期:
1、创建线程对象
2、调用start方法,进入就绪状态,等待cpu分配时间片
3、运行状态:分配到时间片进入运行状态,这个时候线程既有运行资格,又有执行权。
4、阻塞状态:线程进入阻塞
调用sleep,线程放弃cpu执行权,等待时间到,进入就绪态
调用yield,线程放弃当前cpu执行权,重新分配时间片
调用wait,线程放弃cpu执行权,等待唤醒
调用join,线程必须等待join的线程执行完,才能继续执行
5、 结束状态,线程正常执行完任务结束
三、创建线程的两种方法:
1、通过继承Thread来创建线程:
(1)、创建一个子类继承extends Thread类
(2)、在子类中重写run()方法。
(3)、创建一个子类的对象,就是创建了一个线程
(4)、调用start()方法,启动线程。
2、通过实现Runnable接口来创建线程。
(1)、创建一个子类来实现Runnable接口
(2)、重写Runnable接口中的Run()方法
(3)、在主函数中使用子类创建对象
(4)、Thread 创建线程,然后把子类对象作为参数传进去。
四、调用run方法和start方法的区别
如果直接调用run()方法,那么和普通的函数调用没有任何区别,就是先去执行调用函数,然后再返回到主函数中来顺序执行,
其实线程并没有被启动。而调用start()方法,进行了两个步骤,一是启动线程,二是调用run()方法,但是调用之后,线程并没有
进入运行状态,而是出于就绪状态,它有运行资格,但是没有执行权,处于等待cpu分配资源的阶段。
五、为什么要覆盖Thread或者接口中的run()方法
因为run()方法能被线程所调用,所以run()方法中通常是我们要运行的代码,所以,我们在定义一个子类时,要把我们要线程来
执行的程序代码放进去,所以要重写run()方法;
六、同步代码块和同步函数
1、同步代码块的应用,我们知道在多个线程同时操作一段代码时,如果执行到某一句代码时,cpu有可能切换到其他的线程,这样保留
的数据就会改变,不能确保安全性,为了提高代码的安全性,我们要给特定的代码上锁以确定在某一个线程能够执行时,其他的线程进不去。
格式如下:synchronized(对象){
执行代码;
}
同步函数:public synchronized void show(){
执行代码;
}
给函数或者代码块上锁确实能够提高安全性,但是也耗费了资源,因为线程每次执行都要进行判断,而且如果某线程在执行中睡眠或者
异常,哪怕其他的线程抢到cpu 的执行权,也进不去。
2、同步函数中用的锁是this ,静态同步方法使用锁是该方法所在类的字节码文件对象(类名、class)。
七、实现方法和继承方法的区别
实现方法可以避免单继承的局限性,比如Student类它继承了Person这个类,而它里面需要多线程来完成的代码时,那么它是不能够再去
继承Thread类,所以接口Runnable解决了这个问题。
1、获取线程名称的方法:Thread.currentThread(),获取当前线程的名称
2、设置线程名称有两种方式:一是:通过构造函数初始化,然后在super()调用父类的方法;
二是:通过父类Thread中的方法setName()来完成。
下面通过代码来体现上面的一些概念:
案例一:
1 public class ThreadDemo { 2 public static void main(String[] args){ 3 Athread a1=new Athread("线程一"); 4 Athread a2=new Athread("线程二"); 5 //a1.setName("线程一"); 6 //a2.setName("线程二"); 7 a1.start(); 8 a2.start(); 9 for(int x=0;x<60;x++) 10 { 11 System.out.println("Hello,World!"); 12 } 13 } 14 } 15 16 //创建一个类继承Thread线程类 17 class Athread extends Thread { 18 Athread(String name) 19 { 20 super(name); 21 } 22 public void run()//重写Thread中run()方法 23 { 24 for(int i=0;i<60;i++) 25 { 26 System.out.println(Thread.currentThread()+"xiancheng"+i); 27 } 28 } 29 }
案例二:卖票程序
1 public class TicketDemo { 2 public static void main(String[] args) { 3 Ticket t = new Ticket(); 4 Thread t1 = new Thread(t); 5 Thread t2 = new Thread(t); 6 Thread t3 = new Thread(t); 7 Thread t4 = new Thread(t); 8 //设置线程名称 9 t1.setName("线程一"); 10 t2.setName("线程二"); 11 t3.setName("线程三"); 12 t4.setName("线程四"); 13 //启动线程 14 t1.start(); 15 t2.start(); 16 t3.start(); 17 t4.start(); 18 } 19 } 20 21 class Ticket implements Runnable { 22 int ticket = 100; 23 Object obj = new Object(); 24 25 public void run() { 26 while (true) { 27 synchronized (obj)//同步代码块,进行上锁 28 { 29 if (ticket > 0) { 30 try { 31 Thread.sleep(10); 32 } catch (Exception e) { 33 } 34 System.out.println("正在卖第" + ticket-- + "张票"); 35 } 36 } 37 } 38 } 39 }
案例三:银行存钱系统
1 public class BankDemo { 2 public static void main(String[] args) { 3 Bank b = new Bank(); 4 Thread t1 = new Thread(b); 5 Thread t2 = new Thread(b); 6 t1.setName("储户A"); 7 t2.setName("储户B"); 8 t1.start(); 9 t2.start(); 10 } 11 } 12 13 //创建一个类,实现Runnable接口 14 class Bank implements Runnable { 15 Deposit d = new Deposit(); 16 17 public void run() { 18 for (int i = 0; i < 3; i++) { 19 d.add(); 20 } 21 } 22 } 23 24 //定义一个存钱的类 25 class Deposit { 26 int sum = 0; 27 Object obj = new Object(); 28 29 public void add() { 30 synchronized (obj) { 31 sum += 100; 32 System.out.println(Thread.currentThread() + "sum=" + sum); 33 } 34 } 35 }