黑马程序员 知识点总结-Java多线程
来源:互联网 发布:centos7 iso yum源 编辑:程序博客网 时间:2024/05/16 15:51
----------------------Android培训、Java培训、期待与您交流! ----------------------
进程:
是一个正在运行的程序,比如正在运行的迅雷,QQ等。
线程:
每个进程执行都有一个执行顺序,该顺序是一个执行路径,或者叫做一个控制单元,线程就是进程中的一个独立的控制单元;线程控制着进程的运行;一个进程中至少有一个线程。Java虚拟机允许应用程序并发地运行多个执行线程。
比如:JVM启动时会运行一个进程java.exe该进程中至少有一个线程负责java程序的执行,并且这个线程运行的代码存在于main方法中,该线程成为主线程。除了主线程外,还有负责垃圾回收机制的进程。
class Test {public static void main(String[] args) // 主线程{for (int i = 0; i < 4000; i++) {System.out.println("Hello World!");}}}
创建线程
创建新执行线程有两种方法:
方法一:将类声明为Thread
的子类。该子类应重写Thread
类的run
方法。接下来可以分配并启动该子类的实例。
class Demo extends Thread // 定义子类继承Thread{public void run() // 复写run()方法,存储自定义代码{for (int i = 0; i < 30; i++) {System.out.println("Demo run" + i);}}}class ThreadDemo {public static void main(String[] arge) {Demo d = new Demo(); // 创建子类对象,创建一个线程d.start(); // 调用start()方法:启动线程;调用run()方法d.run(); // 仅仅是对象调用方法,没有开启新的线程for (int i = 0; i < 30; i++) {System.out.println("Hello World" + i);}}}
方法二:声明实现Runnable
接口的类。该类然后实现run
方法。然后可以分配该类的实例,在创建Thread
时作为一个参数来传递并启动。
定义类实现Runnable接口
覆盖Runnable接口中run()方法,将线程要运行的代码存放在该run方法中
通过Thread类建立线程对象
将Runnable接口的子类对象作为实际参数传递给Thread类的构造函数;因为自定义的run()方法所属对象是Runnable接口的子类对象,而又要线程去执行指定对象的run()方法,所以要明确该run()方法所属的对象
调用Thread类的start()方法开启线程并调用Runnable接口子类的run()方法
//定义类实现Runnable接口class Ticket implements Runnable {private int ticket = 100;public void run() { // 覆盖Runnable接口中的run()方法while (true) {if (ticket > 0) {System.out.println(Thread.currentThread().getName()+ " sale..." + ticket--);}}}}public class Demo {public static void main(String[] args) {// TODO Auto-generated method stubTicket 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()方法中。Thread子类无法再从其它类继承(java语言单继承);编写简
单,run()方法的当前对象就是线程对象,可直接操作。
第二种方式:使用Runnable接口创建线程,线程代码存放在Runnable接口
实现的子类的run()方法中。可以将CPU,代码和数据分开,形成清晰的模
型;线程体run()方法所在的类可以从其它类中继承一些有用的属性和方法;
有利于保持程序的设计风格一致
在实际应用中,几乎都采用第二种方式,第二种能避免单继承的局限性
*从运行的结果中可以发现:
发现运行结果每次都不一样,因为多个线程都在获取cpu的执行使用权,cpu
执行到谁,谁就运行。在某一时刻,cpu(多核除外)只能有一个程序在运
行,由于cpu在做着快速的切换,一大到看上去是在同时运行的效果。可以
得到多线程的一个特性:随机性。
为什么要覆盖run()方法呢?
Run()方法:用于存储要运行的代码,
Start()方法:用于开启线程
线程的四种状态
获取线程对象及名称
线程都有自己默认的名称名称的格式:Thread-编号编号从0开始
1、currentThread():静态方法,用于获取当前正在执行的线程对象的引用
2、GetName():用户与获取当前线程对象的名称
3、currentThread().getName()和this.getName()作用相同,都用于获取当前线程对象的名称
4、setName():用于给当前线程对象设置名称或者调用Thread类的构造方法设置当前线程对象的名称
class Demo extends Thread {public Demo(String name) {super(name);}public void run() {for (int i = 0; i < 10; i++) {// 通过调用this.getName()方法获取该线程对象的名称System.out.println(this.getName() + " run…" + i);// 通过静态方法currentThread()方法获取当前线程对象// 通过currentThread.getName()方法获取当前线程对象名称System.out.println(Thread.currentThread().getName() + " run…" + i);}}}class ThreadTest {public static void main(String[] args) {Demo t1 = new Demo("线程1");Demo t2 = new Demo("线程2");t1.start();t2.start();}}
多线程的安全问题
class Ticket implements Runnable {private int ticket = 100;public void run() { // 覆盖Runnable接口中的run()方法while (true) {if (ticket > 0) {try {Thread.sleep(10);} catch (Exception e) {}System.out.println(Thread.currentThread().getName()+ " sale..." + ticket--);}}}}public class Demo {public static void main(String[] args) {// TODO Auto-generated method stubTicket 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();}}//运行结果出现了0,-1,-2等的错票,多线程出现了安全问题
问题原因:
当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分,还没有执行完,另一个线程参与进来执行,导致共享数据错误。
解决办法:
对多条操作共享数据的语句,只能让一个线程执行完,在执行过程中,其
他线程不可以参与执行。
Java对于多线程的安全问题提供了专业的解决方案:同步代码快
Synchronized(对象){
需要同步的代码
}
代码应修改为:
class Ticket implements Runnable {private int ticket = 100;public void run() { // 覆盖Runnable接口中的run()方法Object obj = new Object();while (true) {synchronized (obj) {if (ticket > 0) {try {Thread.sleep(10);} catch (Exception e) {}System.out.println(Thread.currentThread().getName()+ " sale..." + ticket--);}}}}}
多线程同步代码块
Synchronized(对象){
需要同步的代码
}
对象如同锁,持有锁的线程可以在同步中执行,没有持有锁的线程即使拥有cpu
的执行权,也进不去,因为没有持有锁。
同步的前提:
必须有两个或有两个以上的线程
必须是多个线程使用同一个锁
好处:解决了多线程的安全问题
弊端:多个线程需要判断锁,较为消耗资源
多线程同步函数
Public synchronized 返回值类型方法阿明(){方法体}
class Bank {private int sum;public synchronized void add(int n) {sum = sum + n;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 c = new Cus();Thread t1 = new Thread(c);Thread t2 = new Thread(c);t1.start();t2.start();}}
同步函数用的锁是this
因为函数需要被对象调用,那么函数都有一个所属的对象引用,就是this
验证同步函数用的锁是this:
思路:使用两个线程来买票,一个线程在执行同步代码快中所用的锁是obj,一个线程在执行同步函数中,两个线程都在执行买票动作。
class Ticket implements Runnable {private int tick = 100;Object obj = new Object();public Boolean f = true; // 用于区分第一个线程执行同步代码块,第二// 个线程在执行同步函数public void run() {if (f) {while (true) {synchronized (obj) {if (tick > 0) {try {Thread.sleep(10);} catch (Exception e) {}System.out.println(Thread.currentThread().getName()+ "…code " + tick--);}}}} else {while (true)show();}}public synchronized void show() {if (tick > 0) {try {Thread.sleep(10);} catch (Exception e) {}System.out.println(Thread.currentThread().getName() + "…show…"+ tick--);}}}class ThisLockDemo {public static void main(String[] args) {Ticket t = new Ticket();Thread t1 = new Thread(t);Thread t2 = new Thread(t);t1.start();try {Thread.sleep(10);} catch (Exception e) {}t.f = false;t2.start();}}
运行结果出现了0号票,说明程序不安全,两个线程所用的锁不是同一个锁
而把同步代码块中对象参数改为’this’,则运行结果没有出现不符合的票,说明
两个线程用的是同一个锁,所以同步函数所用的锁是this
静态同步函数所用的锁是该方法所在类字节码文件对象,即类名.class
如果把上面代码中的同步函数用static修饰,则运行结果又会出现0号票,说
明静态同步函数所用的锁不是this,而把同步代码块中的对象参数改
为’Ticket.class’后,运行结果没有出现不合法的票,所以说明两个线程用的锁是
同一个锁,所以静态同步函数所用的锁是本类类名.class
----------------------Android培训、Java培训、期待与您交流! ----------------------
- 黑马程序员 知识点总结-Java多线程
- 黑马程序员--多线程知识点总结
- 黑马程序员,黑马论坛-----多线程知识点总结
- 黑马程序员 Java多线程总结
- 黑马程序员---java多线程总结
- 黑马程序员-----java多线程总结*
- 黑马程序员---------java多线程总结
- 黑马程序员 java多线程总结
- 黑马程序员 知识点总结-Java内部类
- 黑马程序员 知识点总结-Java异常
- 黑马程序员 知识点总结-Java多态
- 黑马程序员 知识点总结-Java继承
- 黑马程序员 知识点总结-Java泛型
- 黑马程序员 知识点总结-Java IO(一)
- 黑马程序员 知识点总结-Java GUI
- 黑马程序员 知识点总结-Java网络编程
- 黑马程序员 知识点总结-Java正则表达式
- 黑马程序员 知识点总结-Java反射
- HACMP步骤
- 驱动程序如何发通知给应用程序
- AUPE学习第三章------文件I/O2
- 数据仓库的难点和痛点
- 我自己好。
- 黑马程序员 知识点总结-Java多线程
- map
- struts2中使用EL表达式配合sturts2标签显示数据问题
- CString String
- 图像处理和计算机视觉中的经典论文
- linux 中 shell 简介
- wireshark的使用 转载
- 开篇之谈
- 再谈HashMap-由一个实际问题引发的对HashMap设计吐嘈