1.初始线程:线程的基本操作
1.新建线程
2.终止线程
- stop
造成数据不一致
3.线程中断
1public void Thread.interrupt() // 中断线程 2public boolean Thread.isTnterrupted() // 判断是否被中断 3public static boolean Thread.interrupted() // 判断是否被中断,并清除当前中断状态
4.等待(wait)和通知(notify)
1public final void wait() throws InterruptedException 2public final native void notify() 3// 4public static native void sleep(long millis) throws InterruptedException
Object.wait()和Thread.sleep()方法都可以让线程等待若干时间。除了wait()可以被唤醒外,另外一个主要区别就是wait()方法会释放目标对象的锁,而Thread.sleep()方法不会释放任何资源。
Object.wait()方法并不是可以随便调用的。它必须包含在对应的synchronzied语句中,无论是wait()或者notify()都需要首先获得目标对象的一个监视器。
5.挂起(suspend)和继续执行(resume)线程
suspend() 在导致线程暂停的同时,并不会取释放任何锁资源。
6.等待线程结束(join)和谦让(yield)
join
1public final void join() throws InterruptedException 2public final synchronized void join(long millis) throws InterruptedException
join:一个线程的输入可能非常依赖另外一个或者多个线程的输出,此时这个线程就需要等待依赖线程执行完毕,才能继续执行。
- join():无限等待,它会一致阻塞当前线程,直到目标线程执行完毕。
- join(long millis):给出一个最大等待时间,如果超过给定时间目标线程还在执行,当前线程也会因为“等不及了”,而继续往下执行。
join() 的本质是让调用线程 ==wait()== 在当前线程对象实例上
1//核心代码 2while (isAlive()) { 3 wait(0); 4}它让调用线程在当前线程对象上进行等待。当线程执行完成后,被等待的线程会在退出前调用notifyAll() 通知所有的等待线程继续执行。因此,值得注意的一点是:不要在应用程序中,在Thread对象实例上使用类似wait()或者notify()等方法,因此这很有可能会影响系统API的工作,或者被系统API所影响
yield
public static native void yield();
2. volatile与Java内存模型(JMM)
volatile:使用volatile申明一个变量时,就等于告诉虚拟机,这个变量极有可能会被某些程序或者线程修改。为了确保这个变量被修改后,应用程序范围内的所有线程都能够“看到”这个改动,虚拟机就必须采用一些特殊的手段,保证这个变量的可见性等特点。
注意:volatile并不能代替锁🔒,它也无法保证一些符合操作的原子性。 例如:👇
1public class VolatileTest { 2 3 volatile static int i = 0; 4 5 public static class PlusTask implements Runnable{ 6 public void run(){ 7 for(int k = 0;k<10000;k++){ 8 i++; 9 } 10 } 11 } 12 13 public static void main(String[] args) throws InterruptedException { 14 Thread[] threads = new Thread[10]; 15 for (int i = 0;i<10;i++){ 16 threads[i] = new Thread(new PlusTask()); 17 threads[i].start(); 18 } 19 20 for (int i = 0;i<10;i++){ 21 threads[i].join(); 22 } 23 System.out.println(i); 24 } 25}
volatile 也能保证数据的可见性和有序性
3.分门别类的管理:线程组
1// 创建线程组 2ThreadGroup tg = new ThreadGroup("PrintGroup"); 3// 使用Thread构造方法指定线程所属的县城组 4Thread t = new Thread(tg,new ThreadTest(),"T1");
stop:慎用,它会停止线程组中所有的线程。和Thread.stop()一样。
4.驻守后台:守护线程(Daemon)
比如:垃圾回收线程、JIT线程
与之对应的是用户线程,当一个Java内,只有守护线程时,Java虚拟机就会自然退出。
1public class DaemonDemo { 2 public static class DaemonT extends Thread{ 3 public void run(){ 4 while(true){ 5 System.out.println("I'm alive"); 6 try { 7 Thread.sleep(5000); 8 } catch (InterruptedException e) { 9 e.printStackTrace(); 10 } 11 } 12 } 13 } 14 15 public static void main(String[] args) throws InterruptedException { 16 Thread t = new DaemonT(); 17 t.start(); 18 // 将t设置为守护线程 19 t.setDaemon(true); 20 Thread.sleep(3000); 21 22 } 23}
5.先干重要的事:线程优先级
使用1-10表示线程优先级。一般使用内置的三个静态标量表示:
数字越大则优先级越高。
1/** 2 * The minimum priority that a thread can have. 3 */ 4public final static int MIN_PRIORITY = 1; 5 6/** 7 * The default priority that is assigned to a thread. 8 */ 9public final static int NORM_PRIORITY = 5; 10 11/** 12 * The maximum priority that a thread can have. 13 */ 14public final static int MAX_PRIORITY = 10;
6.线程安全的概念与synchronized
synchronized:是实现线程间的同步。它的工作是对同步的代码加锁,使得每一次,只能有一个线程进入同步块,从而保证线程间的安全性。
用法:
- 指定加锁对象:对给定对象加锁,进入同步代码前要获得给定对象的锁。
- 直接作用于实例方法:相当于对当前实例加锁,进入同步前要获得当前实例的锁
- 直接作用于静态方法:相当于对当前类加锁,进入同步代码前要获得当前类的锁。
7.程序中的幽灵:隐蔽的错误
ArrayList 线程不安全 HashMap 线程不安全. ConcurrentHashMap代替
8.初学者常见问题:错误的加锁
参考文献: 《Java高并发设计》