并发编程详解

背景

为什么需要并发编程?
1.并发可以提高程序执行效率

eg:货车拉来了一批货,需要尽快卸货,如果1个工人搬运,需要花费10个小时,如果请10个工人同时搬运,理论上只要1个小时。同理,程序中如果想“多个人”同时处理一个任务,java提供了多线程的功能

2.并发可以将同步改成异步

eg:和其他系统集成,某个接口执行完需要10分钟,其他系统不关心执行过程,只关心结果,不希望等10分钟,想立即拿到结果(实际上是一个同步转异步),怎么处理?可以单独创建一个线程去处理这个任务,同时将接口调用成功的结果返回。对于用户来说,就能在毫秒级拿到调用成功的结果(真实的任务还在新建的线程中慢慢执行)

延伸

多线程看上去都是优点,有限制的地方吗?

多线程的效率不能无限提高,实际并发数和硬件配置有关,线程池中一般配置多线程的并发数为CPU的核数加1
多个线程同时处理某个资源,可能会有问题,这个在后面展开

概念

进程
是操作系统进行资源分配的基本单位

线程
是操作系统能够进行运算调度的最小单位

多线程
多个线程同时执行

进程和线程的关系
包含关系,一个进程包含多个线程。一个应用最少有一个进程,一个进程最少有一个线程。
进程有独立的资源内存区域(内存空间),线程之间没有单独的资源内存区域,多个线程共享进程的内存区域,线程有自己的堆栈和局部变量。
进程是一个静态概念,如一个程序,线程是进程当中最小的执行单元(动态概念)

线程的使用

开启线程的三种方式
1.继承Thread类

继承Thread类
重写run()
创建本类对象,调用start()
public class ThreadOne extends Thread {
 @Override
 public void run() {
 for (int i = 0; i < 1000; i++) {
 System.out.println("线程启动了"+Thread.currentThread().getId()+"=
 try {
 Thread.sleep(500);//睡眠500毫秒
 } catch (InterruptedException e) {
 e.printStackTrace();
 }
 }
 }
 public ThreadOne(String name) {
 super(name);
 }
}
public class MyThread {
 public static void main(String[] args) {
 new ThreadOne("Thread_one").start();
 new ThreadOne("Thread_Two").start();
 }
}

2.实现Runnable接口:

实现Runnable接口
重写run();
创建Thread对象,并注入本类对象
调用Thread对象的start()
public class ThreadTwo implements Runnable {
 @Override
 public void run() {
 for (int i = 0; i < 1000; i++) {
 System.out.println("线程启动了"+Thread.currentThread().getId()+"=
 try {
 Thread.sleep(500);//睡眠500毫秒
 } catch (InterruptedException e) {
 e.printStackTrace();
 }
 }
 }
}
public class MyThread {
 public static void main(String[] args) {
 new Thread(new ThreadTwo(),"Thread_one").start();
 new Thread(new ThreadTwo(),"Thread_two").start();
 }
}

3.通过Callable和Future创建线程:

创建Callable接⼝的实现类,并实现call()⽅法,该call()⽅法将作为线程执⾏体,并且有返回值。
创建Callable实现类的实例,使⽤FutureTask类来包装Callable对象,该FutureTask对象
封装了该Callable对象的call()⽅法的返回值。
使⽤FutureTask对象作为Thread对象的target创建并启动新线程
调⽤FutureTask对象的get()⽅法来获得⼦线程执⾏结束后的返回值
public class MyCallableThread implements Callable<Integer> {
 public Integer call() throws Exception {
 int i = 0;
 for(;i<100;i++)
 {
 System.out.println(Thread.currentThread().getName()+" "+i);
 Thread.sleep(100);
 }
 return i;
 }
}


public static void main(String[] args) {
 MyCallableThread ct=new MyCallableThread();
 FutureTask ft =new FutureTask(ct);
 new Thread(ft,"有返回值的线程").start();
 System.out.println("Main here");
 try {
 System.out.println("ft result:"+ft.get());
 } catch (InterruptedException e) {
 e.printStackTrace();
 } catch (ExecutionException e) {
 e.printStackTrace();
 }
}

开启线程的三种方式的比较

实际开发中由于继承了thread类,就不能继承其他类,所以多用实现runnable接口或实现callable接口。runnable接口没有返回值,callable接口中可以有返回值

线程的五种状态

在这里插入图片描述
1)new
2) 调⽤start后
3)分配到CPU资源
4)如出现Sleep或IO,然后回到2)
5)执⾏完后死亡

多线程与并发

 并发:服务能同时处理很多请求,提⾼程序效率与性能
 并发带来的问题:公共资源竞争,使得数据最终处理不正确

eg:12306火车订票,秒杀
分析下⾯Demo1的结果!

public class Demo1 {
 public static int numTicket=5;
 public static void main(String[] args) throws InterruptedException {
 for (int i = 0; i < 5; i++) {
 new Thread(()->{
 numTicket--; 
 }).start();
 }
 Thread.sleep(3000);
 System.out.println("numTicket:"+numTicket);
 }
}

在这里插入图片描述
分析:创建了5个线程来消费ticket这个公共资源,循环5次,每次自减1,结果应该是0,为啥会打印的结果实1?
线程1进来拿到ticket 值为2,进入了休眠,线程2同时进来也拿到了ticket 值也为2,再自减就打印了1。所以多个线程并发,在对公共资源执行写的操作时会有问题

线程同步

关键字synchronized
语法1

synchronized(object){
}

语法2

public synchronized void f(){}

eg:如果不同步的话,会出现如下情况

public class Ticket {
 public int num = 100;
 public void sellTicket(){
 if (this.num <= 0) {
 return;
 }
 this.num--;
 System.out.println("用户" + Thread.currentThread().getName() + "买到票
 
 }
}
public class MyTicketThread extends Thread {
 Ticket ticket;
 @Override
 public void run() {
 for (int i = 0; i < 10; i++) {
 ticket.sellTicket();
 }
 }
 public MyTicketThread(String name, Ticket ticket) {
 super(name);
 this.ticket = ticket;
 }
}

public class Test {
 public static void main(String[] args) {
 Ticket t =new Ticket();
 new MyTicketThread("张1",t).start();
 new MyTicketThread("张2",t).start();
 new MyTicketThread("张3",t).start();
 new MyTicketThread("张4",t).start();
 new MyTicketThread("张5",t).start();
 new MyTicketThread("张6",t).start();
 new MyTicketThread("张7",t).start();
 new MyTicketThread("张8",t).start();
 new MyTicketThread("张9",t).start();
 new MyTicketThread("张10",t).start();
 new MyTicketThread("张11",t).start();
 }
}

同步后

public class Ticket {
 public int num = 100;
 public void sellTicket(){
 synchronized (this) {
 if (this.num <= 0) {
 return;
 }
 this.num--;
 System.out.println("用户" + Thread.currentThread().getName() + "
 }
 }
}

或者

public class Ticket {
 public int num = 100;
 public synchronized void sellTicket(){
 if (this.num <= 0) {
 return;
 }
 this.num--;
 System.out.println("用户" + Thread.currentThread().getName() + "买到票
 }
}

线程死锁

eg:⼀条线程锁住了资源,然后出现了死循环,其他线程⽆法得到资源
 eg:A线程锁住了AA资源然后需要BB资源,但是B线程已经锁住了BB资源需要AA资源,这样双⽅都⽆法释放资源和得到资源。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值