线程的名字(setName/getName)
线程的分类
java中,线程可以分为:
前台线程,又叫做执行线程、用户线程
后台线程,又叫做守护线程、精灵线程
在主线程中,创建出来的线程对象,默认就是前台线程,在它启动之前,我们还可以给它设置为后台线程
//在启动线程之前,可以将其设置为后台线程,否则默认是前台线程
t.setDaemon(true);
线程优先级
线程类Thread中,有一个属性,表示线程的优先级 private int priority;
线程的优先级使用int类型数字表示,最大是10,最小是1,默认的优先级是5。
线程组
Java中使用 java.lang.ThreadGroup 类来表示线程组,它可以对一批线程进行管理,对线程组进行操作,同时也会对线程组里面的这一批线程操作。
创建线程组的时候,需要指定该线程组的名字。
也可以指定其父线程组,如果没有指定,那么这个新创建的线程组的父线程组就是当前线程组。
main线程默认属于main线程组
线程状态
获取状态 getState();
阻塞
sleep();谁调用sleep谁就阻塞
join();谁调用谁执行,调用x.join();代码的线程阻塞
打断阻塞
如果指定了时间,线程阻塞一定的时间后,会自动恢复到RUNNABLE状态,这种情况下,线程的状态为TIMED_WAITING(有限期等待)
如果没有指定时间,线程会一直阻塞着,直到某个条件满足时,才会自动恢复,这种情况下,线程的状态为WAITING(无限期等待)
interrupt方法可以打断线程阻塞
查看线程对象中“打断标识”值的俩个方法:
线程类Thread中的 isInterrupted 方法:
注意,这个非静态方法,只是返回这个“打断标识”值,并且不会对这个值进行清除(true- >false),因为所传参数ClearInterrupted的值为false
线程类Thread中的 interrupted 方法:
注意,这个静态方法,返回这个“打断标识”值,并且会对这个值进行清除(true->false),因为所 传参数ClearInterrupted的值为true
interrupt()、isInterrupted()、interrupted() 的结构关系大致如下
三者区别
interrupt()方法
其作用是中断此线程(此线程不一定是当前线程,而是指调用该方法的Thread实例所代表的线程),但实际上只是给线程设置一个中断标志,线程仍会继续运行。
interrupted()方法
作用是测试当前线程是否被中断(检查中断标志),返回一个boolean并清除中断状态,第二次再调用时中断状态已经被清除,将返回一个false。
isInterrupted()方法
作用是只测试此线程是否被中断 ,不清除中断状态。
线程安全
JVM内存中的堆区,是一个共享的区域,是所有线程都可以访问的内存空间。
JVM内存中的栈区,是线程的私有空间,每个线程都有自己的栈区,别的先无法访问到自己栈区 的数据。
多线程环境中,如果有俩个线程并发访问堆区中一个对象中的数据,那么这个数据可能会出现和预期结果不符的情况
java里面线程跟进程的关系
main线程与main方法的关系,main线程执行在jvm里的过程以及结束
当JVM启动的时候,会启动一个名为“Main”的线程。程序就会在这个线程上运行,除非用户自己创建了其他线程。
Main线程首先就会寻找"static void main(String[] args)"方法,并且调用这个方法。这个就是程序的进入点。
如果我们希望程序可以并发,那么我们可以创建多线程,并且给予每个线程一些操作。接下来这些线程就会并发的执行这些操作。JVM同时也会创建一些其他的内部线程在“幕后”工作(比如垃圾回收)。
线程两种创建方式(继承Thread类以及实现Runable接口)
创建线程为了提高效率
拿线程名字,取名等等。。。。api
前台线程
后台线程
线程优先级 1-10 默认5
线程的状态
内部枚举出的情况,用
new(创建完线程对象)——》Runable(就绪状态)start()——》Running(Runable) 抢到cpu——》terminated(终止状态)
还可能——》Blocked/Time_waitting(join/sleep(long))/waitting(join)阻塞状态——》time/interrupt被打断——》Running(Runable) 抢到cpu——》terminated(终止状态)
线程同步解决线程不安全的方法
一种广泛方便的方法,其他以后再了解
用 synchronized加锁
synchronized (锁对象){
//操作共享变量的代码,这些代码需要线程同步,否则会有线程安全问题
//…
}
public class Test {
public static void main(String[] args) {
MyData myData = new MyData();
Thread t1 = new Thread("t1"){
@Override
public void run() {
String name = Thread.currentThread().getName();
synchronized (myData){
for (int i = 0; i < 10; i++) {
myData.num = i;
System.out.println(name + ": " + myData.num);
}
}
}
};
Thread t2 = new Thread("t2"){
@Override
public void run() {
synchronized (myData){
for (int i = 100; i < 20000; i++) {
myData.num = i;
}
}
}
};
t1.start();
t2.start();
}
}
class MyData{
int num;
}
还可以使用 synchronized 直接修饰一个方法,表示这个方法中的所有代码都需要线 程同步。
synchronized 关键字修饰非静态方法,默认使用 this 当做锁对象,并且不能自己另外指定
synchronized 关键字修饰静态方法,默认使用 当前类的Class对象 当做锁对象,并且不能自己另外指定
Object类中有三个方法: wait()、notify()、notifyAll
当一个对象,在线程同步的代码中,充当锁对象的时候,在 synchronized 同步的代码块中,就可以调用这个锁对象的这三个方法了
三个核心点:
任何对象中都一定有这三个方法
只有对象作为锁对象的时候,才可以调用
只有在同步的代码块中,才可以调用
synchronized 关键字,虽然可以达到线程同步的效果,但是太“霸道”了,只要一个线程拿到了锁对象,那么这个线程无论是在运行状态,还是时间片用完,回到就绪状态,还是sleep休眠,这个线程都是死死的拿着这个锁对象不释放,只有这个线程把线程同步的代码执行完,才会释放锁对象让别的线程使用。
wait 方法可以让拿到的锁的线程,即使代码没执行完,也可以把锁立即给释放
TIMED_WAITING、WAITING、BLOCKED都属于线程阻塞,他们共同的特点是就是线程不执行代码,也不参与CPU的争夺,除此之外,它们还有各自的特点:(重要)
阻塞1,线程运行时,调用sleep或者join方法后,进入这种阻塞,该阻塞状态可以恢复到RUNNABLE状态,条件是线程被打断了、或者指定的时间到了,或者join的线程结束了
阻塞2,线程运行时,发现锁不可用后,进入这种阻塞,该阻塞状态可以恢复到RUNNABLE状态,条件是线程需要争夺的锁对象变为可用了(别的线程把锁释放了)
阻塞3,线程运行时,调用了wait方法后,线程先释放锁后,再进入这种阻塞,该阻塞状态可以恢复到BLOCKED状态(也就是阻塞2的情况),条件是线程被打断了、或者是被别的线程唤醒了(notify方法)
线程调用无参的wait方法,会释放锁并进入等待池,且此时的状态为WAITING,如果线程调用有 参的wait方法,指定一个等待时间,那么线程释放锁后,进入到等待池
当你调用notify时,只有一个等待线程会被唤醒而且它不能保证哪个线程会被唤醒,这取决于线程调度器。
如果你调用notifyAll方法,那么等待该锁的所有线程都会被唤醒,但是在执行剩余的代码之前,所有被唤醒的线程都将争夺锁定
线程池
java.util.concurrent
线程锁
java.util.concurrentLock
原子性
java.util.concurrent.atomic