线程安全,与多线程的应用

一、线程安全

1.什么是线程安全

线程安全,与多线程的应用_第1张图片

线程安全,与多线程的应用_第2张图片

线程安全,与多线程的应用_第3张图片

2.用程序模拟线程安全问题

线程安全,与多线程的应用_第4张图片

public class Account {
    private double money;//余额
    private String cardId;//卡号

    public Account() {
    }

    public Account(double money, String cardId) {
        this.money = money;
        this.cardId = cardId;
    }
    public void drawMoney(double money){
        //先搞清楚是谁来取钱
        String name = Thread.currentThread ().getName ();
        //1.判断账户金额是否足够
        if (this.money>=money){
            System.out.println (name+"开始取钱");
            this.money-=money;
            System.out.println (name+"取钱成功后余额为"+this.money);
        }else {
            System.out.println ("未取钱余额不足");
        }
    }

    public double getMoney() {
        return money;
    }

    public void setMoney(double money) {
        this.money = money;
    }

    public String getCardId() {
        return cardId;
    }

    public void setCardId(String cardId) {
        this.cardId = cardId;
    }
}




public class MyThread extends Thread{
    private  Account acc;
    public MyThread(Account acc,String name){
        super ( name );
        this.acc=acc;
    }



    @Override
    public void run() {
//        if (acc.getMoney ()>=100000.0){
//            System.out.println (Thread.currentThread ().getName ()+"开始取钱");
//            acc.setMoney (acc.getMoney ()-100000.0);
//            System.out.println (Thread.currentThread ().getName ()+"取钱成功");
//        System.out.println ( Thread.currentThread ().getName ()+"取钱后账户余额"+acc.getMoney () );
//        }else {
//            System.out.println ("账户余额不足");
//        }

        acc.drawMoney ( 100000.0 );
    }

}



public class ThreadTest {
    public static void main(String[] args) {
        //1.创建一个账户类对象代表两个人的共享账户
        Account acc = new Account ( 100000.0, "787878787877" );
        //2.创建两个线程,分别表示小明,小红,两人同时去一个账户中取钱10w
        Thread m1 = new MyThread ( acc,"小明" );
        Thread m2 = new MyThread ( acc,"小红" );
        m1.start ();
        m2.start ();

    }
}

线程安全,与多线程的应用_第5张图片

 3.如何解决线程安全问题?

1.线程同步

线程安全,与多线程的应用_第6张图片

解决方案:加锁

线程安全,与多线程的应用_第7张图片

2.如何实现线程同步(加锁)

1.同步代码块

线程安全,与多线程的应用_第8张图片

线程安全,与多线程的应用_第9张图片

public class Account {
    private double money;//余额
    private String cardId;//卡号

    public Account() {
    }

    public Account(double money, String cardId) {
        this.money = money;
        this.cardId = cardId;
    }
    public void drawMoney(double money){
        //先搞清楚是谁来取钱
        String name = Thread.currentThread ().getName ();
        //1.判断账户金额是否足够
        synchronized (this) {//同步代码块上锁  推荐用共享资源作为锁(this)
            if (this.money>=money){
                System.out.println (name+"开始取钱");
                this.money-=money;
                System.out.println (name+"取钱成功后余额为"+this.money);
            }else {
                System.out.println (name+"未取钱余额不足");
            }
        }
    }
    public static void test(){ //静态方法推荐用类名上锁
        synchronized (Account.class){

        }
    }

    public double getMoney() {
        return money;
    }

    public void setMoney(double money) {
        this.money = money;
    }

    public String getCardId() {
        return cardId;
    }

    public void setCardId(String cardId) {
        this.cardId = cardId;
    }
}
2.同步方法

线程安全,与多线程的应用_第10张图片

线程安全,与多线程的应用_第11张图片

  public synchronized void drawMoney(double money){
        //先搞清楚是谁来取钱
        String name = Thread.currentThread ().getName ();
        //1.判断账户金额是否足够

            if (this.money>=money){
                System.out.println (name+"开始取钱");
                this.money-=money;
                System.out.println (name+"取钱成功后余额为"+this.money);
            }else {
                System.out.println (name+"未取钱余额不足");
            }

    }

线程安全,与多线程的应用_第12张图片3.Lock锁

线程安全,与多线程的应用_第13张图片

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class Account {
    private double money;//余额
    private  String cardId;//卡号
    //创建一个锁对象
    private final Lock lk=new ReentrantLock ();//final  保护锁对象

    public Account() {
    }

    public Account(double money, String cardId) {
        this.money = money;
        this.cardId = cardId;
    }

    public  void drawMoney(double money){
        //先搞清楚是谁来取钱
        String name = Thread.currentThread ().getName ();
        //1.判断账户金额是否足够
            lk.lock ();//加锁
        try {
            if (this.money>=money){
                System.out.println (name+"开始取钱");
                this.money-=money;
                System.out.println (name+"取钱成功后余额为"+this.money);
            }else {
                System.out.println (name+"未取钱余额不足");
            }
        } catch (Exception e) {
            e.printStackTrace ();
        } finally {
            lk.unlock ();//解锁
        }
        

    }
    public static void test(){ //静态方法推荐用类名上锁
        synchronized (Account.class){

        }
    }

    public double getMoney() {
        return money;
    }

    public void setMoney(double money) {
        this.money = money;
    }

    public String getCardId() {
        return cardId;
    }

    public void setCardId(String cardId) {
        this.cardId = cardId;
    }
}

二、线程通信(前提保证线程安全)

1.什么是线程通信

线程安全,与多线程的应用_第14张图片

线程安全,与多线程的应用_第15张图片

线程安全,与多线程的应用_第16张图片

2.代码实例

public class ThreadTest {
    public static void main(String[] args) {
        Desk desk=new Desk ();

        //创建3个生产者线程
        new Thread ( ()-> {
            while (true) {
                desk.put();
            }

        },"厨师1" ).start ();
        new Thread ( ()-> {
            while (true) {
                desk.put();
            }
        },"厨师2" ).start ();
        new Thread ( ()-> {
            while (true) {
                desk.put();
            }

        },"厨师3" ).start ();

        //创建两个消费者线程
       new  Thread(()->{
           while (true) {
               desk.get();
           }
       },"吃货1").start ();
        new  Thread(()->{
            while (true) {
                desk.get();
            }
        },"吃货2").start ();
    }
    }



import java.util.ArrayList;
import java.util.List;

public class Desk {
    private List list =new ArrayList<> ();
    //放包子
    //厨师1,厨师2,厨师3
    public synchronized void put() {

        try {
            String name = Thread.currentThread ().getName ();
            //判断是否由包子
            if(list.size ()==0){
                list.add ( name+"做的肉包子" );
                System.out.println (name+"做了个肉包子");
                Thread.sleep ( 2000 );
             //唤醒别人,等待自己,(用锁对象调用)
                this.notifyAll ();//先唤醒 后等待
                this.wait ();
            }else {
                //唤醒别人,等待自己,(用锁对象调用)
                this.notifyAll ();//先唤醒 后等待
                this.wait ();
            }
        } catch (Exception e) {
            e.printStackTrace ();
        }

    }
    //取包子
    //吃货1,吃货2
    public synchronized void get() {
        try {
            String name = Thread.currentThread ().getName ();
            if (list.size ()==1){
                //有包子,吃掉
                System.out.println (name+"吃了"+list.get ( 0 ));
                list.clear ();
                Thread.sleep ( 1000 );
                //唤醒别人,等待自己,(用锁对象调用)
                this.notifyAll ();//先唤醒 后等待
                this.wait ();
            }else {
                //没有包子
                //唤醒别人,等待自己,(用锁对象调用)
                this.notifyAll ();//先唤醒 后等待
                this.wait ();
            }
        } catch (Exception e) {
            e.printStackTrace ();
        }

    }
}


三、线程池

1.什么是线程池

线程安全,与多线程的应用_第17张图片

2.线程池的工作原理

控制线程的数量,也控制任务的数量,避免系统创建太多线程,耗费大量的系统资源。挺高系统性能。

线程安全,与多线程的应用_第18张图片

3.如何创建线程池

线程安全,与多线程的应用_第19张图片

1.方式1(ThreadPoolExecutor对象创建)

线程安全,与多线程的应用_第20张图片

import java.util.concurrent.*;

public class ThreadPoolTest {
    public static void main(String[] args) {
//            public ThreadPoolExecutor(int corePoolSize,
//                                      int maximumPoolSize,
//                                      long keepAliveTime,
//                                      TimeUnit unit,
//                                      BlockingQueue workQueue) {
//                                      this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
//                                      Executors.defaultThreadFactory(), defaultHandler);
        //1.通过ThreadPoolExecutor 创建一个线程池对象
    ExecutorService pool= new ThreadPoolExecutor ( 3,5,8,TimeUnit.SECONDS,
                new ArrayBlockingQueue<> ( 4 ),Executors.defaultThreadFactory (),new ThreadPoolExecutor.AbortPolicy ());
        }




    }



线程安全,与多线程的应用_第21张图片

2.方式2(Executors创建)

线程安全,与多线程的应用_第22张图片

import java.util.concurrent.*;

public class Test {
    public static void main(String[] args) throws Exception {
//        //1.通过ThreadPoolExecutor 创建一个线程池对象
//        ExecutorService pool= new ThreadPoolExecutor ( 3,5,8, TimeUnit.SECONDS,
//                new ArrayBlockingQueue<> ( 4 ), Executors.defaultThreadFactory (),new ThreadPoolExecutor.AbortPolicy ());
        //1-2 通过Executors创建一个线程对象。
        ExecutorService pool = Executors.newFixedThreadPool ( 3 );//新建固定数量线程的线程池
        //计算密集型任务:核心线程数量=CPU核数+1;
        //IO密集型任务:核心线程数量=CPU核数*2;
        Executors.newSingleThreadExecutor ();//新建单个线程数量的线程池
        //2.使用线程处理Callable 任务类
        Future f1 = pool.submit ( new MyCallable ( 100 ) );//返回未来任务对象(结果)
        Future f2 = pool.submit ( new MyCallable ( 200 ) );
        Future f3 = pool.submit ( new MyCallable ( 300 ) );
        Future f4 = pool.submit ( new MyCallable ( 400 ) );

        System.out.println ( f1.get () );
        System.out.println ( f2.get () );
        System.out.println ( f3.get () );
        System.out.println ( f4.get () );


    }
}

3.注意事项

线程安全,与多线程的应用_第23张图片

4.线程池处理Runnable任务

1.常用方法

线程安全,与多线程的应用_第24张图片

2.新任务的拒绝策略

线程安全,与多线程的应用_第25张图片

3.代码

public class MyRunnable implements Runnable{
    @Override
    public void run() {
        //描述任务
        System.out.println (Thread.currentThread ().getName ()+"====>666");
        try {
            Thread.sleep ( Integer.MAX_VALUE );
        } catch (InterruptedException e) {
            e.printStackTrace ();
        }
    }
}


import java.util.concurrent.*;

public class ThreadPoolTest {
    public static void main(String[] args) {
//            public ThreadPoolExecutor(int corePoolSize,
//                                      int maximumPoolSize,
//                                      long keepAliveTime,
//                                      TimeUnit unit,
//                                      BlockingQueue workQueue) {
//                                      this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
//                                      Executors.defaultThreadFactory(), defaultHandler);
        //1.通过ThreadPoolExecutor 创建一个线程池对象
    ExecutorService pool= new ThreadPoolExecutor ( 3,5,8,TimeUnit.SECONDS,
            new ArrayBlockingQueue<> ( 4 ),Executors.defaultThreadFactory (),new ThreadPoolExecutor.AbortPolicy ());
        Runnable target = new MyRunnable ();
        pool.execute ( target );//把任务交给线程池,线程池会自动创建一个新的线程,自动处理这个任务,自动执行。
        pool.execute ( target );
        pool.execute ( target );
        //临时线程的创建时机
        pool.execute ( target );//复用前面的核心线程执行任务
        pool.execute ( target );
        //开始进入任务队列
        pool.execute ( target );
        pool.execute ( target );
        pool.execute ( target );
        pool.execute ( target );
        //开始拒绝策略(任务队列占满)
        pool.execute ( target );//拒绝直接抛出错误
//        pool.shutdown ();//等线程池的任务全部执行完毕后,才会关闭线程池
//        pool.shutdownNow ();//立刻关闭线程池,不管任务是否执行完毕


    }




    }




5.线程池处理Callable任务

1.常用方法

线程安全,与多线程的应用_第26张图片

 2.代码

import java.util.concurrent.Callable;

public class MyCallable implements Callable {
    private int n;

    public MyCallable() {
    }

    public MyCallable(int n) {
        this.n = n;
    }

    @Override
    public String call() throws Exception {
        int sum=0;
        for (int i = 0; i <=n; i++) {
            sum+=i;
        }
        return Thread.currentThread ().getName ()+"求出了1-"+n+"的和是:"+sum;
    }
}



import java.util.concurrent.*;

public class Test {
    public static void main(String[] args) throws Exception {
        //1.通过ThreadPoolExecutor 创建一个线程池对象
        ExecutorService pool= new ThreadPoolExecutor ( 3,5,8, TimeUnit.SECONDS,
                new ArrayBlockingQueue<> ( 4 ), Executors.defaultThreadFactory (),new ThreadPoolExecutor.AbortPolicy ());
        //2.使用线程处理Callable 任务类
        Future f1 = pool.submit ( new MyCallable ( 100 ) );//返回未来任务对象(结果)
        Future f2 = pool.submit ( new MyCallable ( 200 ) );
        Future f3 = pool.submit ( new MyCallable ( 300 ) );
        Future f4 = pool.submit ( new MyCallable ( 400 ) );

        System.out.println ( f1.get () );
        System.out.println ( f2.get () );
        System.out.println ( f3.get () );
        System.out.println ( f4.get () );


    }
}

四、并发、并行

1.进程和线程的关系

线程安全,与多线程的应用_第27张图片

2.什么是并发?

线程安全,与多线程的应用_第28张图片

3.什么是并行?

线程安全,与多线程的应用_第29张图片

4.多线程是怎么执行的?

线程安全,与多线程的应用_第30张图片

五、线程的生命周期

       人的生命周期

线程安全,与多线程的应用_第31张图片

线程安全,与多线程的应用_第32张图片

sleep(不会释放锁)   wait(会释放锁)

线程安全,与多线程的应用_第33张图片

六、悲观锁和乐观锁

1.悲观锁:

一上来就加锁,没有安全感,每次只能一个线程进入访问完毕后,再关锁。线程安全,性能较差!
public class MyRunnable implements Runnable{
    private int count;
    @Override
    public void run() {
        for (int i = 0; i <100; i++) {

           //悲观锁
            synchronized (this) {
                System.out.println ("count=====>"+ (++count));
            }


        }

    }
}


public class Test1 {
    public static int Number;
    public static void main(String[] args) {
        //悲观锁,乐观锁的原理
        //悲观锁:一上来就加锁,没有安全感,每次只能一个线程进入访问完毕后,再关锁。线程安全,性能较差!
        //乐观锁:一开始不上锁,认为是没有问题的,大家一起跑,等要出现线程安全问题时才开始控制。线程安全,性能较好!

        //需求:1个变量,100个线程,每个线程对其加100次
        Runnable target =new MyRunnable ();
        for (int i = 1; i <=100; i++) {
            new Thread (target).start ();
        }


    }
}

2.乐观锁

利用原子类实现

乐观锁:一开始不上锁,认为是没有问题的,大家一起跑,等要出现线程安全问题时才开始控制。线程安全,性能较好!
public class MyRunnable2 implements Runnable{
    //乐观锁 CAS算法 比较(Compare)A(and)S(修改Set) 算法
    //整数修改的乐观锁:原子类实现的
    private AtomicInteger count=new AtomicInteger ();

    @Override
    public void run() {
        for (int i = 0; i <100; i++) {


                System.out.println ("count=====>"+ count.incrementAndGet ());//先加1然后再返回值


        }


public class Test2 {

    public static void main(String[] args) {
        //悲观锁,乐观锁的原理
        //悲观锁:一上来就加锁,没有安全感,每次只能一个线程进入访问完毕后,再关锁。线程安全,性能较差!
        //乐观锁:一开始不上锁,认为是没有问题的,大家一起跑,等要出现线程安全问题时才开始控制。线程安全,性能较好!

        //需求:1个变量,100个线程,每个线程对其加100次
        Runnable target =new MyRunnable2 ();
        for (int i = 1; i <=100; i++) {
            new Thread (target).start ();
        }


    }
}

你可能感兴趣的:(java,开发语言)