首页 > 代码库 > 黑马程序员——java多线程基础知识1
黑马程序员——java多线程基础知识1
多线程 |
进程是一个正在执行的程序。
cpu在同时执行这些程序,其实是跳跃式的,做快速的切换,时间很短。一个进程可能存在多条路径。迅雷的多路径。每一个进行执行都有一个执行顺序,该顺序是一个执行路径,或这叫一个控制单元。每一个进程至少有一个线程,线程就是进程中的一个独立的控制单元,线程控制进程的执行。jvm启动的时候会有一个进程就叫做java,exe,该进程中至少有一个线程在控制Java程序的执行 ,而且该线程的执行代码在 主函数中。该线程称为主线程。虚拟机至少也有两个线程,一个主线程执行,另一个负责垃圾回收的线程。生活中常见的下载就是多线程的。
存在的意义,能产生同时运行多段代码。
继承thread类
如何自定义去创建线程去执行代码?线程已经被Java封装起来了,会调用windows来创建线程。
thread:允许程序并发地执行多个线程。成为thread的子类,并且重写run(runable对象。)
创建线程的第一种方式:继承thread类
1.建立好一个对象就建立好一个线程
1.定义类执行thread
2.复写run方法。
3.调用线程的start方法,这个方法。 示例中发现:创建并启动一个线程,start。run方法不具备调用线程的能力,是start方法,启动线程,调用run方法。
病毒一直在抢劫cpu资源。cpu切换随机的。内存是瓶颈。
发现运行结果每次都不同,因为每个线程都在获取cpu的执行权,cpu执行到谁,谁就运行。cpu在做着快速的切换已达到看上去是同时运行的结果,这就是多线程的一个特性,随机性谁抢到谁执行,至于执行多长,cpu说了算。
为什么要覆盖run方法。
thread用于描述线程。该类就定义了一个功能,用于存储线程要运行的代码,该存储功能就是run方法,run方法用于存储线程运行的代码,主线程放在main里面。你开启线程的意义就是为了执行你指定的代码。复写run方法的目的就让那个自定义的代码存储在run方法中,让进程运行。不能d.run,调用的run方法,主线程执行run,start没有开启线程。start开启线程,执行该线程的run方法,run就是一般调用方法,还是单线程的。
线程的运行状态。
被创建(调用windows)start开始运行,冻结(睡眠时间)时间结束回到运行,wait()等待。notify唤醒功能。任务管理器的结束进程,才结束。放弃执行资格。消亡:stop和run方法结束。阻塞状态:创建了不一定就马上能执行,要等待cpu来执行,具备运行资格,但没有执行权。冻结状态也是回到阻塞状态。
获取线程对象以及名称
线程都有自己默认的名称,编号从0开始。(用super改写名称)currentthread()获取当前线程对象
getname()获取线程名称。
局部变量在每一个线程中都有自己独立的空间。
卖票程序
同时出票。
总共只有100张票,你创建了4个,卖了400张。。。用static可以,但是生命周期太长。t1start4次,会出现进程运行。跑圈,前面每跑一圈bang一枪。你已经到了运行状态,再运行没有意义。这也是不ok的,在运行的线程不需要开启。因为没有方法了,因为继承Thread类的run方法存放在类Ticket中
实现runable类。必须实现无参run方法。
实现runnable,实现run
调用:
ticket t=new t不是线程newthread()才是线程
在创建线程对象时就要明确运行那段代码。
package Second; class Ticket extends Thread{ int tickets; public Ticket(int ticket) { super(); this.tickets = ticket; } @Override public void run(){ while(tickets>0){ tickets--; try { Thread.sleep(3); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+">>>"+tickets); } } } public class TicketTest{ public static void main(String[] args) { Ticket ticket=new Ticket(100); ticket.start();//由于是继承thread,所以ticket对象本身有start方法 Thread t1=new Thread(ticket);//这两个是通过创建父类对象调用子类方法的多态现象 Thread t2=new Thread(ticket); t1.start(); t2.start(); } }//打印结果 /* Thread-0>>>10 Thread-2>>>10 Thread-1>>>8 Thread-0>>>7 Thread-2>>>6 Thread-1>>>5 Thread-2>>>4 Thread-0>>>4 Thread-1>>>2 Thread-0>>>1 Thread-2>>>0 Thread-1>>>0 Thread-0>>>0 */
创建线程的第二种方式实现thread
thread里可以传ranable的对象,他有这样一个构造函数。
实现runable接口步骤:
1.定义类实现runable接口
2.覆盖runable里run方法
3.通过thread类创建线程对象。
4.ranable接口的子类对象作为实际参数传递给threa类的构造函数
因为自定义的run方法所属对象是runable接口的子类对象,所以要让线程去执行制定对象的run方法。就必须明确该run方法所属对象。
5.开启start并调用runnable的子类对象run;
class Ticket2 implements Runnable{ int tickets; public Ticket2(int ticket) { super(); this.tickets = ticket; } @Override public void run(){ while(tickets>0){ tickets--; try { Thread.sleep(3); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+">>>"+tickets); } } } public class TicketTest2{ public static void main(String[] args) { Ticket2 ticket=new Ticket2(100); //ticket.start();由于是实现Runnable,此时Ticket不在拥有start的方法。 Thread t1=new Thread(ticket);//这两个是通过创建父类对象调用子类方法的多态现象 Thread t2=new Thread(ticket); Thread t3=new Thread(ticket); t1.start(); t2.start(); t3.start(); } }//打印结果 /*Thread-2>>>9 Thread-1>>>9 Thread-0>>>8 Thread-1>>>6 Thread-0>>>6 Thread-2>>>5 Thread-0>>>3 Thread-2>>>2 Thread-1>>>1 Thread-2>>>0 Thread-1>>>0 Thread-0>>>0*/
实现方式和继承方式的区别:
单继承。方框里的代码需要多线程去执行。学生类继承了person,有了父类,但是你要多线程执行,怎么办。我可以给你提供方法,但是你要符合我的规则。学生是人的一种,学生象runnable
实现方式的好处是避免了但继承的局限性,在定义线程时用实现的方式,而且这方式也可以共享t对象。。。。runnable。
;两种方式最大的不同就是线程代码存放位置的不同,继承存放在thread子类的run方法中,实现时,线程代码存放在接口的子类的run方法中。
安全问题
就是那个判断了,没执行的问题。多语句的时候,同时又操作了共享数据时,在上面的卖票程序中,我们可以看到,结果出现了不该出现的0号票。
写多进程的时候一定要小心安全问题,否则有时测试不出来,但是中病毒了呢,问题的原因在于多条语句在在操作同一个线程的共享数据时,一个线程对多条语句只执行了一部分还没有执行完,另一条线程参与进来执行,导致共享数据的错误。
解决办法
同步代码块
synchronization哪些要同步呢?操作共分享数据的语句。
同步代码块的原理:同步锁。。某段时间类只能由一条线程,直到这段代码执完毕。持有锁的线程可以在同步中执行,没有持有所得线程即使获取cpu的执行权也进不去,因为没有获取锁。火车上的卫生间
同步书写的前提:
1.必须要有两个或者连着以上的线程
2.必须是多个线程 使用同一个锁,才能叫同步。必须保证同一段代码只有一个线程运行。
好处解决了多线的安全问题
弊端:多了一个判断是否上锁,消耗了资源,是在允许消耗的范围内。
同步函数。
1.明确哪些代码是多线程运行代码
2.明确共享数据
3.明确多线程运行代码中那些语句是操作共享数据的。
同步封装代码和函数封装的区别:带有同步性与否。
把synchronized,作为修饰符放在函数上。两种表现形式同步代码块和同步函数。
同步函数的锁是this
怎么只有0线程在执行?因为循环锁住了。别人进不来。。。没搞清楚哪些要同步哪些不用,拿出来,调用show就好了。同步函数用得是哪一个锁呢?函数需要被对象调用。怎么验证这结论:使用两个线程来卖票,一个线程在同步代码块中,一个同步函数,都在执行卖票工作为什么都在run里执行,不执行code?哦,主函数太快了,让主函数睡一会儿。还是出现了错票0 。不安全。两个锁不一样。
静态同步函数的是class对象。静态进内存时,内存中没有本类对象,但是一定有该类对应的字节码文件对象,Ticket.class。静态的同步方法使用的锁是所在类的字节码文件对象。类名.class。懒汉式加了锁会比较低效,因为每一个都要判断用双重否定。
懒汉式
死锁:同步中嵌套同步而锁却不同。尽量避免死锁。
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。