第十次总结 线程的异步和同步
Posted zxxb
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了第十次总结 线程的异步和同步相关的知识,希望对你有一定的参考价值。
- 线程的异步和同步?
- 如何实现线程的同步?
- 什么是线程池?
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);
以上是关于第十次总结 线程的异步和同步的主要内容,如果未能解决你的问题,请参考以下文章