第十次总结 线程的异步和同步

Posted zxxb

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了第十次总结 线程的异步和同步相关的知识,希望对你有一定的参考价值。

  1. 线程的异步和同步?
  2. 如何实现线程的同步?
  3. 什么是线程池?

1.线程的异步和同步?

异步:线程默认是异步执行的
多个线程可以同时操作同一块内存

 

如果对内存数据的修改执行异步操作,可能会出现错误的数据,叫做线程不安全

要保证数据安全,对于修改数据的操作,需要进行同步

 

同步:在同一个时间片段内,只能有一个线程来操作同一块内存空间

 

一般情况下,读操作是可以异步的
写操作必须要同步


2.如何实现线程的同步?

模拟银行取钱

Account类

/**
 * 银行账户类
 */
public class Account {

    //账户余额
    private int money = 1000;
  /**
     * 取钱的操作
     *
     * @param m 要取出的金额
     */
    public void quQian(int m) {if (money < m) {
            System.out.println("余额不足!");
        } else {
            try {
                Thread.sleep(1000);
            } catch (Exception ef) {
                ef.printStackTrace();
            }
            //计算出新的余额
            money = money - m;
            System.out.println("取出了" + m + "元,余额为:" + money);
        }
    }

}

 

QuQianThread类

/**
 * 取钱的线程
 */
public class QuQianThread extends Thread{

    //银行账户对象
    private Account account;

    //通过构造方法传入账户对象
    public QuQianThread(Account account){
        this.account = account;
    }

    @Override
    public void run() {
        //开始取钱
        account.quQian(800);
    }
}

 

测试类

public class Test {
    public static void main(String[] args) {

        //创建账户对象
        Account account = new Account();


        QuQianThread t1 = new QuQianThread(account);
        QuQianThread t2 = new QuQianThread(account);


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

    }
}

 

如果不进行线程同步,输出结果可能为

技术图片    技术图片    技术图片

 

 

 

1.使用同步关键字来对一段代码进行同步
synchronized(对象){
要同步的操作代码...
}

当执行到synchronized的时候,先检查是否有锁,如果没有,就进去执行,进去执行之前先加锁
如果发现有锁,就等待其它线程释放锁

修改Acoout类

public void quQian(int m) {
        synchronized (this) {
            if (money < m) {
                System.out.println("余额不足");
            } else {
                try {
                    Thread.sleep(1000);
                } catch (Exception ef) {
                    ef.printStackTrace();
                }
                money = money - m;
                System.out.println("取出了" + m + "元,余额为:" + money);
            }
        }
    }
}

 

2.在方法上加同步关键字 synchronized

当执行该方法的时候,先检查是否有锁,如果没有,就执行方法进去执行之前先加锁
如果有锁,就等待其它线程释放锁

public synchronized  void quQian(int m) {
        synchronized (this) {
            if (money < m) {
                System.out.println("余额不足");
            } else {
                try {
                    Thread.sleep(1000);
                } catch (Exception ef) {
                    ef.printStackTrace();
                }
                money = money - m;
                System.out.println("取出了" + m + "元,余额为:" + money);
            }
        }
    }

}

 


3.使用同步锁对象
ReentrantLock rtl = new ReentrantLock();
//上锁
rtl.lock()
//解锁
rtl.unlock()

public class Account {

    //账户余额
    private int money = 1000;
    //创建锁对象
    ReentrantLock rtl = new ReentrantLock();

    /**
     * 取钱的操作
     *
     * @param m 要取出的金额
     */
    public void quQian(int m) {
        //上锁
        rtl.lock();
        if (money < m) {
            System.out.println("余额不足!");
        } else {
            try {
                Thread.sleep(1000);
            } catch (Exception ef) {
                ef.printStackTrace();
            }
            //计算出新的余额
            money = money - m;
            System.out.println("取出了" + m + "元,余额为:" + money);

            //解锁
            rtl.unlock();
        }
    }

}

 

4.使用线程本地变量

 

ThreadLocal

 

ThreadLocal的数据存存放在线程的内存中的,会在每个线程中生成一份副本
在线程中使用这个变量的时候,实际上使用的是副本

 

ThreadLocal中会有一个ThreadLocalMap,多个线程的数据是放在Map中的、

MyThread

public class MyThread extends Thread {

    @Override
    public void run() {
       Test.local.set("Hello");
        this.setName("Hello");
        System.out.println(this.getName() + ">>" + Test.local.get());
        try {
            Thread.sleep(1000);
        }catch (Exception ef){
            ef.printStackTrace();
        }
        System.out.println(this.getName() + ">>" + Test.local.get());
    }
}

 

 MyThread2

public class MyThread2 extends Thread {
    @Override
    public void run() {
        Test.local.set("World");
        this.setName("World");
        System.out.println(this.getName() + ">>" + Test.local.get());
        //移除local的值
        Test.local.remove();
        System.out.println(this.getName() + ">>" + Test.local.get());
    }
}

 

测试类

public class Test {

//    static String msg="";

    //用来存放线程本地变量
    static ThreadLocal<String> local = new ThreadLocal<>();


    public static void main(String[] args) {

//
        local.set("Main");
//        local.get();
//        local.remove();


        MyThread t1 = new MyThread();
        MyThread2 t2 = new MyThread2();

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

        try {
            Thread.sleep(3000);
        }catch (Exception ef){
            ef.printStackTrace();
        }
        System.out.println("main:"+local.get());
    }
}

 

技术图片


5.使用wait...notify机制
生产者-消费者 模式

wait()方法和notify()方法不是线程的方法,是Object类的方法
所有的对象都能够调用wait和notify方法

wait和notify必须用在synchronized关键字内部

6.使用阻塞队列
LinkedBlockQueue

Baozi类

public class BaoZi {

    private String type;

    public BaoZi(String type){
        this.type = type;
    }

    @Override
    public String toString() {
        return type+"包子";
    }
}

 

Customer消费者

/**
 * 消费者线程
 */
public class Customer2 extends Thread {

    @Override
    public void run() {
        System.out.println(this.getName() + "执行了");

        while (true) {

            if (TestMain2.list.size() > 0) {
                BaoZi bz = TestMain2.list.poll();
                System.out.println(this.getName() + "-------------买到一个" + bz);
                try {
                    Thread.sleep(1000);
                } catch (Exception ef) {
                    ef.printStackTrace();
                }
            }
        }

    }
}

 

测试类

public class TestMain2 {

    //存放数据的容器
    public static LinkedBlockingQueue<BaoZi> list = new LinkedBlockingQueue<>();

    public static void main(String[] args) {

        Productor2 p = new Productor2();
        Customer2 c = new Customer2();
        Customer2 c2 = new Customer2();

        p.start();
        c.start();
        c2.start();
    }
}

 


什么是线程池?

//单线程池
ExecutorService service = Executors.newSingleThreadExecutor();

//固定大小的线程池
ExecutorService service2 = Executors.newFixedThreadPool(10);

//可缓存线程的线程池
ExecutorService service3 = Executors.newCachedThreadPool();


ExecutorService service4 = Executors.newScheduledThreadPool(10);

 

以上是关于第十次总结 线程的异步和同步的主要内容,如果未能解决你的问题,请参考以下文章

第十周java学习总结

201621123042《java程序设计》第十次作业

第十五节:深入理解async和await的作用及各种适用场景和用法

第十次作业总结

第十次作业总结

第十次作业总结