线程安全与线程通信

线程状态

创建:当new出一个线程对象时即为创建线程

就绪:线程创建完成后进入就绪状态

运行:线程获得CPU资源后进入运行状态,释放CPU资源后进入就绪状态

阻塞:运行中线程可以被sleep方法等阻塞,进入阻塞状态,阻塞解除后进入就绪状态

死亡:线程执行完毕后或外部干涉终止线后线程被杀死

程序休眠sleep:使线程阻塞若干毫秒,使用时需要捕获异常

  • 形式:Thread.sleep(毫秒数);

程序礼让yield:使线程回到就绪状态,CPU重新调用,重新调用时不一定会调用其他线程,也就是不一定礼让成功

  • 形式:Thread.yield();

线程强制执行join:让一个线程强制执行,正在执行的线程进入阻塞

  • 形式:thread.join();

观测线程状态State:其方法返回值为线程状态,可以使用一个变量储存其返回值

  • 形式:Thread.State state = thread.getState();

线程优先级:线程存在优先级,优先级越高CPU给予的资源越多,调用的概率越高

  • 形式:获取优先级:getPriority()

    ​ 设置优先级:setPriority(int )

守护线程:线程分为用户线程与守护线程,守护线程会与用户线程一起执行一起结束

  • 形式:将线程设置为守护线程:thread.setDaemon(true)

线程安全

线程不安全:

当多个线程操作同一对象时,会出现多个线程获取同一资源的情况,导致线程不安全

例如下面的一段代码:

public class Threadsafe {
    public static void main(String[] args) {
        Buyticket ticket = new Buyticket();
        new Thread(ticket, "people").start();        //买票的人
        new Thread(ticket, "people2").start();
        new Thread(ticket, "people3").start();
    }
}
class  Buyticket implements Runnable{
    int ticketnumber = 10;      //票数
    @Override
    public  void run() {
        while (true) {
            if (ticketnumber <= 0) {        //票数为0则停止买票
                return;
            }
            try {
                Thread.sleep(100);     //设置睡眠使不安全效果增强
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "抢到了第" + ticketnumber-- + "张票");   //输出买票的人与买到第几张票,买一张少一张
        }
    }
}

执行后会发现会有不同的人买到同一张票,线程不安全

线程安全:

  • 使用同步方法及同步块对资源上锁:synchronized关键字,使线程安全

    public class Threadsafe {
        public static void main(String[] args) {
            Buyticket ticket = new Buyticket();
            new Thread(ticket, "people").start();        //买票的人
            new Thread(ticket, "people2").start();
            new Thread(ticket, "people3").start();
        }
    }
    class  Buyticket implements Runnable {
        private int ticketnumber = 10;     //票数
        boolean flag = true;
    
        @Override
        public void run() {
            while (flag) {
                synchronized (this) {		//同步块
                    if (ticketnumber <= 0) {    //票数为0则停止买票
                        return;
                    }
                    try {
                        Thread.sleep(100);     //设置睡眠使不安全效果增强
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + "抢到了第" + ticketnumber-- + "张票");   //输出买票的人与买到第几张票,买一张少一张
                }
            }
            }
        }
    
    • 同步方法与同步块会给方法或包裹的代码上锁,使一个资源只能被一个线程操
    • synchronized是隐式锁,出了作用域自动释放
  • Lock锁:

    import java.util.concurrent.locks.ReentrantLock;
    
    public class Threadsafe {
        public static void main(String[] args) {
            Buyticket ticket = new Buyticket();
            new Thread(ticket, "people").start();        //买票的人
            new Thread(ticket, "people2").start();
            new Thread(ticket, "people3").start();
        }
    }
    class  Buyticket implements Runnable {
        private int ticketnumber = 10;     //票数
        boolean flag = true;
        ReentrantLock lock = new ReentrantLock();	//创建lock对象
        @Override
        public void run() {
            while (flag) {
                    try {		//可以使用try,catch捕获异常
                        lock.lock();		//给资源上锁
                        if (ticketnumber <= 0) {    //票数为0则停止买票
                            return;
                        }
                        try {
                            Thread.sleep(100);     //设置睡眠使不安全效果增强
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        System.out.println(Thread.currentThread().getName() + "抢到了第" + ticketnumber-- + "张票");   //输出买票的人与买到第几张票,买一张少一张
                    }finally {
                        lock.unlock();		//最后使用finally释放锁
                    }
            }
        }
    }
    
    • Lock锁只有代码块锁
    • Lock是显示锁需要手动开启与关闭
    • Lock花费更少时间调度,性能更好,并且有良好的扩展性

死锁

定义:当多个线程相互申请对方的资源,又不释放自己的资源时就会形成死循环,称为死锁

产生死锁的条件:

  • 互斥条件:一个资源每次只能被一个进程使用
  • 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放
  • 不剥夺条件:进程已获得的资源,在未使用完之前,不能强行剥夺
  • 循环等待条件:若干进程之间形成一种头尾相连的循环等待资源关系

只要解决其中一个或多个条件就能避免死锁的发生

线程通信

定义:线程之间的交互称为线程通信

方法:java中提供了几个方法解决线程间通信问题

  • wait():表示线程等待直到其他线程通知,wait()会释放锁
  • wait(long timeout):指定等待的毫秒数
  • notify():唤醒一个等待状态的线程
  • notifyAll():唤醒同一个对象上所有调用wait()方法的对象

生产者消费者问题:生产者生产资源,由消费者来消费,协调两个线程之间调度问题

  • 管程法:通过建立缓冲区来存放生产者生产的资源来达到协调调度问题

    import java.util.Arrays;
    public class Threadcom {
        public static void main(String[] args) {
            SynContainer container = new SynContainer();
            new producter(container).start();
            new consumer(container).start();
        }
    }
    //生产者
    class producter extends Thread{
        SynContainer container;
        public producter(SynContainer container){
            this.container = container;
        }
        //生产
        @Override
        public void run() {
            for (int i = 1; i < 100; i++) {
                container.push(new produce(i));
                System.out.println("生产了第"+i+"号产品");
            }
        }
    }
    //消费者
    class consumer extends Thread{
        SynContainer container;
        public consumer(SynContainer container){
            this.container = container;
        }
        //消费
        @Override
        public void run() {
            for (int i = 1; i < 100; i++) {
                System.out.println("消费了第"+container.pop().id+"号产品");
            }
        }
    }
    //产品
    class produce{
        int id;
        public produce(int id){
            this.id = id;
        }
    }
    //缓冲区
    class SynContainer{
        //产品容器
        produce produces[] = new produce[10];
        //容器计数
        int count = 0;
        //生产者放入产品
        public synchronized void push(produce produce){
            //如果容器满了
            if (count == produces.length-1){
                //生产者等待
                try {
                    this.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            //如果没满,生产者放入产品
            produces[count] = produce;
            count++;
            //通知消费者消费
            this.notifyAll();
        }
        //消费者消费产品
        public synchronized produce pop(){
            //如果没有产品
            if (count == 0){
                //消费者等待
                try {
                    this.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            //如果可以消费
            count--;
            produce produce = produces[count];
            //通知生产者生产
            this.notifyAll();
            return produce;
        }
    }
    
  • 信号灯法:设置一个flag作为信号灯,根据flag的状态调整线程状态

    public class Threadcom2 {
        public static void main(String[] args) {
            TV tv = new TV();
            new preformer(tv).start();
            new watcher(tv).start();
        }
    }
    //生产者 --->演员
    class preformer extends Thread{
        TV tv;
        public preformer(TV tv){
            this.tv = tv;
        }
        @Override
        public void run() {
            for (int i = 0; i < 20; i++) {
                if (i%2 == 0) {
                    tv.preform("CCTV6");
                }else {
                    tv.preform("CCTV10");
                }
            }
        }
    }
    //消费者 --->观众
    class watcher extends Thread{
        TV tv;
        public watcher(TV tv){
            this.tv = tv;
        }
        @Override
        public void run() {
            for (int i = 0; i < 20; i++) {
                tv.watch();
            }
        }
    }
    //产品 --->电视节目
    class TV{
        //表演节目
        String voice;
        //信号灯
        boolean flag = false;
        //表演
        public synchronized void preform(String voice){
            //有节目时,演员休息
            if (flag){
                try {
                    this.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            //没有节目时,表演,提醒观众观看
            System.out.println("表演了"+voice);
            this.notifyAll();
            this.voice = voice;
            this.flag = !this.flag;
        }
        //观看
        public synchronized void watch(){
            //没有节目时,观众休息
            if (!flag){
                try {
                    this.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            //有节目时看节目
            System.out.println("观看了"+voice);
            this.notifyAll();
            this.flag = !this.flag;
        }
    }
    
posted @ 2022-05-06 22:53  谦谦子  阅读(53)  评论(0)    收藏  举报