Java多线程
1、多线程概述
当一个程序运行时,内部可能包含了多个顺序执行流,每个顺序执行流就是一个线程。主要以下几个优点:
- 线程之间很容易实现共享内存
- 创建线程代价较小
- Java语言内置多线程功能支持
2、线程的创建和启动
所有的线程对象都是Thread类或其子类的对象,每一个线程完成一定的任务。
Java定义了两种创建线程的方法:
方法一:继承Thread类创建线程类
Ø 定义Thread的子类,并重写run方法
Ø 创建Thread子类的实例,即创建了线程对象
Ø 用线程对象的start方法来启动该线程
1 public class DemoThread extends Thread 2 { 3 private int i; 4 String name; 5 public DemoThread(String name) 6 { 7 super(name); 8 } 9 public void run() 10 { 11 for(;i<100;i++) 12 System.out.println(Thread.currentThread().getName()+"-------"+i); 13 14 } 15 public static void main(String[] args) 16 { 17 for(int i=0;i<100;i++) 18 { 19 System.out.println(Thread.currentThread().getName()+"-----------"+i); 20 21 if(i==20) 22 { 23 new DemoThread("windows1").start(); 24 new DemoThread("windows2").start(); 25 } 26 } 27 } 28 }
Ø 定义Runnable接口的实现类,并重写该接口的run方法(该方法是该线程的线程执行体)
Ø 创建Runnable实现类的实例,并以此实例作为Thread的target来创建Tread对象
1 public class DemoThread implements Runnable 2 { 3 private int i; 4 public void run() 5 { 6 for(;i<100;i++) 7 System.out.println(Thread.currentThread().getName()+"-------"+i); 8 9 } 10 public static void main(String[] args) 11 { 12 for(int i=0;i<100;i++) 13 { 14 System.out.println(Thread.currentThread().getName()+"-----------"+i); 15 16 if(i==20) 17 { 18 DemoThread t=new DemoThread(); 19 new Thread(t,"Windows1").start(); 20 new Thread(t,"Windows2").start(); 21 } 22 } 23 } 24 }
两种方法比较:
- 采用实现Runnable接口方式的线程还可以继承其他类,而继承Tread类后不能再继承其他父类。
- 实现Runnable接口方式的线程可以共享一个target对象,适合多个相同线程来处理同一份资源的情况
- 若需要访问当前线程,实现Runnable接口方式只能使用Thread.currentThread()方法,而继承Thread可直接使用this即获取当前线程。
3、线程的生命周期
线程被创建并启动以后要经历五种状态,分别是新建(New)、就绪(Runnable)、运行(Running)、阻塞(Blocked)和死亡(Dead)。
新建:使用关键字new创建一个线程后,处于新建状态,此时仅由虚拟机为其分配内存并初始化成员变量,程序不会执行线程执行体。就绪:当线程对象调用start()方法后,该线程处于就绪状态,虚拟机为其创建调用栈和程序计数器,但是此时线程并没有运行,只是表示线程可以运行了。
运行:当处于就绪状态的线程获得了CPU,开始执行run方法,则该线程处于运行状态。
阻塞:当发生线程调用sleep方法、阻塞式IO方法、suspend方法、等待通知(notify),线程将近阻塞状态。阻塞状态在合适的时候会重新进入就绪状态。
死亡:线程会以以下三种方式结束,然后就处于死亡状态,run方法执行完成、线程抛出未捕获的异常、直接跳跃stop方法结束进程。
4、线程的控制
isAlive():测试线程是否处于活动状态
isDaemo():测试进程是否为守护进程
join():等待该进程终止
sleep():在指定的毫秒数内让正在执行的线程休眠,该线程不丢失任何监视器的所属权
yield():暂停当前正在执行的线程对象,并执行其他线程,它是将线程转入就绪状态
setPriority(int newPriority):更改线程的优先级
5、线程的同步
当两个或多个线程需要访问同一共享资源时,需要某种方式来确保资源在某一时刻只被一个线程使用,这个方式称为“同步“。
为了解决同步问题,java引入同步监视器,代码块格式如下:
synchronized (obj) { }
同步锁:它具有与使用
synchronized
方法和语句所访问的隐式监视器锁相同的一些基本行为和语义,但功能更强大。 使用Lock对象的代码格式如下:1 public class X 2 { 3 private final ReentrantLock lock=new ReentrantLock(); 4 public void m() 5 { 6 lock.lock();//加锁 7 try 8 { 9 //需要保证线程安全的代码 10 } 11 finally 12 { 13 lock.unlock();//释放锁 14 } 15 } 16 } 17
6、线程的通信
Java为了避免轮询检测,通过wait()、notify()和notifyAll()方法实现进程内通信的机制。
wait():告诉调用线程放弃监控器进入等待模式直到其他线程进入同一监控器并调用notify()方法。
wait():告诉调用线程放弃监控器进入等待模式直到其他线程进入同一监控器并调用notify()方法。
notify():唤醒在此同步器上等待的单个线程
notifyAll():唤醒在此同步器上等待的所有线程
7、线程池
线程池在系统启动时就创建大量空闲的线程,程序将一个Runnable对象传给线程池,线程池就会启动一个线程来执行该对象的run方法,当run方法执行结束后该线程不会死亡,而是再次返回线程池中称为空闲状态的,等待执行下一个Runnable对象的run方法。
使用线程池来执行线程任务的步骤如下:
- 调用Executors类的静态工厂方法创建一个ExecutorService对象,该对象代表一个线程池。
- 创建Runnable实现类或Callable实现类的实例,作为线程执行任务。
- 调用ExecutorService对象的submit方法提交Runnable实例或Callable实例。
- 当不想提交任何任务时调用ExecutorService对象的shutdown方法来关闭线程池。
eg:
1 class TestThread implements Runnable 2 { 3 public void run() 4 { 5 for(int i=0;i<50;i++) 6 { 7 System.out.println(Thread.currentThread().getName()+"---"+i); 8 } 9 } 10 } 11 public class TestMain 12 { 13 14 public static void main(String[] args) 15 { 16 // TODO 自动生成的方法存根 17 TestThread t=new TestThread(); 18 ExecutorService pool=Executors.newFixedThreadPool(6); 19 pool.submit(new Thread(t)); 20 pool.submit(new Thread(t)); 21 pool.shutdown(); 22 } 23 24 }