黑马程序员--线程
来源:互联网 发布:mysql my.cnf 路径 编辑:程序博客网 时间:2024/06/08 11:31
------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------
一、线程基本知识
进程:正在进行中的程序。其实进程就是一个应用程序运行时的内存分配空间。
线程:其实就是进程中一个程序执行控制单元,一条执行路径。进程负责的是应用程序的空间的标示。线程负责的是应用程序的执行顺序。
多线程:一个进程至少有一个线程在运行,当一个进程中出现多个线程时,就称这个应用程序是多线程应用程序,每个线程在栈区中都有自己的执行空间,自己的方法区、自己的变量。开启多个线程是为了同时运行多部分代码。每一个线程都有自己运行的内容。这个内容可以称为线程要执行的任务。
多线程好处:解决了多部分同时运行的问题。
多线程的弊端:线程太多会效率的降低。
其实应用程序的执行都是cpu在做着快速的切换完成的,这个切换是随机的。
JVM启动时就启动了多个线程,至少有两个线程可以分析的出来。
1,执行main函数的线程,
该线程的任务代码都定义在main函数中。
2,负责垃圾回收的线程。
二、Thread创建线程
创建线程的第一种方式:继承Thread ,由子类复写run方法。
步骤:
1,创建一个新的线程类,继承Thread类并覆盖Thread类的run方法;
class ThreadType extends Thread{
publicvoid run(){
…
}
}
2,创建一个线程类对象,创建方法与一般对象的创建相同,使用关键字new完成;
ThreadType tt = new ThreadType();
3,启动新线程对象,调用start()方法;
tt.start();
4,线程自己调用run()方法。
package ThreadDemo;class ThreadDemo extends Thread{private String name;ThreadDemo(String name) {this.name = name;}public void run() {for (int i = 0; i < 10; i++) {System.out.println(name+"...i="+i+"...ThreaName="+Thread.currentThread().getName());}}}public class CreateThreadDemo {public static void main(String[] args) {ThreadDemo d1 = new ThreadDemo("张三"); //创建线程ThreadDemo d2 = new ThreadDemo("李四"); //创建线程d1.start(); //开启线程,调用run方法d2.start();for (int i = 0; i < 20; i++) {System.out.println("i="+i+"...over..."+Thread.currentThread().getName());}}}
创建线程的第二种方式:实现一个接口Runnable。
利用实现Runnable接口来创建线程的方法可以解决Java语言不支持的多重继承问题。
步骤:
1,创建一个实现Runnable接口的类,并且在这个类中重写run方法。
class ThreadType implement Runnable{
public void run(){
......
}
}
2,使用关键字new新建一个实现了Runnable接口的子类对象。
Runnable rb = new ThreadType();
3,通过Runnable的实例创建一个线程对象,在创建线程对象时,调用的构造函数是new Thread(ThreadType),它用ThreadType中实现的run()方法作为新线程对象的run()方法。
Thread td = new Thread(rb);
4,通过调用ThreadType对象的start()方法启动线程运行。
td.start();
package ThreadDemo;class ThreadType implements Runnable {public void run() {show();}public void show() {for (int i = 0; i < 20; i++) {System.out.println(Thread.currentThread().getName()+"..."+i);}}}public class CreateThreadTest {public static void main(String[] args) {ThreadType d = new ThreadType();Thread t1 = new Thread(d);Thread t2 = new Thread(d);t1.start();t2.start();}}
三、线程的周期
线程的创建仅仅是线程生命周期中的一个内容,线程的整个周期由线程创建、运行、冻结和消亡等部分组成,这些状态之间的转化是通过线程提供的一些方法完成的。
线程状态:
被创建:start()
运行:具备执行资格,同时具备执行权;
冻结:sleep(time),wait()—notify()唤醒;线程释放了执行权,同时释放执行资格;
临时阻塞状态:线程具备cpu的执行资格,没有cpu的执行权;
消亡:stop()
四、线程同步
Java应用程序中的多线程可以共享资源,例如文件、数据库、内存等。当线程以并发模式访问共享数据时,共享数据可能会发生冲突。Java引入线程同步的概念,以实现共享数据的一致性。线程同步机制让多个线程有序的访问共享资源,而不是同时操作共享资源。
1、同步概念
在线程异步模式的情况下,同一时刻有一个线程在修改共享数据,另一个线程在读取共享数据,当修改共享数据的线程没有处理完毕,读取数据的线程肯定会得到错误的结果。如果采用多线程的同步控制机制,当处理共享数据的线程完成处理数据之后,读取线程读取数据。
2、同步的格式
定义同步是有前提的:
1,必须要有两个或者两个以上的线程,才需要同步。
2,多个线程必须保证使用的是同一个锁。
同步代码块
格式:
synchronized(对象) { // 任意对象都可以。这个对象就是锁。
需要被同步的代码;
}
同步函数:其实就是将同步关键字定义在函数上,让函数具备了同步性。
同步函数是用的哪个锁呢?
通过验证,函数都有自己所属的对象this,所以同步函数所使用的锁就是this锁。
当同步函数被static修饰时,这时的同步用的是哪个锁呢?
静态函数在加载时所属于类,这时有可能还没有该类产生的对象,但是该类的字节码文件加载进内存就已经被封装成了对象,这个对象就是该类的字节码文件对象。
所以静态加载时,只有一个对象存在,那么静态同步函数就使用的这个对象。
这个对象就是类名.class
3、同步代码块和同步函数的区别?
同步代码块使用的锁可以是任意对象。
同步函数使用的锁是this,静态同步函数的锁是该类的字节码文件对象。
在一个类中只有一个同步,可以使用同步函数。如果有多同步,必须使用同步代码块,来确定不同的锁。所以同步代码块相对灵活一些。
4、线程应用
package ThreadDemo;/* * 需求:模拟4个线程同时卖100张票 */class Ticket implements Runnable {private int num = 100;public void run() {while (true) {if(num>0){try {Thread.sleep(10);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}System.out.println(Thread.currentThread().getName()+"...sale..."+num--);}}}}public class TicketDemo {public static void main(String[] args) {Ticket t = new Ticket();//创建4个线程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-0通过了if判断后,在执行到“num--”语句之前,num此时仍等于1。CPU切换到Thread-1、Thread-2、Thread-3之后,这些线程依然可以通过if判断,从而执行“num--”的操作,因而出现了0、-1的情况。
采用同步方式修改后class Ticket implements Runnable {private int num = 100;Object obj = new Object();public void run() {while (true) {synchronized (obj) {if (num>0) {System.out.println(Thread.currentThread().getName()+"...sale..."+num--);}}}}}
以单例设计模式中的懒汉式(演示加载方式)为例:
当多线程访问懒汉式时,因为懒汉式的方法内对共性数据进行多条语句的操作。所以容易出现线程安全问题。为了解决,加入同步机制,解决安全问题。但是却带来了效率降低。
为了效率问题,通过双重判断的形式解决。
package ThreadDemo;public class Single {private static Single s = null;private Single() {}public static Single getInstance() {if (s == null) {synchronized (Single.class) {if (s == null) {s = new Single();}}}return s;}}
五、线程间通信
多线程之间可以通过消息通信,已达到相互协作的目的。Java中线程之间的通信是通过Object类中的wait()、notify()、notifyAll()等几种方法实现的。Java中每一个对象内部不仅有一个对象锁之外,还有一个线程等待队列,这个队列用于存放所有等待对象锁的线程。
等待唤醒机制
wait:将同步中的线程处于冻结状态。释放了执行权,释放了资格。同时将线程对象存储到线程池中。
notify:唤醒线程池中某一个等待线程。
notifyAll:唤醒的是线程池中的所有线程。
注意:
1:这些方法都需要定义在同步中。
2:因为这些方法必须要标示所属的锁。
你要知道 A锁上的线程被wait了,那这个线程就相当于处于A锁的线程池中,只能A锁的notify唤醒。
3:这三个方法都定义在Object类中。为什么操作线程的方法定义在Object类中?
因为这三个方法都需要定义同步内,并标示所属的同步锁,既然被锁调用,而锁又可以是任意对象,那么能被任意对象调用的方法一定定义在Object类中。
wait和sleep区别:分析这两个方法:从执行权和锁上来分析:
wait:可以指定时间也可以不指定时间。不指定时间,只能由对应的notify或者notifyAll来唤醒。
sleep:必须指定时间,时间到自动从冻结状态转成运行状态(临时阻塞状态)。
wait:线程会释放执行权,而且线程会释放锁。
Sleep:线程会释放执行权,但不是不释放锁。
六、同步死锁:通常只要将同步进行嵌套,就可以看到现象。同步函数中有同步代码块,同步代码块中还有同步函数。
七、线程Thread 中常用方法
interrupt():中断线程。
setPriority(int newPriority):更改线程的优先级。
getPriority():返回线程的优先级。
toString():返回该线程的字符串表示形式,包括线程名称、优先级和线程组。
Thread.yield():暂停当前正在执行的线程对象,并执行其他线程。
setDaemon(true):将该线程标记为守护线程或用户线程。将该线程标记为守护线程或用户线程。当正在运行的线程都是守护线程时,Java 虚拟机退出。该方法必须在启动线程前调用。
join:临时加入一个线程的时候可以使用join方法。
当A线程执行到了B线程的join方式。A线程处于冻结状态,释放了执行权,B开始执行。A什么时候执行呢?只有当B线程运行结束后,A才从冻结状态恢复运行状态执行。
八、Lock接口
1、JDK1.5以后将同步和锁封装成了对象, 并将操作锁的隐式方式定义到了该对象中, 将隐式动作变成了显示动作。
2、Lock接口: 出现替代了同步代码块或者同步函数, 将同步的隐式操作变成显示锁操作。 同时更为灵活, 可以一个锁上加上多组监视器。
lock(): 获取锁。
unlock(): 释放锁, 为了防止异常出现, 导致锁无法被关闭, 所以锁的 关 闭动作要放在finally中。
3、Condition接口: 出现替代了 Object中的wait、 notify、 notifyAll方法。 将这些监视器方法单独进行了封装,变成Condition监视器对象, 可以任意锁进行组合
Condition接口中的await方法对应于Object中的wait方法。
Condition接口中的signal方法对应于 Object中的notify方法。
Condition接口中的signalAll方法对应于Object中的notifyAll方法
- 黑马程序员之多线程
- 黑马程序员-----线程复习
- “黑马程序员”~线程例题
- 黑马程序员_线程
- 黑马程序员 线程总结
- 黑马程序员:线程池
- 黑马程序员-线程同步
- 黑马程序员-线程入门
- 黑马程序员----线程
- 黑马程序员-线程
- 黑马程序员-----线程
- 黑马程序员:java线程
- 黑马程序员--线程池
- 黑马程序员--C#线程
- 黑马程序员_线程
- 黑马程序员_线程
- 黑马程序员-----线程
- 黑马程序员---深入线程
- newInstance参数详解
- 【poj2229】Sumsets
- UVa 12700 - Banglawash
- Jquery 中两个页面载入后执行的函数的分析区别
- maven TestNg 测试框架 not found
- 黑马程序员--线程
- 正式宣布DXBC2GLSL,HLSL字节码到GLSL的编译器
- metasploit渗透测试笔记(内网渗透篇)
- java面向对象
- hdu5481
- daemon.c
- java面向对象2
- Lesson16 Norm computations
- 黑马程序员——高新技术----枚举及部分JDK新特性