黑马程序员Java基础__多线程
来源:互联网 发布:bt天堂新域名 编辑:程序博客网 时间:2024/04/28 17:38
一、多线程
一、线程和进程的区别:
进程:正在进行中的程序。其实进程就是一个应用程序运行时的内存分配空间。
线程:其实就是进程中一个程序执行控制单元,一条执行路径。进程负责的是应用程序的空间的标示。线程负责的是应用程序的执行顺序。
二、自定义线程:
线程有如此的好处,那要如何才能通过代码自定义一个线程呢?其实,线程是通过系统创建和分配的,java是不能独立创建线程的;但是,java是可以通过调用系统,来实现对进程的创建和分配的。java作为一种面向对象的编程语言,是可以将任何事物描述为对象,从而进行操作的,进程也不例外。我们通过查阅API文档,知道java提供了对线程这类事物的描述,即Thread类。创建新执行线程有两种方法:
一)创建线程方式一:继承Thread类。
1、步骤:
第一、定义类继承Thread。
第二、复写Thread类中的run方法。
第三、调用线程的start方法。分配并启动该子类的实例。
start方法的作用:启动线程,并调用run方法。
示例1:
class Demo extends Thread {public void run() {for (int i = 0; i < 60; i++)System.out.println(Thread.currentThread().getName() + "demo run---"+ i);}}class Test2 {public static void main(String[] args) {Demo d1 = new Demo();// 创建一个对象就创建好了一个线程Demo d2 = new Demo();d1.start();// 开启线程并执行run方法d2.start();for (int i = 0; i < 60; i++)System.out.println("Hello World!---" + i);}}
二)创建线程方式二:实现Runnable接口
1、步骤:
第一、定义类实现Runnable接口。
第二、覆盖Runnable接口中的run方法。
第三、通过Thread类建立线程对象。要运行几个线程,就创建几个对象。
第四、将Runnable接口的子类对象作为参数传递给Thread类的构造函数。
第五、调用Thread类的start方法开启线程,并调用Runnable接口子类的run方法。
示例2:
//多个窗口同时卖票
class Ticket implements Runnable {private int tic = 20;public void run() {while (true) {if (tic > 0)System.out.println(Thread.currentThread().getName() + "sale:"+ tic--);}}}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();}}
三)两种方式区别:
继承Thread:线程代码存放Thread子类run方法中。
实现Runnable:线程代码存在接口的子类的run方法。
四)为什么要覆盖run方法呢?
1)Thread类用于描述线程。该类定义了一个功能:用于存储线程要运行的代码,该存储功能即为run方法。也就是说,Thread类中的run方法用于存储线程要运行的代码,就如同main方法存放的代码一样。
2)复写run的目的:将自定义代码存储在run方法中,让线程运行要执行的代码。直接调用run,就是对象在调用方法。调用start(),开启线程并执行该线程的run方法。如果直接调用run方法,只是将线程创建了,但未运行。
三、多线程的安全问题:
发现一个线程在执行多条语句时,并运算同一个数据时,在执行过程中,其他线程参与进来,并操作了这个数据。导致到了错误数据的产生。
涉及到两个因素:
1,多个线程在操作共享数据。
2,有多条语句对共享数据进行运算。
原因:这多条语句,在某一个时刻被一个线程执行时,还没有执行完,就被其他线程执行了。
解决安全问题的原理:
只要将操作共享数据的语句在某一时段让一个线程执行完,在执行过程中,其他线程不能进来执行就可以解决这个问题。
四、关于线程同步问题:
一)同步的好处及弊端:
好处:解决了线程安全问题。
弊端:相对降低性能,因为判断锁需要消耗资源,产生了死锁。
二)定义同步的前提:
1,必须要有两个或者两个以上的线程,才需要同步。
2,多个线程必须保证使用的是同一个锁。
示例3:
{ private int tic = 100; Object obj = new Object(); public void run() { while(true) { synchronized(obj)//任意的一个对象 { //此两句为共享语句 if (tic > 0) System.out.println(Thread.currentThread().getName() + "sale:" + tic--); } } } } class TicketDemo { public static void main(String[] args) { Ticket t = new Ticket(); Thread t1 = new Thread(t,"1");//创建第一个线程 Thread t2 = new Thread(t,"2");//创建第二个线程 //开启线程 t1.start(); t2.start(); } }
三)同步代码块和的同步函数的区别:
同步代码块使用的锁可以是任意对象。
同步函数使用的锁是this,静态同步函数的锁是该类的字节码文件对象。
在一个类中只有一个同步,可以使用同步函数。如果有多同步,必须使用同步代码块,来确定不同的锁。所以同步代码块相对灵活一些。
五、等待唤醒机制:
一)涉及的方法:
wait:将同步中的线程处于冻结状态。释放了执行权,释放了资格。同时将线程对象存储到线程池中。
notify:唤醒线程池中某一个等待线程。
notifyAll:唤醒的是线程池中的所有线程。
二)注意事项:
1:这些方法都需要定义在同步中。
2:因为这些方法必须要标示所属的锁。
你要知道 A锁上的线程被wait了,那这个线程就相当于处于A锁的线程池中,只能A锁的notify唤醒。
3:这三个方法都定义在Object类中。为什么操作线程的方法定义在Object类中?
因为这三个方法都需要定义同步内,并标示所属的同步锁,既然被锁调用,而锁又可以是任意对象,那么能被任意对象调用的方法一定定义在Object类中。
六、关于多线程单利设计模式:
懒汉式的特点在于延时加载,但是在多线程访问的时候会出现安全问题。
解决问题:加同步(同步函数,和同步代码块都行)来解决但是有些低效, 用双重判断的方法解决效率的问题。
class Single { private Single(){}; private Single s=null; public static Singtle getInstance() { if(s==null)//使用双重判断的形式. { synchronized(Single.class)//锁是该类所属的字节码文件对象 { if(s==null) s=new Single(); } } return s; } }
---------------------- ASP.Net+Android+IO开发S、.Net培训、期待与您交流! ----------------------
- 黑马程序员Java基础__多线程
- 黑马程序员——Java基础__多线程(上)
- 黑马程序员——Java基础__多线程(下)
- 黑马程序员__Java基础__多线程
- 黑马程序员__JAVA基础__多线程
- 黑马程序员__java基础__多线程
- 黑马程序员__多线程
- 黑马程序员__多线程
- 黑马程序员__多线程
- 黑马程序员__多线程
- 黑马程序员__多线程
- 黑马程序员__多线程
- 黑马程序员Java基础__面向对象
- 黑马程序员Java基础__集合(上)
- 黑马程序员Java基础__集合(下)
- 黑马程序员Java基础__其他对象
- 黑马程序员Java基础__网络编程
- 黑马程序员__多线程1
- 优先队列--poj2431
- android 简析自定义布局、布局的执行流程
- 页面与后台代码传值方法
- 方位传感器(重力小球)
- Instruments 使用
- 黑马程序员Java基础__多线程
- 磁场传感器(指北针)
- 父子进程同步的功臣——wait
- 通过地址重写访问动态Web
- 计算机视觉领域的一些牛人博客,超有实力的研究机构等的网站链接
- UVA - 10404 Bachet's Game
- 【MySQL】关于数据库连接超时
- C/C++ 排列组合问题(递归)
- 进程控制地字第1号系统调用——exec