- 线程状态
初始状态:
一个刚被创建好的线程对象,就是初始状态的对象。
Thread t = new Thread(new Runnable02());
当前新创建的 t 线程对象就是初始状态。
可运行状态:
一个线程想要运行必须先抢到 CPU。启动与运行是不同的概念。
当一个线程对象调用了 start() 方法启动了,但还没有抢到 CPU 时,就是一个可运行状态。
可运行状态的线程对象只做一件事:抢 CPU。
运行状态:
一个抢到 CPU 正在执行的线程对象。称为运行状态。
当一个运行状态的线程对象,CPU 时间到期要转换为 可运行状态。
终止状态:
一个执行完成 run() 方法的线程对象,就是终止状态。
- 线程中的常用属性和方法
name 属性:
用来表示一个线程对象的名称。可以在创建线程对象时指定。也可以通过方法指定。
public class NameTest {
public static void main(String[] args) {
Thread t = new Thread(new Runnbale02(),"线程1");
System.out.println(t.getName());
}
}
currentThread() :
Thread 类的静态方法,动态获取当前执行的线程对象。
public static native Thread currentThread()
public class Runnbale02 implements Runnable{
@Override
public void run() {
for(int i = 1;i<=10;i++) {
//通过Thread.currentThread()方法获取当前的线程对象
//通过.getName()来获取线程对象的名称
String name = Thread.currentThread().getName();
System.out.println(name+"-"+ i+":000");
}
}
}
sleep() 方法:
---------- 休眠方法。
哪一个线程运行过程中,遇到 sleep 方法。哪一个线程自己休眠。
public static native void sleep(long millis) throws InterruptedException
public class Runnbale02 implements Runnable{
@Override
public void run() {
for(int i = 1;i<=100;i++) {
String name = Thread.currentThread().getName();
try {
//通过Thread.sleep()方法进行休眠,并指定休眠时间;单位为毫秒
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(name+"-"+ i+":000");
}
}
}
- 阻塞状态
线程对象运行过程中,出现以下情况:
- 等待 IO。----做标准输入时,用户需要从控制台输入数据时。
- sleep方法。---- 当前线程休眠。
当线程遇到以上两种情况时,会进入阻塞状态,并会释放CPU。
- 本地线程对象:ThreadLocal 类
本地线程对象其实是一个容器,而且是一个只能保存一个数据的容器对象。
这个容器在每一个不同的线程中,都有一份自己的实例。
每一个线程对象都使用自己的 ThreadLocal 类的实例。
当出现多个线程时,每一个线程中都有一个自己使用的 ThreadLocal 对象。
常用方法:
public static void main(String[] args) {
ThreadLocal<String> local = new ThreadLocal<>();
//使用get、set方法来设置和取
local.set("peter");
//因为只存放一个数据,所以get方法没有参数
String s = local.get();
System.out.println(s);
}
- 线程不安全
线程不安全是指:多个线程在操作数据时,数据不正确的情况。
线程不安全的几个条件:
- 多个线程 ----- 多线程
- 同时操作 ----- 并发
- 同一个数据 ----- 临界资源
- 破坏了 不可分割的操作时 ----- 原子性
当以上的情况发生时,就有可能造成数据的不一致。这种情况就称线程不安全。
当多线程并发操作临界资源时,破坏了其原子性操作时,就可能造成数据不一致的问题。
解决方法:线程同步。
- 线程同步
线程同步:多个线程以串行的方式执行。
而,一起并发执行的方式则为:异步。
串行的方式执行:是针对对临界资源的操作部分以串行的方式操作。
同步锁 synchronized:
使用同步锁,来保证临界资源的原子性操作不被分割。
在执行原子性操作之前,先获取同步锁对象。之后再执行原子性代码。只有原子性代码执行完成之后。我们才会释放同步锁对象。
特殊注意:只有使用同一个同步锁的多个线程,才会以串行的方式执行。
必须要保证,多个线程使用的是同一个锁对象。才能叫多个线程的同步。
PS:一般情况下,临界资源做为锁对象更合适。
同步锁的使用方式:
synchronized(锁对象){
//原子性操作代码;
}
存款任务类:
public class DepositRunnable implements Runnable{
//任务类需和业务层对接
private AcountService service = new AcountService();
private Acount acount;
private Double money;
public DepositRunnable(Acount acount, Double money) {
this.acount = acount;
this.money = money;
}
@Override
public void run() {
//此处使用acount作为锁对象
synchronized (acount) {
System.out.println(Thread.currentThread().getName() + "-存款任务开始!");
service.deposit(acount, money);
System.out.println(Thread.currentThread().getName() + "-存款成功!" + acount.getMoney());
System.out.println(Thread.currentThread().getName() + "-存款任务结束!");
}
}
}
取款任务类:
public class DrawRunnable implements Runnable {
private AcountService service = new AcountService();
private Acount acount;
private Double money;
public DrawRunnable(Acount acount, Double money) {
this.acount = acount;
this.money = money;
}
@Override
public void run() {
//使用同步锁:
synchronized (acount) {
System.out.println(Thread.currentThread().getName() + "-取款开始!");
try {
service.draw(acount, money);
System.out.println(Thread.currentThread().getName() + "-取款成功!" + acount.getMoney());
} catch (Exception e) {
e.printStackTrace();
System.out.println(Thread.currentThread().getName() + "-" + e.getMessage() + "取款失败!" + acount.getMoney());
}
System.out.println(Thread.currentThread().getName() + "-取款结束!");
}
}
}
测试类中:
public class Test {
public static void main(String[] args) {
Acount acount = new Acount(10000.0);//临界资源
//创建任务对象:
DepositRunnable depositRunnable01 = new DepositRunnable(acount,10000.0);
DepositRunnable depositRunnable02 = new DepositRunnable(acount,2000.0);
DrawRunnable drawRunnable01 = new DrawRunnable(acount,5000.0);
DrawRunnable drawRunnable02 = new DrawRunnable(acount,5000.0);
//创建线程对象并传入参数:任务类和名称
Thread t1 = new Thread(depositRunnable01,"自己");
Thread t2 = new Thread(depositRunnable02,"父母");
Thread t3 = new Thread(drawRunnable01,"女儿");
Thread t4 = new Thread(drawRunnable02,"老婆");
//线程启动:
t1.start();
t2.start();
t3.start();
t4.start();
}
}