1.概念
关于多线程的基础概念不做过多概述!
线程:简单来讲,就是程序运行的最小单位,比如我们运行的main方法也是一个线程。一个进程可能有几个线程在运行计算数据。
多线程的优点:多任务操作系统的有点相信都体会过了,可以最大程度上利用CPU 的空闲时间,CPU在多个线程来回切换,大大减少单任务处理由于进程等待带来的空闲时间的CPU空闲,线程之间是相互独立的。
2.使用多线程
想要学习一个技术就得接近它 控制它
1) 实现方式
a.继承Thread
b.实现Runnable接口
先来看看Thread的结构
public class Thread implements Runnable {
从源代码可以发现Thread实现了Runable接口,他们之间有多态关系其实,使用继承Thread方式最大的局限就是多继承,所以为了支持多继承,完全可以实现Runnable接口,一边继承一边实现。两种方式没有本质区别,一个是重载一个重写。
public class Manager extends Thread{ private List<String> queue = new LinkedList<String>(); @Override public void run(){ System.out.println(); } public static void main(String[] args) { Manager m = new Manager(); m.start(); } }
线程执行特性:
1.随机性
2.不确定性
多线程执行的先后顺序是不确定性的,调用start()方法只是告诉"线程规划器" 我已经做好执行的准备了 具体执行顺序让系统安排一个时间去执行run() 方法。start()的先后顺序不决定具体的线程执行顺序。具体代码这里就不贴了。
2) 线程安全
自定义线程类中的实例变量有共享变量和飞共享变量之分。
a.不共享的情况
各自线程有各自的实例变量相互不影响,多线程操作也能保证安全
b.共享情况
public class Manager extends Thread{ int count = 10; @Override public void run(){ super.run(); count--; System.out.println("由"+Thread.currentThread()+"线程计算count:"+count); } public static void main(String[] args) { Manager m = new Manager(); Thread p = new Thread(m,"Manager1"); Thread c = new Thread(m,"Manager2"); Thread v = new Thread(m,"Manager3"); p.start(); c.start();v.start(); } }
可以发现执行结果:由Thread[Manager2,5,main]线程计算count:8 由Thread[Manager3,5,main]线程计算count:7 由Thread[Manager1,5,main]线程计算count:8
线程1、3对count同时进行了操作,产生非线程安全。想要的结果是依次递减打印。
i- -可以分解3步:1.去的原有i的值;
2.计算i - -;
3.给i进行赋值;
如果多线程对着个参数进行操作,很有可能在第一步两个线程同时取到同样的数据,导致结果重复;其实很典型的列子就是消费者模式,多个消费者做在同一张桌子上吃饭,生产者端吃的。消费者必须等生产者端上来才能开始吃,也就是安排队的方式。具体怎么处理后续说明
3) sleep方法
简单介绍一下sleep()方法于我的理解,当然还有currentThread、isAlive、getId等方法,就不一一介绍了。
a)currentThread() 方法是获取当前线程对象
b)sleep() 方法是让当前线程在指定的毫秒数暂停。是指的this.currentThread()返回的线程
这里就要说到sleep和wait()的区别了:
1.sleep() 是Thread类的一个静态方法 让调用的当前线程暂停一段时间 wait 是Object 的方法
2.sleep 不会释放对象锁、放弃CPU ,wait会释放对象锁 且让出CPU
3.wait 必须依赖于synchroid ,否者会抛出java.lang.IllegalMonitorStateException 异常,先要获取对象锁 可以通过notify 、notifyall 唤醒
4) 停止线程
停止线程意味着在线程处理完任务之前停掉正在做的任务,看似简单,但必须做好防范,以便达到预期的效果。
停止线程可以用Thread.stop() ,强制性停止当前对象线程,而且会释放锁,导致和预期不一致的结果
1) interrrupt()
并不能真正的停止线程:
public class ThreadTest extends Thread{ @Override public void run(){ for(int i = 0; i< 50000;i++){ System.out.println("i:"+i); } } public static void main(String[] args) { ThreadTest test = new ThreadTest(); test.start(); test.interrupt(); try { Thread.sleep(100); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println("interrupt"); test.stop(); } }
运行结果:可以看出,调用interrupt() 方法后线程并没有立即停止,而是设置线程停止标志.i:49994 i:49995 i:49996 i:49997 i:49998 i:49999
2)判断线程是否停止
a)Thread. interrupted() 测试当前线程是否停止 静态方法
b)this.isInterrupted() 判断线程是否停止 实例方法
那么这两个方法有什么区别呢?先来看看interrupted的解释:测试当前线程是否中断
运行结果:public static void main(String[] args) { ThreadTest test = new ThreadTest(); test.start(); Thread.currentThread().interrupt(); System.out.println(Thread.interrupted()); System.out.println(Thread.interrupted()); System.out.println("interrupt"); }
从上述结果来看 ,interrupted()的确判断出了线程是否中断状态,但为什么第二个位false呢,从官方帮助文档查阅发现:true false interrupt i:0
测试当前线程是否中断,线程的中断状态由改方法清除。换句话说调用这个方法后线程的中断状态会被清除,这就是为什么第二次调用会是false。
再来看isInterrupted()方法:终端正在运行的线程,不是static方法;
运行结果:public class ThreadTest extends Thread{ @Override public void run(){ for(int i = 0; i< 50000;i++){ System.out.println("i:"+i); } } public static void main(String[] args) { ThreadTest test = new ThreadTest(); test.start(); try { Thread.sleep(10); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } Thread.currentThread().interrupt(); test.interrupt(); System.out.println(test.isInterrupted()); System.out.println(test.isInterrupted()); System.out.println("interrupt"); test.stop(); } }
可以看到并没有清除状态。i:306 true true interrupt
最后再来看下这两个方法的解释吧:
a) interrupted() 测试当前线程中断状态,并具有清除标志置为false的功能,静态方法
b) isinterrupted() 测试线程的终端状态,但不清状态标志 实例方法
3) 能停止的线程
据以上的内容,可以用在线程中判断线程状态的方式来停止线程:
运行结果:public class ThreadTest extends Thread{ @Override public void run(){ for(int i = 0; i< 50000;i++){ if(this.isInterrupted()){ break; } System.out.println("i:"+i); } System.out.println("thread is end"); } public static void main(String[] args) { ThreadTest test = new ThreadTest(); test.start(); try { Thread.sleep(10); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } Thread.currentThread().interrupt(); test.interrupt(); System.out.println(test.isInterrupted()); System.out.println(test.isInterrupted()); } }
i:278 i:279 true true thread is end
从运行结果上看:线程跳出了循环,但是线程并没有真正的停止。如何解决呢,看下更新后的代码:
运行结果:public class ThreadTest extends Thread{ @Override public void run(){ try { for(int i = 0; i< 50000;i++){ if(this.isInterrupted()){ throw new InterruptedException(); } System.out.println("i:"+i); } System.out.println("thread is end"); } catch (Exception e) { System.out.println("线程停止,进入catch块"); // TODO: handle exception } } public static void main(String[] args) { ThreadTest test = new ThreadTest(); test.start(); try { Thread.sleep(10); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } Thread.currentThread().interrupt(); test.interrupt(); System.out.println(test.isInterrupted()); System.out.println(test.isInterrupted()); } }
由此可见线程进入异常停止!i:292 true true 线程停止,进入catch块
4) 沉睡中停止
如果线程在sleep中停止呢?会发生什么情况:
运行结果:public class ThreadTest extends Thread{ @Override public void run(){ try { Thread.sleep(200000); System.out.println("thread is end"); } catch (Exception e) { System.out.println("线程停止,进入catch块"); // TODO: handle exception e.printStackTrace(); } } public static void main(String[] args) { ThreadTest test = new ThreadTest(); test.start(); try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } test.interrupt(); System.out.println(test.isInterrupted()); System.out.println(test.isInterrupted()); } }
从以上结果来看,在沉睡中停止线程,会进入到catch(),且清除了线程运行状态标志。sleep不会释放线程锁及CPU,导致中断抛出异常false false 线程停止,进入catch块 java.lang.InterruptedException: sleep interrupted at java.lang.Thread.sleep(Native Method) at com.wyj.design.consumer.ThreadTest.run(ThreadTest.java:7)
相反如果打断后在沉睡呢:
从以上结果来看,先中断线程,在沉睡会进入到catch()。i:199998 i:199999 线程停止,进入catch块 java.lang.InterruptedException: sleep interrupted at java.lang.Thread.sleep(Native Method) at com.wyj.design.consumer.ThreadTest.run(ThreadTest.java:11)
5) 能停止的线程
暴力的停止线程stop().
调用stop() 会抛出ThreadDeth 异常,通常情况下不需要现行的捕捉。
stop 方法已被JDK标记为过期的方法作废
1.stop方法会释放锁 可能会导致线程安全
2.异常停止线程导致结束流程没有执行
5) 守护线程
在java线程中有两种线程:用户线程、守护线程
守护线程是一种特殊的线程,随着用户线程,在JVM中只要有一个非守护线程,守护线程就在工作,最常见的是垃圾回收线程。JVM所有线程结束了,垃圾回收线程也就结束了。
运行结果:public class ThreadTest extends Thread{ @Override public void run(){ try { int i = 0; while(true){ System.out.println("i:"+i++); Thread.sleep(1000); } } catch (Exception e) { System.out.println("线程停止,进入catch块"); // TODO: handle exception e.printStackTrace(); } } public static void main(String[] args) { ThreadTest test = new ThreadTest(); test.setDaemon(true); test.start(); try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
i:0 i:1
用户线程结束了,守护线程也就结束了。
3 小结
本文介绍了Thread的一些基础知识。在使用线程的时候可能会发生很多意想不到的情况,这也是多线程一些不可预知的的一个体现,需要学习这个基础知识和一些常用的情况来规避这些问题。