- 线程的异步和同步?
- 如何实现线程的同步?
- 什么是线程池?
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();
}
}
什么是线程池?
线程池
Executors 用来创建线程池的类
单线程池,只能有一个线程的线程池
newSingleThreadExecutor
固定大小线程池
newFixedThreadPool(10)
缓存线程池,可以根据任务数量来创建线程
newCachedThreadPool
可以执行延时任务的线程池
newScheduledThreadPool(10)
public class Demo {
public static void main(String[] args) {
//单线程池
// ExecutorService service = Executors.newSingleThreadExecutor();
//
// //固定大小的线程池
// ExecutorService service2 = Executors.newFixedThreadPool(3);
//
// //可缓存线程的线程池
// ExecutorService service3 = Executors.newCachedThreadPool();
//
//
// ExecutorService service4 = Executors.newScheduledThreadPool(10);
ScheduledExecutorService service5 =Executors.newScheduledThreadPool(3) ;
for (int i = 0; i < 5; i++) {
//参数1 任务对象
//参数2 延迟时间
//参数3: 延迟的时间单位
MyRunnable r1 = new MyRunnable();
service5.schedule(r1,5, TimeUnit.SECONDS);
}
//结束完任务,关闭线程池
// service5.shutdown();
//立马关闭线程池,并返回还在等待执行的任务列表
List<Runnable> list =service5.shutdownNow();
System.out.println(list.size());
}
}