线程状态 线程同步

2023.02.28 星期二

1. 线程状态

在这里插入图片描述

1.1 初始状态

一个刚被创建好的线程对象,就是初始状态的对象。

Thread t = new Thread(new Runnable02());

当前新创建的t线程对象就是初始状态。

1.2 可运行状态

一个线程想要运行必须先抢到CPU。启动与运行是不同的概念。

当一个线程对象调用了start()方法启动了,但还没有抢到CPU时,都是一个可运行状态。

可运行状态的线程对象只做一件事:抢CPU。

1.3 运行状态

一个抢到CPU正在执行的线程对象。称为运行状态。

当一个运行状态的线程对象,CPU时间到期要转换为可运行状态。

1.4 终止状态

一个执行完成run()方法的线程对象,就是终止状态。

2. 线程中的常用属性和方法
2.1 name属性

用来表示一个线程对象的名称。可以在创建线程对象时指定。也可以通过方法指定。

public class NameTest {
    public static void main(String[] args) {
        Thread t = new Thread(new Runnbale02(),"线程1");

        System.out.println(t.getName());
    }
}
2.2 currentThread()

Thread类的静态方法。动态获取当前执行的线程对象。
public static native Thread currentThread()

public class Runnbale02 implements Runnable{
    @Override
    public void run() {
        for(int i = 1;i<=10;i++) {
            String name = Thread.currentThread().getName();
            System.out.println(name+"-"+ i+":000");
        }
    }
}
2.3 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(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(name+"-"+ i+":000");
        }
    }
}
3. 阻塞状态

线程对象运行过程中,出现以下情况:
1 等待IO。做标准输入时,用户需要从控制台输入数据时。
2 sleep方法。当前线程休眠。
当线程遇到以上二种情况时,会进入阻塞状态。并会释放CPU。
在这里插入图片描述

4. 本地线程对象ThreadLocal类
4.1 本地线程对象是什么?

本地线程对象其实是一个容器。而且是一个只能保存一个数据的容器对象。

这个容器在每一个不同的线程中,都有一份自己的实例。

每一个线程对象都使用自己的ThreadLocal类的实例。

当出现多个线程时,每一个线程中都有一个自己使用的ThreadLocal对象。

4.2 常用方法
public static void main(String[] args) {
    ThreadLocal<String> local = new ThreadLocal<>();

    local.set("peter");

    String s = local.get();

    System.out.println(s);
}
5. 案例:取款与存款案例
1 编写二个任务

一个存款
一个取款

2 专家原则

专家原则:事情哪个对象最清楚,哪个对象负责。

3 编写账号的类
public class Acount {
    private Double money;
    public Acount(Double money) {
        this.money = money;
    }
    public Double getMoney() {
        return money;
    }
    public void setMoney(Double money) {
        this.money = money;
    }
}
4. 编写DAO
public class AcountDAO {
    public void update(Acount acount,Double money){
        acount.setMoney(money);
    }
}
5. 编写Service
public class AcountService {
    private AcountDAO acountDAO = new AcountDAO();

    public void deposit(Acount acount,Double money){
        double temp = acount.getMoney();
        temp = temp + money;
        acountDAO.update(acount,temp);
    }
    public void draw(Acount acount,Double money) throws Exception {
        double temp = acount.getMoney();
        if (temp > money) {
            temp = temp - money;
            acountDAO.update(acount, temp);
        }else {
            throw new Exception("账户余额不足!!!");
        }
    }
}
6. 编写任务类

任务类一定是与Service层对接。

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() {
        service.deposit(acount,money);
        System.out.println("存款成功!"+acount.getMoney());
    }
}



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() {
        try {
            service.draw(acount,money);
            System.out.println("取款成功!"+acount.getMoney());
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println(e.getMessage()+"取款失败!"+acount.getMoney());

        }
    }
}
7. 编写测试类
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);

    Thread t1 = new Thread(depositRunnable01,"自己");
    Thread t2 = new Thread(depositRunnable02,"父母");
    Thread t3 = new Thread(drawRunnable01,"女朋友");

    t1.start();
    t2.start();
    t3.start();
 }

会出现数据不一致的情况。
账户的余额会经常变化。不是一个正确的固定的数值。

6. 线程不安全

线程不安全是指多个线程在操作数据时,数据不正确的情况。

线程不安全的几个条件:
1 多个线程 - 多线程
2 同时操作 - 并发
3 同一个数据 - 临界资源
4 破坏了不可分割的操作时 - 原子性
当以上的情况发生时,就有可能造成数据的不一致。这种情况就称线程不安全。

当多线程并发操作临界资源时,破坏了其原子性操作时,就有可能造成数据不一致。

解决方案:线程同步

7. 线程同步

线程同步:多个线程以串行的方式执行。这种方式称为线程同步。
手拉手一起走,是异步。
串行的方式执行:是针对对临界资源的操作部分以串行的方式操作。

7.1 同步锁synchronized

我们使用同步锁,开保证临界资源的原子性操作不被分割。

在执行原子性操作之前,先获取同步锁对象。之后再执行原子性代码。只有原子性代码执行完成之后。我们才会释放同步锁对象。

特殊注意:只有使用同一个同步锁的多个线程,才会以串行的方式执行。

必须要保存,多个线程使用的是同一个锁对象。才能叫多个线程的同步。

一般情况下,临界资源做为锁对象更合适。

7.2 同步锁的使用方式一

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() {
        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() + "-取款结束!");
        }
    }
}

Test类:

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();
     }
}
7.3 同步锁的使用方式二

可以直接在方法上声明。

public synchronized void add();

等价于:

public class A{
public synchronized void add(){
}
}
//=========》
public class A{
public void add(){
	synchronized(this){
			
}
}
}

A a = new A();
a.add();
a.add();

A a1 = new A();
a1.add();

这个调用的add方法与a对象调用的add方法不同步。

对于Vector类来说:多个线程同时操作同一个Vector对象。调用其add方法时,是以同步方式保存对象。称为线程安全。

7.4 同步锁的使用方式三(JDK5)

在JDK1的线程时,锁对象类型是Object。表示任意类型都可以做为锁对象。

实际开发时,一般使用临界资源对象做为锁对象。

到了JDK5,就专门创建了一个锁对象。

java.util.concurrent.locks.Lock接口

public interface Lock {
	void lock();
	void unlock();
}

java.util.concurrent.locks.ReentrantLock 实现类

public class ReentrantLock implements Lock, java.io.Serializable{
}

创建一个锁对象的方式

Lock lock = new ReentrantLock();

修改方式一的线程任务类

public class DepositRunnable implements Runnable {
    private AcountService service = new AcountService();
    private Acount acount;
    private Double money;
    private Lock lock;//锁对象
    public DepositRunnable(Acount acount, Double money, Lock lock) {
        this.acount = acount;
        this.money = money;
        this.lock = lock;
    }
    @Override
    public void run() {
        lock.lock();//加锁
        System.out.println(Thread.currentThread().getName() + "-存款任务开始!");
        service.deposit(acount, money);
        System.out.println(Thread.currentThread().getName() + "-存款成功!" + acount.getMoney());
        System.out.println(Thread.currentThread().getName() + "-存款任务结束!");
        lock.unlock();//解锁
    }
}

public class Test {
    public static void main(String[] args) {
        Lock lock = new ReentrantLock();//实例化锁对象
        Acount acount = new Acount(10000.0);//临界资源

        DepositRunnable depositRunnable01 = new DepositRunnable(acount,10000.0,lock);
        DepositRunnable depositRunnable02 = new DepositRunnable(acount,2000.0,lock);

        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();
     }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值