###线程的概念: 1.线程 cpu执行或调度的基本单位;(进程里最小的一个执行单元)(程序里不同的执行路径) 2.进程 cpu资源分配的基本单位;
###线程的基本方法: 1.sleep:当前线程睡眠一段时间,将CPU时间片让给别的线程; 2.yield:让出当前cpu,返回就绪状态,进入等待队列,任然有可能继续被cpu选中继续执行; 3.join:T1中调用T2.join(),停止T1的执行,等待T2执行完毕,再继续执行T1;
###创建线程的五种方式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 import java.util.concurrent.*;public class HowToCreateThread { public static class MyThread extends Thread { @Override public void run () { System.out.println("Hello MyThread!" ); } } public static class MyRunnable implements Runnable { @Override public void run () { System.out.println("Hello MyRunnable!" ); } } public static class MyCallable implements Callable <String > { @Override public String call () throws Exception { System.out.println("Hello MyCallable" ); return "success" ; } } public static void main (String[] args) throws ExecutionException, InterruptedException { new MyThread().start(); new Thread(new MyRunnable()).start(); new Thread(() -> System.out.println("Hello MyLambda!" )).start(); ExecutorService service = Executors.newCachedThreadPool(); service.execute(() -> { System.out.println("Hello ThreadPool" ); }); Future<String> future = service.submit(new MyCallable()); String str = future.get(); System.out.println(str); service.shutdown(); FutureTask<String> futureTask = new FutureTask<>(new MyCallable()); Thread thread = new Thread(futureTask); thread.start(); System.out.println(futureTask.get()); } }
Java的6种线程状态: 1.NEW: 线程刚刚创建,还没有启动,调用start()方法之前;
2.RUNNABLE:可运行状态,由线程调度器安排执行;在此状态下有两种表现,READY(就绪状态)(当前没有分配到时间片,当线程被
调度器选中,CPU为其分配时间片进行线程任务执行),RUNNABLE(已经分配到时间片,正在执行线程任务,可以调用Thread.yield方法
进入READY状态),当发生了线程上下文切换,就有线程被挂起进入了READY(就绪状态);
3.WAITING:等待被执行唤醒;(注意:JUC的锁都是基于CAS来实现的,例如ReentrantLock().lock()方法进入的是waiting状态)
4.TIME WAITING:隔一段时间后自动唤醒;
5.BLOCKED:被阻塞,正在等待锁;(只有在Synchronized没有获取到锁资源的时候才会进入该状态)
6.TERMINATED:线程结束;(Thread.join()等待线程执行结束,线程状态进入terminated)
###Sleep,Wait,Notify 1.Sleep与Wait的区别,Sleep不释放锁; 2.Wait线程挂起,释放锁,且必须配合Synchronized使用; 3.notify唤醒其他线程,且必须配合Synchronized使用;
###.线程的打断(interrupt):
1 2 3 public void interrupt () public boolean isInterrupted () public static boolean interrupted ()
interrupt和sleep() wait() join() sleep()方法在睡眠的时候,不到时间是没有办法叫醒的,这个时候可以用interrupt设置标志位,然后呢必须得catch InterruptedException来进行处理,决定继续睡或者是别的逻辑,(自动进行中断标志复位)
interrupt是否能中断正在竞争锁的线程 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 public class Interrupt_and_sync { private static Object o = new Object(); public static void main (String[] args) { Thread t1 = new Thread(()-> { synchronized (o) { SleepHelper.sleepSeconds(10 ); } }); t1.start(); SleepHelper.sleepSeconds(1 ); Thread t2 = new Thread(()-> { synchronized (o) { } System.out.println("t2 end!" ); }); t2.start(); t2.interrupt(); } }
interrupt()不能打断正在竞争锁的线程synchronized(),同时interrupt()也不能打断ReentrantLock的lock()方法;
如果想打断正在竞争锁的线程,使用ReentrantLock的lockInterruptibly() 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 import java.util.concurrent.locks.ReentrantLock;public class Interrupt_and_lockInterruptibly { private static ReentrantLock lock = new ReentrantLock(); public static void main (String[] args) { Thread t1 = new Thread(()-> { lock.lock(); try { SleepHelper.sleepSeconds(10 ); } finally { lock.unlock(); } System.out.println("t1 end!" ); }); t1.start(); SleepHelper.sleepSeconds(1 ); Thread t2 = new Thread(()-> { System.out.println("t2 start!" ); try { lock.lockInterruptibly(); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } System.out.println("t2 end!" ); }); t2.start(); SleepHelper.sleepSeconds(1 ); t2.interrupt(); } }
由以上述结论其实可以看出:ReentrantLock()的lock()方法虽然也是使线程进入waiting状态,但是还是和Thread.wait()方法还是有所区别的,Thread.wait()进入的waiting状态可以被interrupt打断,但是lock()方法不行,想要被打断,需要 使用ReentrantLock()的lockInterruptibly();
###如何优雅的结束线程
自然结束(能自然结束就尽量自然结束);
stop();(这三个方法已经废弃,主要是因为stop方法是直接结束线程,不能够执行善后操作, 很容易造成数据不一致)
suspend():让线程停止,但是线程任然持有锁资源,容易造成死锁; resume();恢复停止的线程继续执行
volatile标志(是一个volatile属性,线程执行过程中使用while循环检查该属性,判断线程是否结束执行)
不适合某些场景(比如还没有同步的时候,线程做了阻塞操作,没有办法循环回去)
打断时间也不是特别精确,比如一个阻塞容器,容量为5的时候结束生产者, 但是,由于volatile同步线程标志位的时间控制不是很精确,有可能生产者还继续生产一段儿时间
interrupt() and isInterrupted(比较优雅)
###线程间数据交换的方式 1.使用Exchanger来实现两个线程交换数据,两个线程在同一个时间点阻塞着才能完成数据交换;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 public class MyTestExchanger { static Exchanger<String> exchanger = new Exchanger<>(); public static void main (String[] args) { new Thread(()->{ String s = "T1" ; try { s = exchanger.exchange(s); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + " " + s); }, "t1" ).start(); new Thread(()->{ String s = "T2" ; try { s = exchanger.exchange(s); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + " " + s); }, "t2" ).start(); } }
2.使用SynchronousQueue来实现数据传递,a线程向SynchronousQueue中put数据,b线程从Queue中取数据,如果没有线程取数据,
a线程会一直阻塞住;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 public class MySynchronusQueue { public static void main (String[] args) throws InterruptedException { BlockingQueue<String> strs = new SynchronousQueue<>(); new Thread(()->{ try { System.out.println(strs.take()); } catch (Exception e) { e.printStackTrace(); } }).start(); new Thread(()->{ try { strs.put("aaa" ); } catch (Exception e) { e.printStackTrace(); } }).start(); System.out.println(strs.size()); } }