黑马程序员_Java基础——多线程(第2篇)
来源:互联网 发布:宣传图片制作软件 编辑:程序博客网 时间:2024/05/16 17:06
---------------------- ASP.Net+Unity开发、.Net培训、期待与您交流! ----------------------
一、引言
初学者刚接触多线程时往往觉得多线程技术多么的高深难懂,就如古代两军对垒不战而退,还没开始学呢,首先心理心理上就处于了弱势。其实完全没必要背着这种心理负担去学习。就如同忽悠人一样,许多高科技的东西就喜欢去起专业的名词唬人,接触之后才知道完全不是那么回事。多线程技术其实也是一回事,了解了也就觉得就那么回事儿。那么什么是多线程呢?了解多线程首先要了解什么是进程!关于进程,简单来说就是正在运行的程序。而一个线程就是进程中的一个独立的控制单元,其控制着进程的的执行,因而一个进程中至少有一个线程。而所谓多线程自然就是多个线程在一个进程中同时执行了,很明显这样做可以提高生产效率。
二、多线程实现技术分析
那么在Java中如何让多个线程同时运行呢?Java提供了两种创建多线程的解决方案。其一是继承Thread类,其二是实现Runnable接口。下面我们具体的来看看这两种方式的具体实现以及区别。
--继承Thread类
步骤:1.继承Thread类;2.覆写Thread类中的run()方法;3.调用线程的start()方法执行run()方法
小例子:有自定义线程与主线程两个线程,观看执行结果
class DuoXianCheng extends Thread{public void run(){for(int i = 0; i < 60; i++)System.out.println("run..." + i );}}class ThreadDemo{public static void main(String[] args){DuoXianCheng d = new DuoXianCheng();d.start();for(int i = 0; i < 60; i++){System.out.println("hello word" + i);}}}我们从执行结果中不难发现自定义线程与主线程在交替执行,而且每一次的执行结果都是不一样的。这是因为多个线程都在获取cpu的执行权,cpu执行到谁,谁就运行。明确一点,在某一个时刻只能有一个线程在运行(多核除外),cpu在做着快速的切换,以达到看上去同时执行的效果。我们可以形象的把多线程的运行行为看成是在互相的抢夺cpu的执行权。这也就是多线程的特点之一,随机性,谁抢到谁执行,至于执行的多长的时间,则由cpu决定。
那么为什么要覆盖run()方法呢?我们可以认为Thread类用于描述线程,该类就定义了一个功能,用于存储线程要运行的代码,该存储功能就在run方法。简单来说就是,线程要运行的代码就放在run方法中,这就好比主线程的代码放在main方法中。
--实现Runable接口
步骤:1.定义类实现Runable接口;2.覆盖Runable接口中的run方法,将线程要运行的代码放在存放在改run方法中;3.通过Thread类建立线程对象;4.将Runnable接口的子类对象作为参数传递给Thrad类的构造函数;5.调用Thread类对象的start()方法开启线程并调用Runnable接口中的run()方法
这里是不是有个疑问?为什么要把Runnable接口的子类对象作为参数传递给Thread类的构造函数?因为自定义的run()方法所属的对象是Runnable接口的子类对象,所以要让线程去执行指定对象的run()方法,就必须要明确你所要执行的代码所在的run()方法的所属对象。
那么这两种实现方式有什么区别呢?
--避免的了单继承的局限性,因为java中只能单继承,不允许多继承,所以一个对象要是有父类,那么就不能通过这种继承的方式来实现。所以java提供了另一种方式来实现,虽然不允许多继承,但是可以实现接口,在平时的编程中建议使用实现接口的方式
--线程代码存放的位置不同
三、多线程的安全问题
当多个线程在操作同一个共享数据时,一个线程对多条语句子执行了一部分,还没有执行完,这时另一个线程参与进来,导致共享数据错乱。这就导致了多线程的安全问题。
如下例子,可能会出现打印0号票的情况,这就是典型的多线程安全问题。
/*
需求:简单的买票程序
多个窗口同事买票
*/
class Ticket implements Runnable { private int ticket = 100; public void run() { while(true) { if(ticket > 0) { //如果线程0,1,2,3都在这儿趴下了,那么就会出现线程的安全问题 //可以用try{Thread.sleep(10);}catch(Exception e){}来模拟 //sleep()抛出InterruptedException异常 //这里为什么不能抛出这个异常呢?因为run方法时从Runnable接口中覆写来的,而Runnable中的run方法 //没有抛出异常,所以这里绝对不能抛,只能处理 System.out.println(Thread.currentThread().getName() + "..." + ticket--); } } } } class ThreadTicketDemo { 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(); } }那么如何解决这样的多线程的安全问题呢。Java中对于多线程的安全问题提供了专业的解决方案,那就是synchronized关键字——同步。同步有两种形式,一是同步代码块,而是同步函数。
synchronized(对象) { //需要被同步的代码 }这里的对象如同锁,持有锁的线程可以在同步中执行,没有持有锁的线程即使获得了cpu的执行权也没有办法进去执行代码,因为没有获取锁。
//下面存钱的例子,同步函数解决多线程的安全问题
/*
需求:银行有个金库
有两个用户分别往里面存300块,每次存100,分三次
*/
class Bank { private int sum; Object obj = new Object(); public synchronized void add(int n) { //synchronized(obj) //{ sum = sum + n; try{Thread.sleep(20);} catch(Exception e){} //如果线程1和线程0都在这边趴了那么就出问题了 //同步一下就可以了 //也可以在函数上加同步,变成同步函数 System.out.println("sum = " + sum); //} } } class Cus implements Runnable { private Bank b = new Bank(); public void run() { for(int i = 0; i < 3; i++) { b.add(100); } } } class BankDemo { public static void main(String[] args) { Cus cus = new Cus(); Thread t1 = new Thread(cus); Thread t2 = new Thread(cus); t1.start(); t2.start(); } }
但是同步也是要有条件的,对于同步的前提:1.必须有两个或两个以上的线程;2.必须多个线程使用同一把锁,必须保证同步中只能有一个线程在执行。
至此,同步的好处和弊端也显而易见了,好处便是解决了多线程的安全问题,弊端嘛,自然是需要判断锁,需要消耗资源。
注:同步函数所使用的锁是this,而静态同步函数呢?因为静态同步函数没有本类对象,所以其所使用的锁是该类的字节码文件对象 类名.class
四、单例设计模式(懒汉式-延迟加载)的多线程实现
class Single { private static Single s = null; private Single() { } public static Single getInstance()//这里如果用同步函数的话效率会很低, {//素以一般使用同步代码块儿 if(s == null)//提高效率,使用双层判断 { synchronized(Single.class)//因为是静态的没有this,锁可以使用Single.class { if(s == null) //A-->趴下,B-->趴下 s = new Single(); return s; } } } }
五、死锁
死锁简单来说就是锁相互之间相互等待对方的情况,就用个小例子来说明了!
class Test implements Runnable { private boolean flag; Test(boolean flag) { this.flag = flag; } public void run() { if(flag) { synchronized(DeadLock.obj1) { System.out.println("if obj1"); synchronized(DeadLock.obj2)//互相等待对方 { System.out.println("if obj2"); } } } else { synchronized(DeadLock.obj2) { System.out.println("else obj2"); synchronized(DeadLock.obj1)//互相等待对方 { System.out.println("else obj1"); } } } } } class DeadLock { static Object obj1 = new Object(); static Object obj2 = new Object(); } 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(); } }
---------------------- ASP.Net+Unity开发、.Net培训、期待与您交流! ----------------------
- 黑马程序员_Java基础——多线程(第2篇)
- 黑马程序员_java基础——多线程
- 黑马程序员_java基础——多线程
- 黑马程序员_Java基础_多线程2
- 黑马程序员_Java基础_多线程2
- 黑马程序员_Java基础——集合框架(上)(第3篇)
- 黑马程序员_Java基础——集合框架(下)(第4篇)
- 黑马程序员_Java基础——IO框架(上)(第5篇)
- 黑马程序员_Java基础——IO框架(下)(第6篇)
- 黑马程序员_Java基础——异常(第1篇)
- 黑马程序员_Java基础——网络编程(第7篇)
- 黑马程序员_java基础--多线程
- 黑马程序员_java基础-多线程
- 黑马程序员_java基础--多线程
- 黑马程序员_java基础多线程
- 黑马程序员_Java基础篇(一)——基本知识
- 黑马程序员_Java基础篇(五)——反射
- 黑马程序员_Java基础篇(六)——泛型
- 混合动力汽车 动力性能试验方法
- 电脑系统修复提高电脑一步一步210-646-7355。添加更多的内存
- ny 98 成绩转换
- 又一次的开博,新一次的航程
- 从mybatis中获取数据库的名称
- 黑马程序员_Java基础——多线程(第2篇)
- 3.有一字符串,包含数字与字母,编程去除数字。要求:1、要求在原字符串中操作2、使用指针处理
- js声明数组、对象在jsp页面中(获得ajax得到json数据)
- Spring IoC(控制反转)【三】
- 男人,改变了航空历史
- Stanford Machine Learning: (1). Linear Regression
- c语言经典,高手必备,找出卖耶稣的叛徒问题!
- 数据结构---栈的链表实现
- Android Toast.makeText