自学内容网 自学内容网

并发编程---线程安全

一.三大特性

1.原子性(线程任务不可再分)

一个操作或者多个操作要么全部执行,要么全部不执行。这确保了数据的一致性,避免了部分执行导致的数据错误。

原子类(Atomic)

//  static int x = 1;
    static AtomicInteger x = new AtomicInteger(1);//原子类创建

    public static void main(String[] args) {
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    for (int i = 0;i < 100;i++){
                        System.out.println(x.addAndGet(1));
                        Thread.sleep(100);
                    }
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });

        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    for (int i = 0;i < 100;i++){
                        System.out.println(x.addAndGet(1));
                        Thread.sleep(100);
                    }
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });

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

2.可见性(线程之间的操作是互相不可见的)

volatile

当多个线程访问同一变量时,其中一个线程修改了这个变量,其他线程必须立刻知道这个变量被修改的值。这保证了数据的实时更新和一致性。

    static boolean flag = false;

    public static void main(String[] args) {
        //线程A如果flag为true,就运行打印语句A:true
        Thread t1 = new Thread(() -> {
            while (true) {
                if (flag){
                    System.out.println("A:" + flag);
                }
            }
        });
        
        //100ms之后将flag变为true
        Thread t2 = new Thread(() -> {
            try {
                Thread.sleep(100);
                flag = true;
                System.out.println("B:" + flag);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        });
          t1.start();
          t2.start();
    }

3.有序性(程序运行顺序不能改变)

在一个线程内观察,所有操作都是有序的;但从一个线程观察另一个线程,所有操作都是无序的。这确保了线程之间的操作不会相互干扰,保持了操作的独立性。

二.线程安全性解决方案-锁

1.锁的使用过程

  1.每一个对象只有一把锁

  2.每个线程抢锁,谁先抢到这个线程就是锁的持有者。

  3.持有锁的线程访问有synchronized 标记的方法/代码块

  4.离开synchronized,线程释放锁

2.模拟场景

火车票抢购

~有100张车票

~多个站点在卖票A,B,C

~100张票共享给A,B,C

~每卖出一张票,票--;

~当票==0,卖票终止。

3.Ticket

public class Ticket {

    private Integer num;
    private String name;

    public Ticket(int num) {
        this.num = num;
    }

    /**
     * 取出剩余的票数量
     * @return
     */
    public int getNum() {
        return num;
    }

    /**
     * 卖票
     * @param name
     */
//    public synchronized void sold(String name){
//        System.out.println("站点" + name + "售出一张票,还剩:" + (--num));
//    }
    public void sold(String name){
        //--num   num-1 --> num
        synchronized (num){
            System.out.println("站点" + name + "售出一张票,还剩:" + (--num));
        }
    }
}
public class StationThread extends Thread{

    private Ticket ticket;
    private String name;

    public StationThread(Ticket ticket,String name){
        this.ticket = ticket;
        this.name = name;
    }

    public void run() {
        while (ticket.getNum() > 0){
            try {
                sleep(100);
                ticket.sold(name);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }

}
public class TicketTest {
    public static void main(String[] args) {
        Ticket ticket = new Ticket(100);

        Thread a = new StationThread(ticket,"A");
        Thread b = new StationThread(ticket,"B");
        Thread c = new StationThread(ticket,"C");

        a.start();
        b.start();
        c.start();
    }
}

4.同步锁

  线程安全的同步锁是通过使用同步机制来确保多个线程在访问共享资源时的数据一致性和防止竞态条件,从而避免数据损坏或程序行为不可预测的问题。‌

  线程安全的同步锁的实现主要依赖于同步代码块和同步方法,以及更高级的同步工具如读写锁、信号量、条件变量和事件。这些机制和工具的共同目标是通过协调多个线程的执行,确保它们不会同时操作相同的数据或资源,从而保持数据的一致性和防止竞态条件。

public class Resource {
 
    private String name;
 
    public Resource(String name) {
        
        this.name = name;
    }
 
    @Override
    public String toString() {
        return "Resource [name=" + name + "]";
    }
}
public class DeadLockDemo extends Thread {
    
    private Resource a;
    private Resource b;
 
    public DeadLockDemo(Resource a, Resource b) {
      
        this.a = a;
        this.b = b;
    }
 
    @Override
    public void run() {
        System.out.println("准备锁定: " + a);
        synchronized (a) {
            try {
                sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("资源:" + a + "锁定成功....");
 
            System.out.println("准备锁定: " + b);
 
            synchronized (b) {
                System.out.println("资源:" + b + "锁定成功....");
            }
            System.out.println("释放资源:" + b + "成功...");
        }
        System.out.println("释放资源:" + a + "成功...");
    }
}
Resource r1 = new Resource("A");
Resource r2 = new Resource("B");
Resource r3 = new Resource("C");

Thread t1 = new DeadLockDemo(r1, r2);
Thread t2 = new DeadLockDemo(r2, r3);
Thread t3 = new DeadLockDemo(r3, r1);

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

5.死锁

  死锁‌是一种特殊的现象,发生在多个线程或进程因争夺资源而相互等待,导致它们都无法继续执行的情况。死锁的四个必要条件包括互斥条件、请求与保持条件、不剥夺条件和环路等待条件。这些条件共同作用,使得资源请求和释放的顺序不当,从而导致系统资源无法有效利用,进而产生死锁‌。

public class Resource {
    private String name;

    public Resource(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    @Override
    public String toString() {
        return "Resource{" +
                "name='" + name + '\'' +
                '}';
    }
}
public class DeadLockThread extends Thread{

    private Resource x;
    private Resource y;

    public DeadLockThread(Resource x,Resource y){
        this.x = x;
        this.y = y;
    }

    @Override
    public void run() {
        synchronized (x){
            System.out.println(x.getName() + "锁定" + x.getName() + "成功...");
            try {
                sleep(200);
                System.out.println(x.getName() + "准备--->锁定" + y.getName() + ">>>>>");
                synchronized (y){
                    System.out.println(x.getName() + "锁定" + y.getName() + "成功...");
                }
                System.out.println("释放" + y.getName() + "成功...");
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
        System.out.println("释放" + x.getName() + "成功...");
    }
}
public class Main {
    public static void main(String[] args) {
        Resource a = new Resource("A");
        Resource b = new Resource("B");
        Resource c = new Resource("C");

        Thread i = new DeadLockThread(a,b);
        Thread j = new DeadLockThread(b,c);
        Thread k = new DeadLockThread(c,a);

        i.start();
        j.start();
        k.start();
    }
}

运行结果:


原文地址:https://blog.csdn.net/wjj20040809/article/details/142432600

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!