设置线程名
线程的任务
设置线程分组
ID,Name…状态
优先级(1-10 默认5)
守护线程(后台线程)
1.在守护线程里面创建的线程,默认是守护线程
2.守护线程状态的设置必须放在线程启动之前
run属于普通方法,start是启动线程的方法
run方法可以执行多次,start方法只能执行一次
1.使用全局自定义的变量来终止线程-------->收到终止指令后,会执行完当前任务再终止
2.使用线程提供的终止方法 interrupt 来终止线程---->收到终止指令之后,立马终止
3.使用线程提供的方法 stop 来终止线程(弃用,终止之后,未释放资源,有安全隐患)
Thread.interrupted()方法,判断完线程状态之后,就会将线程状态设置为false;
Thread.currentThread.isInterrupted()方法,执行判断线程终止为true之后,
join()
currentThread()
线程状态
NEW------------------->新建状态,没有调用start()之前的状态
RUNNABLE---------->运行状态,(running执行中,ready就绪(等待cpu时间片))
BLOCKED------------>阻塞状态,多个线程试图获得同一把锁
WAITING-------------->等待状态,没有明确的等待结束时间,调用wait()
TIMED_WAITING—>超时等待状态,有明确的等待时间,如:sleep(xxx)
TERMINATED-------->终止状态
出让CPU执行权,特点:不一定能成功出让cpu执行权
线程不安全:多线程执行中,程序的执行结果和预期的不相符,就叫做线程不安全
1.非原子性
编译器优化(代码优化)编译器优化在单线程下没问题,可以提升程序的执行效率,但是在多线程下就会出现混乱,导致线程不安全问题(指令重排序)
2.内存不可见
多个线程修改了同一个变量
volatile 关键字(轻量级解决“线程安全”)
解决:
1.强制内存可见(原理:当操作完变量之后,强制删除线程工作内存中的此变量)
2.禁止指令重排序
注意:
volatile不能解决原子性问题
1.CPU抢占调度(不能解决)
2.每个线程操作自己的变量(可能解决)
3.在关键代码上让所有CPU排队执行,加锁;
4.加锁操作的关键步骤
尝试获取 (如果成功拿到锁,加锁,没拿到就等待)
释放锁
1.synchronized 加锁和释放锁(JVM层面的解决方案,自动进行加锁释放锁)
2.Lock 手动锁【程序员自己加锁,释放锁】
在进行加锁操作的时候,同一组业务一定是同一个锁对象;
从操作来看:互斥锁实现 mutex lock
JVM:monitorenter–>monitorexit实现了监视器的加锁和释放锁的操作
Java:
a)锁对象:mutex
b)锁存放的地方:加锁对象的头信息
Lock
注意:一定要把lock()放在try外面
1.如果将lock()方法放在try里面,那么try里面的代码出现异常之后,就会执行finally里的释放锁代码,但还没加锁成功
2.如果将lock()方法放在try里面,那么执行finally的释放锁代码时报的异常就会覆盖try里面的异常
synchronized锁机制是非公平锁
公平锁可以按顺序进行执行,而非公平锁执行的效率更高,是抢占式的
在Java中所有锁默认的策略都是非公平锁
不释放锁会造成死锁
修饰代码块(加锁对象自定义)
修饰静态方法(加锁对象是当前类对象)
修饰普通方法(加锁对象是当前类的实例)
Lock只能修饰代码块
volatile可以解决内存可见性问题和禁止指令重排序,但volatile不能解决原子性问题
synchronized是用来保障线程安全的,也就是说synchronized可以解决任何关于线程安全的问题(5个:抢占式执行,原子性,编译器优化,内存不可见,多个线程修改同一变量)(关键代码排队执行,始终只有一个线程会执行加锁操作)
synchronized既可以修饰代码块,和静态方法或普通方法,lock只能修饰代码块
synchronized只有非公平锁策略,而lock既可以是公平锁,也可以是非公平锁(ReentrantLock默认是非公平锁,也可以通过构造函数声明它为公平锁)
ReentrantLock更加的灵活(如:tryLock)
synchronized是自动加锁释放锁,而RenntrantLock需手动加锁释放锁
Java中的线程是通过Java.lang.Thread实现的,可以通过实例化Thread一个对象来创建一个线程,然后调用start()启动,也可以通过Thread的一个特定的方法run()启动线程。区别如下:
实例化+start():实例化一个thread对象,然后调用start()启动,此时线程处于运行状态中(ready就绪),等待CPU调度,然后再执行线程里面的run()方法,启动结束。不用等待我们的run方法执行完成就可以继续执行下面的代码。
**直接使用run()方法:**直接调用run()方法,启动结束
run方法是thread里面的一个普通的方法,如果直接调用run()方法,此时它会运行在主线程中。由于只有一个主线程,所以如果有两个线程都是直接调用run()方法,那么他们的执行顺序一定是按顺序执行的,并没有实现多线程的目的。
**对比:**start()方法来启动线程,真正实现了多线程,run()方法当作普通方法的方式调用
线程状态
在Java当中,线程通常有五种状态:创建、运行、阻塞、等待、终止
NEW------------------->新建状态,没有调用start()之前的状态
RUNNABLE---------->运行状态,(running执行中,ready就绪(等待cpu时间片))
BLOCKED------------>阻塞状态,
WAITING-------------->等待状态,没有明确的等待结束时间,调用wait()
TIMED_WAITING—>超时等待状态,有明确的等待时间,如:sleep(xxx)
TERMINATED-------->终止状态
代码对比
public class ThreadDemo5 {
public static final boolean flag=false;
public static void main(String[] args) {
Thread t1=new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
});
t1.start();
}
}
调用start()方法,线程名是默认的
public class ThreadDemo5 {
public static final boolean flag=false;
public static void main(String[] args) {
Thread t1=new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
});
t1.run();
}
}
调用run(),线程名是main
没有用start()方法而是用了run()方法
这里方法run()称为线程体,它包含了要执行的这个线程的内容, Run方法运行结束, 此线程终止。然后CPU再调度其它线程。如果调用start(),线程执行的结果可能是乱序的
/**
* 三个线程
* 打印十行ABC
*/
public class ThreadDemo1 {public static void main(String[] args) throws InterruptedException {
show();
}
private static void show() throws InterruptedException {
for(int i=0;i<10;i++) {
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
System.out.print("A");
}
});
t1.run();
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
System.out.print("B");
}
});
t2.run();
Thread t3 = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("C");
}
});
t3.run();
}
}
}
1.使用全局自定义的变量来终止线程,定义全局变量来控制线程终止会执行完当前手头的任务之后再终止。
2.使用线程提供的终止方法interrupt来终止进程,在收到终止指令之后会立马结束执行。
3.使用线程提供的方法 stop 来终止线程(弃用,终止之后,未释放资源,有安全隐患)