线程的创建
继承Thread 类创建线程
12345678910111213public class MyThread extends Thread{//重写run()public voud run(){System.out.println("This is MyThreas...");}public static void main(String[] args){//启动第一个线程new MyThread().start();//启动第二个线程new MyThread().start();}}实现Runnable接口创建线程
1234567891011121314public class MyRunnable implements Runnable{//重写run()public voud run(){System.out.println("This is MyThreas...");}public static void main(String[] args){MyRunnable myRunnable = new MyRunnable();//启动第一个线程new Thread(myRunnable).start();//启动第二个线程new Thread(myRunnable).start();}}使用Callable和Future创建线程
12345678910111213141516171819202122232425262728public class CallableTest {public static void main(String[] args){//使用Java8新加入的Lambda表达式创建匿名的Callable对象//使用FutureTask包装Callable对象,该类的get()方法可以获得Callable的call()方法的返回值FutureTask<Integer> task = new FutureTask<Integer>((Callable<Integer>)() -> {//重写Callable的call()方法int i = 0;for ( ; i < 100 ; i++){System.out.println(Thread.currentThread().getName()+"的循环变量i的值:"+i);}//call()方法有返回值return i;});for(int i = 0 ; i < 1000 ;i++){System.out.println(Thread.currentThread().getName() + i);if(i == 20){//实质还是以Callable对象来创建并启动线程new Thread(task,"带返回值的线程").start();}}try {//这里的get()方法会阻塞,直到子线程执行结束获得返回值System.out.println("子线程的返回值是:" + task.get());} catch (Exception e) {e.printStackTrace();}}}
线程的死亡
- run()或call()方法执行完成,线程正常结束
- 线程抛出一个未捕获的Exception或Error
- 调用线程的stop()方法结束线程,容易导致死锁,不推荐使用。(关闭线程可参考《JAVA:如何优雅地关闭一个线程》)
- 如果线程没有被阻塞,调用interrupt(),表示进入中断状态,执行到wait(),sleep(),join()时,马上会抛出 InterruptedException;如果线程已经被阻塞,线程就将得到InterruptedException异常(该线程必须事先预备好处理此状况),接着逃离阻塞状态。如果异常没有被捕获处理,将会导致线程结束
线程的控制
- join():执行其它线程的join()方法时,调用线程将被阻塞,直到join()方法加入的join线程执行完成为止。通常由使用线程的程序调用,将大问题划分成许多小问题,每个小问题分配一个线程,当所用的小问题得到处理后,再调用主线程进一步操作
- sleep):使线程暂停一段时间,并进入阻塞状态
- yield():让当前的线程暂停,不会阻塞当前线程,只是将线程转入就绪状态
- setPriority(int newPriority):改变线程的优先级
线程同步
使用同步代码块,下面示例中的obj就是同步监视器
1234synchronized(obj){......}使用同步方法,同步方法的同步监视器是this,也就是调用该方法的对象
1234public synchronized void test(){......}使用同步锁(Lock)
123456789101112131415161718class Test{//定义锁对象private final ReentrantLock lock = new ReentrantLock();//定义需要线程安全的方法public void eat(){//加锁(在锁释放之前,只有一个线程可以获得该锁)lock.lock();try{......}finally{//释放锁(一定要放在finally块中,否则程序出现异常会无法释放锁对象,造成死锁)lock.unlock();}}}
死锁
|
|
- 从mian()方法开始看,新线程启动,执行run()方法,新线程获得b对象的锁,进入sleep()等待(此时新线程获得b的锁)
- 接着main线程继续执行init()方法,main线程活动a的锁,进入sleep()等待(main线程获得a的锁)
- (新线程)接着run()中的b.bTest(a)方法执行到a.last(),需要获得a对象的锁,而当前a的锁被main线程占有,故阻塞等待
- (main线程)接着init()中的a.aTest(b)方法执行到b.last(),需要获得b对象的锁,而当前b的锁被新线程占有,故阻塞等待
- 互相阻塞等待对方释放锁,形成死锁
线程的通信
- 由同步监视器调用wait()、notify()、notifyAll()方法
- wait():导致当前进程等待,直到其他线程调用该同步监视器的notify()、notifyAll()方法唤醒
- notify():唤醒在该同步监视器下等待的任意一个线程
- notifyAll():唤醒在该同步监视器下等待的全部线程
- 当用Lock同步线程时,使用Condition控制通信12345private final Lock lock = new ReentrantLock();//指定lock对象对应的Conditionprivate final Conndition cond = lock.newCondition();//使用cond对象调用wait()、notify()、notifyAll()方法控制线程
线程池
- 由于新建线程的开销比较大,故使用线程池在启动时创建大量空闲线程,将Runnable或者Callable对象传给线程池,将会马上执行对应的run()和call()方法,执行完后不会关闭线程,而是让线程变回空闲状态
- 使用Executors工厂类来创建线程池,提供以下静态工厂方法创建进程池
- ExecutorService newCashedThreadPool():创建具有缓存功能的线程池
- ExecutorService newFixedThreadPool(int nThreads):创建一个可重用,具有固定线程数的线程池
- ExecutorService newSingleThreadExecutor():创建只有一个线程的线程池
- ScheduledExecutorService newScheduledThreadPool(int corePoolSize):创建具有指定数量线程的线程池,可以指定延时执行任务
- ScheduledExecutorService newSingleThreadScheduledExecutor():创建具有一个线程的线程池,可以指定延时执行任务
|
|