黑马程序员<java基础<多线程同步异步问题>>

------<a href="http://www.itheima.com" target="blank">Java培训、Android培训、iOS培训、.Net培训</a>、期待与您交流! -------

多线程

进程:正在进行中的程序
线程:就是进程中一个执行单元或执行情景或执行路径负责进程中程序执行的控制单元
一个进程中至少要有一个线程
当一个进程中线程有多个时,就是多线程


多线程解决的问题:可以让多部分代码同时执行。


什么是同时执行呢?其实都是cpu在瞬间做着快速切换完成的。
cpu的切换随机的,按照时间片的方式来完成。


其实java运行就是多线程的
在执行main函数中内容的同时,垃圾回收器也在回收堆内存的垃圾。
所以执行main方法的线程,和执行垃圾回收器的线程同时在执行。
这就是多线程。


jvm在启动线程的时候,每一个线程都有自己要执行的内容
其中一个负责执行main函数中的内容:这个线程称之为:主线程 main
负责垃圾回收器运行的称之为垃圾回收线程


我们就可以推理出,为什么要启动多线程呢?
因为有多部分代码需要同时执行
而且每一个线程都有自己要执行的内容,这个内容称之为:线程的任务
简单说:
启动线程就是为了执行任务,当任务有多个需要同时执行时,就需要多个线程


创建线程方式---继承Thread
通过查阅Thread类的描述,发现
创建线程有两种方式:
方式一:继承Thread类,覆盖run方法
步骤:
1,定义类继承Thread类
2,覆盖Thread类中的run方法
3,创建Thread类的子类对象创建子类线程对象
线程要执行什么内容,线程自己肯定有定义
这个定义就是run方法,这个方法中定义就是线程要运行的内容
可是该run方法的内容并不是我们所需要的。


怎么能让线程去运行我们自定义的内容呢?
只有覆盖Thread类中的run方法。覆盖之前需要先继承。
这就是为什么要继承Thread,类并覆盖run方法的原因。


主线程要运行的任务都在main函数中
自定义的线程要运行的任务都在run方法中


run方法就是专门用于存储线程任务代码的地方


class Demo extends Thread
{
 private String name;
Demo(String name)
{
 this.name=name;
}
public void run()
{
 for(int x=1;x<=10;x++)
{
 System.out.println(name+"---"+x);
}}}
class ThreadDemo
{  public static void main(String[]args)
{ Demo d1=new Demo("旺财");
  Demo d2=new Demo("小明");
线程光创建不行,还需要运行才会出现多条执行路径
发现要运行线程,必须使用start方法
start方法做了两件事:1,开启了线程让线程可以运行 2,调用了run方法


调用run方法和调用start方法的区别:
调用run方法,仅仅是一般对象在调用对象中的方法,并没有开启线程,还有主线程来完成的run方法的运行。
调用start方法,是开启了一个线程(一个新的执行路径)
这个线程去执行了run方法。
d1.start();
d2.start();
for(int x=0;x<20;x++)
{
  System.out.println(Thread.currentThread().getName()+"...."+x);
}
}
}
currentThread()返回对当前正在执行的线程对象的引用


多线程的运行状态
wait()一直冻结  notify()唤醒
sleep(time)  到冻结  sleep(time)时间到解除冻结
临时阻塞状态 这种状态的线程 具备着cpu的执行资格不具备cpu的执行权
start();是该线程开始运行   多次启动一个线程是非法的


多线程---售票代码
创建线程 第一个方式
class Ticket extends Thread
{
 private static int num=100;
public void run()
{ while(true)
{ if(num>0)
{System.out.println(Thread.currentThread().getName()+".....sale....."+num--);
}
}
}
}
class TicketDemo
{
public static void main(String[]args)
{
 Ticket t1=new Ticket();
Ticket t2=new Ticket();
Ticket t3=new Ticket();
Ticket t4=new Ticket();
t1.start();
t2.start();
t3.start();
t4.start();
}}


将售票的例子改写成Runnable接口的方式
创建线程方式二:
1.定义一个类实现Runnable接口
2.覆盖Runnable接口中的run方法
3.创建该接口的子类对象
4.通过Thread类进行线程的创建,并将Runnable接口的子类对象作为实参进行传递。
为什么要传递呢?
4让线程对象创建时,就要明确要运行哪个run方法,而这个run方法是需要被对象调用的,所以将 run方法所属的对象传递给Thread类的构造函数
5.调用Thread类中的start方法开启线程。


Runable接口的由来其实就是将线程的任务进行对象的封装
将线程任务封装成对象后,通过Runnable接口可以降低和Thread对象的耦合性
如果是继承Thread类,覆盖run方法这种情况
Thread的子类即封装了线程的任务,而且自身还是一个线程对象
这就是任务和对象耦合性过高。


class Ticket implements Runnable
{
 private  int num=100;
public void run()
{ while(true)
{ if(num>0)
{System.out.println(Thread.currentThread().getName()+".....sale....."+num--);
}
}
}
}
class TicketDemo
{
public static void main(String[]args)
{
Ticket t=new Ticket();
Thread t1=new Thread(t);
Thread t2=new Thread(t);
Thread t3=new Thread(t);
Thread t4=new Thread(t);
t1.start();
 t2.start();
t3.start();
t4.start();
}}


多线程的安全问题
安全问题产生的原因:
1,多个线程在操作共享数据
2,操作共享数据的代码有多条
一个线程在执行多条操作共享数据的过程中,其他线程参数参与了运算,这时就会发生安全问题


想要分析是否安全问题
依据:线程任务中有没有共享数据,该数据是否被多条语句操作
解决方案:只要保证一个线程在执行多条操作共享数据的语句时,其他线程不能参与运算即可。
当该线程都执行完后,其他线程才可以执行这些语句。
代码表现: java中给我们提供具体的解决语句。
那就是同步代码块
格式:
 synchronized(对象)

需要被同步的语句
}


同步的原理:其实就是将需要同步的代码进行封装,并在该代码上加了一个锁
同步的好处:解决多线程的安全问题
同步的弊端:会降低性能


一种现象:出现了多线程安全问题,为了解决,加上了同步,发现,问题依旧如何处理?


同步的前提:
      必须要保证在同步中有多个线程,因为同步中只有一个线程该同步是没有意义。
      必须保证多个线程在同步中是同一个锁
   
     必须保证多个线程使用的锁是同一个
     这时才成为多个线程被同步了
class Ticket implements Runnable
{
 private  int num=100;
private Object obj=new Object();
public void run()
{ while(true)
{
synchronized(obj)
{
 if(num>0)
{
try{Thread.sleep(10);}catch(InterruptenException e){}
System.out.println(Thread.currentThread().getName()+".....sale....."+num--);
}
}
}
}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值