是进程中的一个实体,是被系统独立调度和分配的基本单位
是线和被创建旦未启动的状态。
创建线程三种方式:
继承自Thread类继承Thread,重写方法run()
public class MyThread extends Thread{ public void run() {} } 实现Runnable接口推荐使用 实现run方法,符合里氏代换原则
public class MyThread implements Runnable { public void run() {} } 实现Callable接口前面两种,在任务执行完成后,无法直接获取执行结果,需要借助共享变量等来获取。 Callable 和 Future则很好地解决了这个问题
异常: call() 可以抛出异常。 Runnable只有通过setDefaultUncaughtExceptionHandler()的方式才能在主线程中捕捉到子线程异常
public class MyThread implements Callable<Integer> { @Override public Integer call() throws Exception { return 1; } }调用start()之后运行之前的状态。
线程的start()不能被多次调用,否则会抛出IllegalStateException异常
run()正在执行时线程的状态。
线程可能会由于某些因素而退出RUNNING,如时间、异常、锁、调度等。
有以下种情况,进入此状态
同步阻塞:锁被其他线程占用主动阻塞:调用Thread的某些方法,主动让出CPU执行权,比如sleep()、 join() 等。等待阻塞:执行了wait()。是 run () 执行结束,或同异常退出后的状态 , 此状态不可逆转。
线程安全问题只在多线程环境下才出现,单纯程串行执行不存在此问题。
保证高并发场景下的结程安全,可以从以下四个维度考量:
单线程总是安全的。通过限制数据仅在单线程内可见, 可以避免数据被其他结程篡改。最典型的就是线程局部变量,它存储在独立虚拟机栈帧的局部变量表中,与其他线程毫无瓜葛。
ThreadLocal 就是采用这种方式来实现线程安全的。
每个Thread实例的内部有一个ThreadLocal.ThreadLocalMap对象的实例threadLocals,用这个来保存线程数据
public void set(T value) { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) map.set(this, value); else createMap(t, value); }只读对象总是安全的。它的特性是允许复制、拒绝写人。 最典型的只读对象有 String 、 Integer 等。
一个对象想要拒绝任何写入,必须要满足以下条件:
使用 final 关键字修饰类, 避免被继承使用 private final 关键字避免属性被中途修改没有任何更新方法返回值不能可变对象为引用某些线程安全类的内部有非常明确的结程安全机制。
比如: StringBuffer 就是一个线程安全类,它采用 synchronized 关键字来修饰相关方法。
如果想要对某个对象进行并发更新操作, 但又不属于上述三类, 需要开发工程师在代码中实现安全的同步机制。
承接三
线程安全的核心理念:要么只读,要么加锁。
合理利用好JDK提供的并发包。
这些类使线程间的协调更加容易,支持了更加丰富的线程协调场景,逐步淘汰了使用 Object 的 wait()和 notify()进行同步的方式。
主要代表:CountDownLatch 、 Semaphore 、 CyclicBarrier 等。
集合并发操作的要求是执行速度快,提取数据准。
最著名的类非 ConcurrentHashMap 莫属, 它不断地优化,由刚开始的锁分段到后来的 CAS,不断地提升并发性能。
其他还有 ConcurrentSkipListMap 、 CopyOnWriteArrayList、BlockingQueue 等。
虽然 Thread 和 ThreadLocal 在 JDK1.O 就已经引入, 但是真正把 Thread 发扬光大的是线程池。根据实际场景的需要,提供了多种创建线程池的快捷方式,如使用 Exec utors 静态工厂或者使用 ThreadPoolExecutor 等。另外,通过 ScheduledExecutorService 来执行定时任务。
锁以 Lock 接口为核心, 派生出在一些实际场景中进行互斥操作的锁相关类。
最有名的是 ReentrantLock
锁的很多概念在弱化, 是因为锁的实现在各种场景中已经通过类库封装进去了
