一.并发和并行
并发是单位时间内可以处理的事件数目 并行是同一时刻,可以同时运行的线程数量 如果有10个cpu核心,同时可以运行10个线程,那么并行度就是10,每一个任务处理是5ms,那么每秒并发就是10*200=2000
二.查看已有线程
2.1 代码
public static void main(String[] args) {
ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
ThreadInfo[] threadInfos = threadMXBean.dumpAllThreads(false, false);
for (ThreadInfo ti : threadInfos) {
System.out.println("[" + ti.getThreadId() + "]" + ti.getThreadName());
}
}
2.2 输出:
[5]Attach Listener -- 获取程序属性
[4]Signal Dispatcher -- 分发信号
[3]Finalizer --- 调用对象的finalize()方法的线程
[2]Reference Handler -- 清除引用
[1]main --主线程
三.启动一个线程
3.1 继承Thread类
public class ThreadImpl extends Thread {
@Override
public void run() {
System.out.println("继承Thread类实现线程...");
}
}
3.2 实现Runnable接口
public class RunnableImpl implements Runnable {
@Override
public void run() {
System.out.println("实现Runnable接口实现多线程...");
}
}
3.3 实现Callable接口
public class CallableImpl implements Callable<String> {
@Override
public String call() throws Exception {
Thread.sleep(50);
System.out.println("实现Callable接口实现多线程...");
return "callable implement";
}
}
3.4 测试:
public class Test {
public static void main(String[] args) throws ExecutionException, InterruptedException, TimeoutException {
Thread t1 = new ThreadImpl();
t1.start();
t1.interrupt();//中断线程,将标志位置为true
Runnable r1 = new RunnableImpl();
Thread t2 = new Thread(r1);
t2.start();
Callable c1 = new CallableImpl();
FutureTask<String> futureTask = new FutureTask<>(c1);
Thread t3 = new Thread(futureTask);
t3.start();
//get方法是阻塞的,可以添加阻塞的超时时间
System.out.println("获取futureTask的返回值:"+futureTask.get(51, TimeUnit.MILLISECONDS));
}
}
比较: 继承Thread类: 没有返回值(单继承局限性) 实现Runnable: 没有返回值(多接口实现,灵活) 实现Callable: 可以返回结果
四.停止一个线程
4.1 线程停止
线程执行完毕自然停止 抛出异常,线程内部未处理异常,抛出异常的话线程就退出了 stop方法(不会释放资源) interrupt:将线程的中断标志位值true,该方法并不会立即停止该线程,类似于告知该线程,尽快停止,至于线程何时停止,则由线程本身决定(线程本身有足够的灵活度来进行停止线程前的处理),在java中线程之间是协作式的,非抢占式的。
4.2 辨析 t1.interrupt(), t1.isInterrupted(), Thread.interrupted();
interrupt();
isInterrupted();
判断当前线程是否处于中断状态,调用的是 private native boolean isInterrupted(false);不会修改线程标志位
Thread.interrupted();
判断当前线程是否处于中断状态,修改标志位为false,底层调用的是: private native boolean isInterrupted(true);
4.3 静态方法Thread#isInterrupted()
/**
* Tests if some Thread has been interrupted. The interrupted state
* is reset or not based on the value of ClearInterrupted that is
* passed.
* 由传入的boolean值决定是否会修改字段标志位,isInterrupted不会修改,
* 静态interupted方法会修改
*/
private native boolean isInterrupted(boolean ClearInterrupted);
方法1是Thread类提供的方法,通常是通过线程对象去中断某个线程。线程对象都是Thread类对象可以调用该方法,只有在Runnable的实现类的内部无法直接调用。
方法2是Thread类提供的方法,通常是用于判定一个线程是否处于中断状态。如果线程是Thread子类,可以直接调用,如果是Runnable实现类,那么该类的内部只能Thread.currentThread().isInterrupted(),先获取当前线程对象,再调用方法,
方法3通常是用于判定一个线程是否处于中断状态。因为是静态方法,方法内部就是通过Thread.currentThread()先获取的当前线程对象,在任何地方都可以调用。
4.4 方法对比
方法名称 作用 备注 interrupt() 通过线程对象去中断某个线程 Thread类的方法,通常是线程外部通过线程对象去中断对应的线程,本质是将线程中断标志位置为true isInterrupted() 通常是用于判定一个线程是否处于中断状态 Thread类的方法,在Runnable实现类内部只能采用这样的方式调用Thread.currentThread().isInterrupted(),不会修改标志位,通常用来判断一个线程是否处于中断状态,本质是判断中段标志是否为true Thread.interrupted() 判定一个线程是否处于中断状态 静态方法,内部通过Thread.currentThread()先获取的当前线程对象,判断标志位是否为true,若为true,还会修改标志位为false,比如在AQS的parkAndCheckInterrupt方法中会用该方法判断线程park结束之后,是否被中断了,并且会清除标志位让线程继续执行(其目的是判断线程park结束是被中断还是park被唤醒,但是又要让线程继续执行,因此要清除标志位)
五.InterruptedException异常
一个进程在捕获到中断信号后(比如外部调用了这个线程的interrupt方法),进程会抛出该异常,并且会将中断标志位置为false,如此一来,外部调用的中断信号不会生效,该线程并不会停止。对应的处理方法是,线程内部捕获到该异常后,在catch到异常后主动调用interupt方法,将自身中断标志位置为true,当然这给线程留下来足够的线程中断所要做的清理工作。 常用的阻塞方法都会抛出该异常,如sleep,join,此时可以感知到外部中断该线程(外部调用该线程的interrupt方法,线程内部会抛出异常,并将标志位置为false),如果采用自定义标志作为线程循环处理的标记,那么仅仅判断标记是不够的,因为循环内部可能阻塞,阻塞处就无法及时感知到外部对标志的修改,这里的判断条件还需要加上该线程的标志位,这样只要外部发出了中断该线程的信号,线程内部阻塞处就会抛出中断异常,循环条件就可以感知到内部的阻塞方法会抛出InterruptedException异常,
5.1 通过标志位中断线程
while ( flag || ! isInterrupted ( ) ) {
try {
take ( ) ;
} catch ( InterruptedException e) {
Thread. currentThread ( ) . interrupt ( ) ;
}
}
5.2.代码示例
public class EndThreadInterruptException {
public static void main ( String[ ] args) throws InterruptedException {
Thread t1 = new Thread ( new MyRunnableInterruptedException ( ) , "Thread-1" ) ;
t1. start ( ) ;
Thread t2 = new Thread ( new MyRunnableInterrupteSelf ( ) , "Thread-2" ) ;
t2. start ( ) ;
Thread t3 = new Thread ( new MyThreadInterruptedException ( ) , "Thread-3" ) ;
t3. start ( ) ;
Thread. sleep ( 4000 ) ;
t1. interrupt ( ) ;
t2. interrupt ( ) ;
t3. interrupt ( ) ;
}
private static class MyRunnableInterruptedException implements Runnable {
@Override
public void run ( ) {
while ( ! Thread. currentThread ( ) . isInterrupted ( ) ) {
try {
Thread. sleep ( 100 ) ;
} catch ( InterruptedException e) {
e. printStackTrace ( ) ;
System. out. println ( Thread. currentThread ( ) . getName ( ) + "sleep抛出InterruptedException异常后,中断标志位:" + Thread. currentThread ( ) . isInterrupted ( ) ) ;
}
System. out. println ( "线程" + Thread. currentThread ( ) . getName ( ) + "执行..." ) ;
}
}
}
private static class MyRunnableInterrupteSelf implements Runnable {
@Override
public void run ( ) {
while ( ! Thread. currentThread ( ) . isInterrupted ( ) ) {
try {
Thread. sleep ( 100 ) ;
} catch ( InterruptedException e) {
e. printStackTrace ( ) ;
System. out. println ( Thread. currentThread ( ) . getName ( ) + "sleep抛出InterruptedException异常后,中断标志位:" + Thread. currentThread ( ) . isInterrupted ( ) ) ;
Thread. currentThread ( ) . interrupt ( ) ;
System. out. println ( Thread. currentThread ( ) . getName ( ) + "调用interrupt后,中断标志位:" + Thread. currentThread ( ) . isInterrupted ( ) ) ;
}
System. out. println ( "线程" + Thread. currentThread ( ) . getName ( ) + "执行..." ) ;
}
}
}
private static class MyThreadInterruptedException extends Thread {
@Override
public void run ( ) {
while ( ! this . isInterrupted ( ) ) {
try {
Thread. sleep ( 100 ) ;
} catch ( InterruptedException e) {
e. printStackTrace ( ) ;
System. out. println ( Thread. currentThread ( ) . getName ( ) + "sleep抛出InterruptedException异常后,中断标志位:" + Thread. currentThread ( ) . isInterrupted ( ) ) ;
}
System. out. println ( "线程" + Thread. currentThread ( ) . getName ( ) + "执行..." ) ;
}
}
}
}
线程Thread-1执行...
线程Thread-2执行...
线程Thread-3执行...
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at com.intellif.mozping.createthread.EndThreadInterruptException$MyThreadInterruptedException.run(EndThreadInterruptException.java:77)
at java.lang.Thread.run(Thread.java:748)
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at com.intellif.mozping.createthread.EndThreadInterruptException$MyRunnableInterruptedException.run(EndThreadInterruptException.java:42)
at java.lang.Thread.run(Thread.java:748)
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at com.intellif.mozping.createthread.EndThreadInterruptException$MyRunnableInterrupteSelf.run(EndThreadInterruptException.java:58)
at java.lang.Thread.run(Thread.java:748)
线程Thread-2执行...
线程Thread-1执行...
线程Thread-3执行...
Thread-3 sleep抛出InterruptedException异常后,中断标志位:false
线程Thread-3执行...
Thread-1 sleep抛出InterruptedException异常后,中断标志位:false
线程Thread-1执行...
Thread-2 sleep抛出InterruptedException异常后,中断标志位:false
Thread-2调用interrupt后,中断标志位:true
线程Thread-2执行...
线程Thread-3执行...
线程Thread-1执行...
线程Thread-3执行...
线程Thread-1执行...
线程Thread-3执行...
从输出看到,线程1和3还会继续运行,因为中断标志位抛出异常后置为false,线程并不会停止。而线程2则在捕获异常后再次中断自身后停止了运行。
六.并发编程要点
6.1 好处
充分利用cpu的资源、加快用户响应的时间,程序模块化,异步化
6.2 风险和注意实现
线程共享资源,存在冲突; 容易导致死锁; 启用太多的线程,就有搞垮机器的可能