黑马程序员——四、java基础之多线程(7)
来源:互联网 发布:金猴闹春 知乎 编辑:程序博客网 时间:2024/06/05 00:30
多线程是java当中非常重要的部分,那么究竟什么是多线程呢?多线程有有什么好处呢?多线程的定义和使用又是如何实现的呢?
4.1进程和线程的概念
进程:正在进行中的程序(直译),是任务的基本单位。一个进程中至少要有一个线程。
线程:就是进程中一个负责程序执行的控制单元(执行路径),是cpu的基本单位。每一个线程都有自己运行的内容。这个内容可以称为线程要执行的任务。
多线程:一个进程中可以多执行路径。
多线程有什么好处呢?比如有三个线程分别为Thread1、Thread2、Thread3,传统的单线程是按照从上往下的顺序依次执行,Thread1执行不完,另外的线程就需要等待。小伙伴们不乐意了,于是就开始你运行一会,我运行一会,多么欢乐和谐。这就是多线程。到底谁执行呢?执行多长时间呢?这是由CPU决定的,就像皇帝想翻哪个妃子的牌子决定权在皇帝那里。
开启多个线程是为了同时运行多部分代码。
多线程好处:解决了多部分同时运行的问题。
多线程的弊端:线程太多会导致效率的降低。
其实应用程序的执行都是cpu在做着快速的切换完成的,这个切换是随机的。
JVM启动时就启动了多个线程,至少有两个线程可以分析的出来。
1,执行main函数的线程,
该线程的任务代码都定义在main函数中。
2,负责垃圾回收的线程。
4.2如何创建线程呢?小伙伴们又捉急了,请往下看
4.2.1创建线程方式一:继承Thread类。
步骤:
1,定义一个类继承Thread类。
2,覆盖Thread类中的run方法。
3,直接创建Thread的子类对象创建线程。
4,调用start方法开启线程并调用线程的任务run方法执行。
可以通过Thread的getName获取线程的名称 Thread-编号(从0开始)
主线程的名字就是main。
4.2.2Thread类里面有好多操作方法啊,小伙伴们不要急,我们节选部分使用率高的几个方法看下作用,慢慢看哦。
start() 启动线程
run() 线程启动后执行的函数
interrupt() 中断线程
join() 合并线程
sleep(long millis) 让当前线程休眠
setDaemon(boolean on) 设置线程为守护线程
setPriority(int newPriority) 设置线程优先级
setName(String name) 设置线程名
getName() 可以获取线程对象的标志名,线程对象默认标志名是 Thread-编号,编号从0开始。
static Thread currentThread() 返回当前正在执行的线程对象的引用
4.2.3创建线程方式二:实现Runnable接口
步骤:
1.定义类实现 Runnable 接口
2.覆盖 Runnable 接口中的 run 方法
3.通过 Thread 类建立线程对象
4.将 Runnable 接口的子类对象作为参数传递给 Thread 类对象的构造函数
5.调用 Thread 类的 start 方法开启线程
总结: Runnable 接口是把 Thread 中未知功能封装起来了,让别的类来实现 Runnalbe 接口之后,再把对象的应用传给 Thread 类对象,那么就实现了 Thread 对象启动新线程后去运行,run 中的方法。Runnable 接口对 Thread 类的功能进行了扩展。
小伙伴们好愤怒,本来智商就很拙计,一个都记不住,为啥还要多一个?矮油矮油,不要捉急嘛,马上告诉你啦。
为什么要有Runnable接口的出现?
1:通过继承Thread类的方式,可以完成多线程的建立。但是这种方式有一个局限性,如果一个类已经有了自己的父类,就不可以继承Thread类,因为java单继承的局限性。
可是该类中的还有部分代码需要被多个线程同时执行。这时怎么办呢?
只有对该类进行额外的功能扩展,java就提供了一个接口Runnable。这个接口中定义了run方法,其实run方法的定义就是为了存储多线程要运行的代码。
所以,通常创建线程都用第二种方式。
因为实现Runnable接口可以避免单继承的局限性。
2:其实是将不同类中需要被多线程执行的代码进行抽取。将多线程要运行的代码的位置单独定义到接口中。为其他类进行功能扩展提供了前提。
所以Thread类在描述线程时,内部定义的run方法,也来自于Runnable接口。
实现Runnable接口可以避免单继承的局限性。而且,继承Thread,是可以对Thread类中的方法,进行子类复写的。但是不需要做这个复写动作的话,只为定义线程代码存放位置,实现Runnable接口更方便一些。所以Runnable接口将线程要执行的任务封装成了对象。
实现方式和继承方式有什么区别?考面试题
实现方式好处:避免了单继承的局限性,在定义线程时建议使用实现方式,
继承thread:线程代码存放在thread的子类run方法中。
实现runnable:线程代码存放在接口的子类的run方法。
4.2.4线程的状态:
被创建:start()
运行:具备执行资格,同时具备执行权;
冻结:sleep(time),wait()—notify()唤醒;线程释放了执行权,同时释放执行资格;
临时阻塞状态:线程具备cpu的执行资格,没有cpu的执行权;
消亡:stop()
4.2.5多线程代码示例,以四个窗口卖火车票为例:
class Ticket implements Runnable{private int tick = 1000;Object obj = new Object();public void run(){while(true){synchronized(obj){if(tick>0){//try{Thread.sleep(10);}catch(Exception e){}System.out.println(Thread.currentThread().getName()+"....sale : "+ tick--);}}}}}class TicketDemo2{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();}}
4.3同步
多线程建立好了,但是神奇地发现,火车票竟然有-1号票存在,这还了得。怎么办啊怎么办?神奇的小伙伴告诉我需要同步。好吧我要开始学习同步了。
多线程安全问题的原因:
当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分,还没有执行完,另一个线程参与进来执行。导致共享数据的错误。
解决办法:
对多条操作共享数据的语句,只能让一个线程都执行完。在执行过程中,其他线程不可以参与执行。
霸气侧漏的java提供了同步的方法来解决那个令人腿疼的-1号票问题。
synchronized(对象)//任意对象都可以。这个对象就是锁。
{
需要被同步的代码
}
同步函数:其实就是将同步关键字定义在函数上,让函数具备了同步性。
同步函数是用的哪个锁呢?
函数都有自己所属的对象this,所以同步函数所使用的锁就是this锁。
当同步函数被static修饰时,这时的同步用的是哪个锁呢?
静态函数在加载时所属于类,这时有可能还没有该类产生的对象,但是该类的字节码文件加载进内存就已经被封装成了对象,这个对象就是该类的字节码文件对象。
所以静态加载时,只有一个对象存在,那么静态同步函数就使用的这个对象。
这个对象就是 类名.class
同步代码块和同步函数的区别?
同步代码块使用的锁可以是任意对象。
同步函数使用的锁是this,静态同步函数的锁是该类的字节码文件对象。
在一个类中只有一个同步,可以使用同步函数。如果有多同步,必须使用同步代码块,来确定不同的锁。所以同步代码块相对灵活一些。
同步的前提:
1,必须要有两个或者两个以上的线程。(小伙伴说:一个线程用毛毛的同步噻)
2,必须是多个线程使用同一个锁。(小伙伴又说:你开别人家的门怎么能进我家呢?)
必须保证同步中只能有一个线程在运行。
好处:解决了多线程的安全问题。
弊端:多个线程需要判断锁,较为消耗资源,产生了死锁。
哇,好恐怖的一个词唉,死锁。这个锁死掉了吗?事实是,这个锁让程序死掉了……
带领小伙伴们见识下毕姥爷经典的死锁代码吧。
public class DeadLockTest {public static void main(String[] args) {Thread t1=new Thread(new Test(true));Thread t2=new Thread(new Test(false));t1.start();t2.start();}}class Test implements Runnable{ private boolean flag; Test11(boolean flag) { this.flag=flag; } public void run() { if(flag) { while(true) { synchronized(MyLock.locka) { System.out.println("if locka"); synchronized(MyLock.lockb) { System.out.println("if lockb"); } } } } else{ while(true) { synchronized(MyLock.lockb) { System.out.println("else lockb"); synchronized(MyLock.locka) { System.out.println("else locka"); } } } }}}class MyLock{ static Object locka=new Object(); static Object lockb=new Object();}
惊呆了吧,实际上死锁就是同步中嵌套同步,而锁不同。
毕姥爷还悄悄告诉我们:除了面试要搞死锁,我们平时要尽量避免死锁。
懒汉式
class Single{ private static Single s = null; private Single(){} public static Single getInstance() { if(s==null) { synchronized(Single.class) { if(s==null) //--->A; s= new Single(); } } return s; }} class SingleDemo{ public static void main(String[] args) { Single ss=Single.getInstance(); }}
线程间通讯
其实就是多个线程在操作同一个资源,但是操作的动作不同。
生产者--消费者示例
public class ProducerConsumerDemo {/**多生产者消费者的情况 * 当出现多个线程时,要用while标识和notifyAll * @param args */public static void main(String[] args) {Resource r=new Resource();Produce pro=new Produce(r);Consumer con=new Consumer(r); Thread t1=new Thread(pro); Thread t2=new Thread(pro); Thread t3=new Thread(con); Thread t4=new Thread(con); t1.start(); t2.start(); t3.start(); t4.start();}}class Resource{ private String name; private int count=1; private boolean flag=false; public synchronized void set(String name) { while(flag)//用了while出现了全部等待try {wait();} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();} this.name=name+"------"+count++; System.out.println(Thread.currentThread().getName()+"...生产者.."+this.name); flag=true; this.notifyAll(); } public synchronized void out() { while(!flag) try {wait();} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();} System.out.println(Thread.currentThread().getName()+"...消费者.."+this.name); flag=false; this.notifyAll(); }}class Produce implements Runnable{ private Resource res; Produce(Resource res) { this.res=res; } public void run() { while(true) { res.set("商品++"); } }}class Consumer implements Runnable{ private Resource res; Consumer(Resource res) { this.res=res; } public void run() { while(true) { res.out(); }}}
wait(),notify(),notifyall();都是用在同步中,因为要对持有监视器(锁)的线程操作。所以要使用在同步中,因为只有同步才具有锁。
JDK1.5新特性
黑马能给我我需要的,所以,黑马,我来了!
——爱编程,爱黑马,我是快乐的小32
---------------------- android培训、.java培训、期待与您交流! ----------------------
详细请查看:http://edu.csdn.net
- 黑马程序员——四、java基础之多线程(7)
- 黑马程序员—JAVA基础之多线程
- 黑马程序员——Java基础(九)之多线程
- 黑马程序员————Java基础之多线程
- 黑马程序员————Java基础之多线程
- 黑马程序员——java基础拾遗之多线程(二) 线程同步、线程通信
- 黑马程序员——java基础之多线程基本概念
- 黑马程序员——java基础之多线程
- 黑马程序员——JAVA基础之多线程的安全问题
- 黑马程序员——Java基础之多线程
- 黑马程序员——java基础之多线程
- 黑马程序员——Java基础之多线程
- 黑马程序员——java基础之多线程
- 黑马程序员——java基础之多线程
- 黑马程序员—Java基础学习笔记之多线程
- 黑马程序员—【Java基础篇】之多线程
- 黑马程序员——Java之多线程
- 黑马程序员——JAVA之多线程
- u3 脚本
- 盒子游戏(The Seventh Hunan Collegiate Programming Contest)
- 用堆排序实现查找最小的K个元素
- Quartz 2D编程指南(1) - 概览
- 冒泡排序(C++)
- 黑马程序员——四、java基础之多线程(7)
- request/response总结
- LeetCode - Search a 2D Matrix
- UDID替代方案
- QT中QVector的使用
- Intent and Intent Filters
- railway stack
- 大连东软实训报告书
- Starling使用总结