黑马程序员 六、线程技术

Java帮帮-IT资源分享网

 六、黑马程序员—线程技术

第六篇
 

1、进程和线程

进程是指一个内存中运行的应用程序,每个进程都有自己独立的一块内存空间,一个进程中

可以有多个线程。比如在 Windows 系统中,一个运行的 xx.exe 就是一个进程。

Java 程序的进程里有几个线程:主线程, 垃圾回收线程(后台线程)

线程是指进程中的一个执行任务(控制单元),一个进程中可以运行多个线程,多个线程可共享

数据。

多进程:操作系统中同时运行的多个程序;

多线程:在同一个进程中同时运行的多个任务;

一个进程至少有一个线程,为了提高效率,可以在一个进程中开启多个控制单元。

并发运行。如:多线程下载软件。

可以完成同时运行,但是通过程序运行的结果发现,虽然同时运行,但是每一次结果都不一

致。

因为多线程存在一个特性:随机性。

造成的原因:CPU 在瞬间不断切换去处理各个线程而导致的。

可以理解成多个线程在抢 cpu 资源。

我的总结:

多线程下载:此时线程可以理解为下载的通道,一个线程就是一个文件的下载通道,多

线程也就是同时开起好几个下载通道.当服务器提供下载服务时,使用下载者是共享带宽的,

在优先级相同的情况下,总服务器会对总下载线程进行平均分配。不难理解,如果你线程

多的话,那下载的越快。现流行的下载软件都支持多线程。

多线程是为了同步完成多项任务,不是为了提供运行效率,通过提高资源使用效率来提

高系统的效率.

线程是在同一时间需要完成多项任务的时候实现的.

线程与进程的比较

线程具有许多传统进程所具有的特征,故又称为轻型进程(Light—Weight Process)或进程

元;而把传统的进程称为重型进程(Heavy—Weight Process),它相当于只有一个线程的任务。

在引入了线程的操作系统中,通常一个进程都有若干个线程,至少需要一个线程。

进程与线程的区别:

1.进程有独立的进程空间,进程中的数据存放空间(堆空间和栈空间)是独立的。

2.线程的堆空间是共享的,栈空间是独立的,线程消耗的资源也比进程小,相互之间可

以影响的。
 

2、创建线程方式

1、继承 Thread 类

子类覆写父类中的 run 方法,将线程运行的代码存放在 run 中。

建立子类对象的同时线程也被创建。

通过调用 start 方法开启线程。

2、实现 Runnable 接口

子类覆盖接口中的 run 方法。

通过 Thread 类创建线程,并将实现了 Runnable 接口的子类对象作为参数传递给 Thread

类的构造函数。

Thread 类对象调用 start 方法开启线程。

可使用匿名内部类来写

Eg:

package july7;

//线程的两种方法

class MyThread extends Thread{

private String name;

public MyThread(String name) {

super();

this.name = name;

}

public void run(){

System.out.println(name+"启动!");

}

}

class YourThread implements Runnable{

private String name;

public YourThread(String name) {

this.name = name;

}

@Override

public void run() {

for (int i = 0; i < 3; i++) {

System.out.println(Thread.currentThread().getName()+" 第

"+i+"次启动!");

}

}

}

public class Demo1 {

public static void main(String[] args) {

for (int i = 0; i < 100; i++) {

if(i == 50){

new MyThread("刘昭").start();

new Thread(new YourThread(""),"章泽天").start();

}

}

}

}

我的总结:

Thread 类中 run()和 start()方法的区别如下:

run()方法:在本线程内调用该 Runnable 对象的 run()方法,可以重复多次调用;

start()方法:启动一个线程,调用该 Runnable 对象的 run()方法,不能多次启动一个

线程;
 

3、两种进程创建方式比较

A extends Thread:

简单

不能再继承其他类了(Java 单继承)

同份资源不共享

A implements Runnable:(推荐)

多个线程共享一个目标资源,适合多线程处理同一份资源。

该类还可以继承其他类,也可以实现其他接口。

我的总结:

实现方式,因为避免了单继承的局限性,所以创建线程建议使用第二种方式。

Eg:

package july7;

//线程卖票的例子

class SellTicket extends Thread{

private String name;

private int num = 50;

public SellTicket(String name) {

super();

this.name = name;

}

public void run(){

for (int i = 1; i <= num; i++) {

System.out.println(name+"卖出了第"+i+"张票!");

}

}

}

class MySell implements Runnable{

private int num = 50;

@Override

public void run() {

for (int i = 1; i <= num; i++) {

System.out.println(Thread.currentThread().getName()+" 卖

出了第"+i+"张票!");

}

}

}

public class Demo2 {

public static void main(String[] args) throws Exception {

new SellTicket("A").start();

new SellTicket("B").start();

new SellTicket("C").start();

new Thread(new MySell(),"D").start();

new Thread(new MySell(),"E").start();

new Thread(new MySell(),"F").start();

for (int i = 10; i > 0; i--) {

System.out.println(i);

Thread.sleep(1000);

}

}

}

我的总结:

为什么要覆盖 run 方法呢?

Thread 类用于描述线程。该类就定义了一个功能,用于存储线程要运行的代码。该存

储功能就是 run 方法.

也就是说 Thread 类中的 run 方法,用于存储线程要运行的代码。
 

4、线程的生命周期

Thread 类内部有个 public 的枚举 Thread.State,里边将线程的状态分为:

NEW-------新建状态,至今尚未启动的线程处于这种状态。

RUNNABLE-------运行状态,正在 Java 虚拟机中执行的线程处于这种状态。

BLOCKED-------阻塞状态,受阻塞并等待某个监视器锁的线程处于这种状态。

WAITING-------冻结状态,无限期地等待另一个线程来执行某一特定操作的线程处于

这种状态。

TIMED_WAITING-------等待状态,等待另一个线程来执行取决于指定等待时间的操作

的线程处于这种状态。

TERMINATED-------已退出的线程处于这种状态。
 

我的总结:

如何停止线程?

只有一种,run 方法结束。 开启多线程运行,运行代码通常是循环结构。 只要控制

住循环,就可以让 run 方法结束,也就是线程结束。
 

5、控制线程

join 方法:调用 join 方法的线程对象强制运行,该线程强制运行期间,其他线程无法运行,

必须等到该线程结束后其他线程才可以运行。

有人也把这种方式成为联合线程

join 方法的重载方法:

join(long millis):

join(long millis,int nanos):

通常很少使用第三个方法:

程序无须精确到一纳秒;

计算机硬件和操作系统也无法精确到一纳秒;

Eg:

package july7;

class MyThreadDemo implements Runnable{

@Override

public void run() {

for (int i = 0; i < 50; i++) {

System.out.println(Thread.currentThread().getName()+" 正

在运行!"+i);

if(i == 25){

try {

new Thread(new MyThreadDemo(),"刘昭").join();

} catch (InterruptedException e) {

e.printStackTrace();

}

}

}

}

}

public class DemoRe10 {

public static void main(String[] args) {

new Thread(new MyThreadDemo(),"刘昭").start();

new Thread(new MyThreadDemo(),"章泽天").start();

}

}

Daemon

后台线程:处于后台运行,任务是为其他线程提供服务。也称为“守护线程”或“精灵线程”。

JVM 的垃圾回收就是典型的后台线程。

特点:若所有的前台线程都死亡,后台线程自动死亡。

设置后台线程:Thread 对象 setDaemon(true);

setDaemon(true)必须在 start()调用前。否则出现 IllegalThreadStateException 异常;

前台线程创建的线程默认是前台线程;

判断是否是后台线程:使用 Thread 对象的 isDaemon()方法;

并且当且仅当创建线程是后台线程时,新线程才是后台线程。

sleep

线程休眠:

让执行的线程暂停一段时间,进入阻塞状态。

sleep(long milllis) throws InterruptedException:毫秒

sleep(long millis,int nanos)

throws InterruptedException:毫秒,纳秒

调用 sleep()后,在指定时间段之内,该线程不会获得执行的机会。

控制线程之优先级

每个线程都有优先级,优先级的高低只和线程获得执行机会的次数多少有关。

并非线程优先级越高的就一定先执行,哪个线程的先运行取决于 CPU 的调度;

默认情况下 main 线程具有普通的优先级,而它创建的线程也具有普通优先级。

Thread 对象的 setPriority(int x)和 getPriority()来设置和获得优先级。

MAX_PRIORITY : 值是 10

MIN_PRIORITY : 值是 1

NORM_PRIORITY : 值是 5(主方法默认优先级)

yield

线程礼让:

暂停当前正在执行的线程对象,并执行其他线程;

Thread 的静态方法,可以是当前线程暂停,但是不会阻塞该线程,而是进入就绪状态。所

以完全有可能:某个线程调用了 yield()之后,线程调度器又把他调度出来重新执行。

我的总结:用到时查询 api!
 

6、多线程安全问题

导致安全问题的出现的原因:

多个线程访问出现延迟。

线程随机性。

注:线程安全问题在理想状态下,不容易出现,但一旦出现对软件的影响是非常大的。

我们可以通过 Thread.sleep(long time)方法来简单模拟延迟情况。

我的总结:

当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分,还

没有执行完,另一个线程参与进来执行。导致共享数据的错误。

解决办法:

对多条操作共享数据的语句,只能让一个线程都执行完。在执行过程中,其他线程不

可以参与执行。

Eg:在前面的卖票例子上,在每卖票的前面加上模拟延时的语句!

package july7;

class SellDemo implements Runnable{

private int num = 50;

@Override

public void run() {

for (int i = 0; i < 200; i++) {

if(num > 0){

try {

//因为它不可以直接调用getName()方法,所以必须要获取当前线

程。

Thread.sleep(10);

} catch (InterruptedException e) {

e.printStackTrace();

}

System.out.println(Thread.currentThread().getName()+" 卖 出 第

"+num--+"张票!");

}

}

}

}

public class Demo3 {

public static void main(String[] args) {

SellDemo s = new SellDemo();

new Thread(s,"A").start();

new Thread(s,"B").start();

new Thread(s,"C").start();

}

}

输出:这样的话,会出现买了第 0,甚至-1 张票的情况!
 

7、多线程安全问题的解决方法

三种方法:

同步代码块:

synchronized(obj)

{

//obj 表示同步监视器,是同一个同步对象

/**.....

TODO SOMETHING

*/

}

同步方法

格式:

在方法上加上 synchronized 修饰符即可。(一般不直接在 run 方法上加!)

synchronized 返回值类型 方法名(参数列表)

{

/**.....

TODO SOMETHING

*/

}

同步方法的同步监听器其实的是 this

静态方法的同步

同步方法

同步代码块

static 不能和 this 连用

静态方法的默认同步锁是当前方法所在类的.class 对象

同步锁

jkd1.5 后的另一种同步机制:

通过显示定义同步锁对象来实现同步,这种机制,同步锁应该使用 Lock 对象充当。

在实现线程安全控制中,通常使用 ReentrantLock(可重入锁)。使用该对象可以显示地加锁和

解锁。

具有与使用 synchronized 方法和语句所访问的隐式监视器锁相同的一些基本行为和语义,

但功能更强大。

public class X {

private final ReentrantLock lock = new ReentrantLock();

//定义需要保证线程安全的方法

public void m(){

//加锁

lock.lock();

try{

//... method body

}finally{

//在 finally 释放锁

lock.unlock();

}

}

}

修改后的例子:

//同步代码块

package july7;

class SellDemo implements Runnable{

private int num = 50;

@Override

public void run() {

for (int i = 0; i < 200; i++) {

synchronized (this) {

if(num > 0){

try {

//因为它不可以直接调用getName()方法,所以必须要获取当前线

程。

Thread.sleep(10);

} catch (InterruptedException e) {

e.printStackTrace();

}

System.out.println(Thread.currentThread().getName()+" 卖 出 第

"+num--+"张票!");

}

}

}

}

}

public class Demo3 {

public static void main(String[] args) {

SellDemo s = new SellDemo();

new Thread(s,"A").start();

new Thread(s,"B").start();

new Thread(s,"C").start();

}

}

//同步方法

package july7;

//同步方法

class FinalDemo1 implements Runnable {

private int num = 50;

@Override

public void run() {

for (int i = 0; i < 100; i++) {

gen();

}

}

public synchronized void gen() {

for (int i = 0; i < 100; i++) {

if (num > 0) {

try {

Thread.sleep(10);

} catch (InterruptedException e) {

e.printStackTrace();

}

System.out.println(Thread.currentThread().getName() +

"卖出了第"

+ num-- + "张票!");

}

}

}

}

public class Demo6 {

public static void main(String[] args) {

FinalDemo1 f = new FinalDemo1();

new Thread(f, "A").start();

new Thread(f, "B").start();

new Thread(f, "C").start();

}

}

//线程同步锁

package july7;

import java.util.concurrent.locks.ReentrantLock;

//同步锁

class FinalDemo2 implements Runnable {

private int num = 50;

private final ReentrantLock lock = new ReentrantLock();

@Override

public void run() {

for (int i = 0; i < 100; i++) {

gen();

}

}

public void gen() {

lock.lock();

try{

//for (int i = 0; i < 100; i++) {

if (num > 0) {

try {

Thread.sleep(10);

} catch (InterruptedException e) {

e.printStackTrace();

}

System.out.println(Thread.currentThread().getName() + "卖出了第"

+ num-- + "张票!");

}

//}

}finally{

lock.unlock();

}

}

}

public class Demo7 {

public static void main(String[] args) {

FinalDemo2 f = new FinalDemo2();

new Thread(f, "A").start();

new Thread(f, "B").start();

new Thread(f, "C").start();

}

}
 

8、线程通信

有一个数据存储空间,划分为两部分,一部分用于存储人的姓名,另一部分用于存储人的性别;

我们的应用包含两个线程,一个线程不停向数据存储空间添加数据(生产者),另一个线程从数

据空间取出数据(消费者);

因为线程的不确定性,存在于以下两种情况:

若生产者线程刚向存储空间添加了人的姓名还没添加人的性别,CPU 就切换到了消费者线

程,消费者线程把姓名和上一个人的性别联系到一起;

生产者放了若干数据,消费者才开始取数据,或者是消费者取完一个数据,还没等到生产者

放入新的数据,又重复的取出已取过的数据;

生产者和消费者

wait():让当前线程放弃监视器进入等待,直到其他线程调用同一个监视器并调用 notify()或

notifyAll()为止。

notify():唤醒在同一对象监听器中调用 wait 方法的第一个线程。

notifyAll():唤醒在同一对象监听器中调用 wait 方法的所有线程。

这三个方法只能让同步监听器调用:

在同步方法中: 谁调用

在同步代码块中: 谁调用

wait()、notify()、notifyAll(),这三个方法属于 Object 不属于 Thread,这三个方法必须由同

步监视对象来调用,两种情况:

1.synchronized 修饰的方法,因为该类的默认实例(this)就是同步监视器,所以可以

在同步方法中调用这三个方法;

2.synchronized 修饰的同步代码块,同步监视器是括号里的对象,所以必须使用该

对象调用这三个方法;

可要是我们使用的是 Lock 对象来保证同步的,系统中不存在隐式的同步监视器对象,那么就

不能使用者三个方法了,那该咋办呢?

此时,Lock 代替了同步方法或同步代码块,Condition 代替了同步监视器的功能;

Condition 对象通过 Lock 对象的 newCondition()方法创建;

里面方法包括:

await(): 等价于同步监听器的 wait()方法;

signal(): 等价于同步监听器的 notify()方法;

signalAll(): 等价于同步监听器的 notifyAll()方法;

例子:设置属性

容易出现的问题是:

名字和性别不对应!

线程通信,很好!

package july7;

class Person{

private String name;

private String sex;

private Boolean isimpty = Boolean.TRUE;//内存区为空!

public String getName() {

return name;

}

public void setName(String name) {

this.name = name;

}

public String getSex() {

return sex;

}

public void setSex(String sex) {

this.sex = sex;

}

public void set(String name,String sex){

synchronized (this) {

while(!isimpty.equals(Boolean.TRUE)){//不为空的话等待消费者

消费!

try {

this.wait();

} catch (InterruptedException e) {

e.printStackTrace();

}

}

this.name = name;//为空的话生产者创造!

this.sex = sex;

isimpty = Boolean.FALSE;//创造结束后修改属性!

this.notifyAll();

}

}

public void get(){

synchronized (this) {

while(!isimpty.equals(Boolean.FALSE)){

try {

this.wait();

} catch (InterruptedException e) {

e.printStackTrace();

}

}

System.out.println(" 姓 名 "+getName()+ ", "+" 性 别

"+getSex());

isimpty = Boolean.TRUE;

this.notifyAll();

}

}

}

class Producer implements Runnable{

private Person p;

public Producer(Person p) {

super();

this.p = p;

}

@Override

public void run() {

for (int i = 0; i < 100; i++) {

if( i % 2 == 0){

p.set("刘昭", "男");

}else{

p.set("章泽天", "女");

}

}

}

}

class Consumer implements Runnable{

private Person p;

public Consumer(Person p) {

super();

this.p = p;

}

@Override

public void run() {

for (int i = 0; i < 100; i++) {

p.get();

}

}

}

public class Demo9 {

public static void main(String[] args) {

Person p = new Person();

new Thread(new Producer(p)).start();

new Thread(new Consumer(p)).start();

}

}

Java帮帮-IT资源分享网

时间: 2024-11-02 16:38:29

黑马程序员 六、线程技术的相关文章

网络资源-黑马程序员Java 知识——精华总结

获取更多资源Java帮帮IT资源分享网 一.黑马程序员-java 概述与基础知识.................................................................................6 1.何为编程?...................................................................................................................6 2.J

黑马程序员 十七、面试题之交通灯管理系统—面向对象的分析与设计、Road 类、Lamp 类、LampController 类、MainClass类)

Java帮帮-IT资源分享网  黑马程序员--面试题之交通灯管理系统 Road 类.Lamp 类.LampController 类.MainClass类   需求: 交通灯管理系统的项目需求 Ø 异步随机生成按照各个路线行驶的车辆. 例如: 由南向而来去往北向的车辆 ---- 直行车辆 由西向而来去往南向的车辆 ---- 右转车辆 由东向而来去往南向的车辆 ---- 左转车辆 ... Ø 信号灯忽略黄灯,只考虑红灯和绿灯. Ø 应考虑左转车辆控制信号灯,右转车辆不受信号灯控制. Ø 具体信号灯控

黑马程序员的c#/.net视频资料下载

问题描述 黑马程序员的c#/.net视频资料下载:视频大纲:c#编程基础!c#面向对象编程基础!WPF基础ADO.Net数据库开发基础ADO.Net项目开发教程html教程asp.net基础视频教程asp.net中级视频教程asp.net高级视频教程JavaScript教程Dom教程等等...详情点击:unity3d视频下载地址:关于unity3d:Unity3d是非常火爆的手机游戏开发技术,IPhone上有55%的游戏采用Unity3d开发.Unity3d开发基于C#和.Net技术,黑马程序员

黑马程序员 一、java 概述与基础知识

获取更多资源关注Java帮帮IT资源分享网 一.黑马程序员-java 概述与基础知识 1.何为编程? 编程就是让计算机为解决某个问题而使用某种程序设计语言编写程序代码,并最终得到结果 的过程. 为了使计算机能够理解人的意图,人类就必须要将需解决的问题的思路.方法.和手段通 过计算机能够理解的形式告诉计算机,使得计算机能够根据人的指令一步一步去工作,完 成某种特定的任务.这种人和计算机之间交流的过程就是编程.   2.Java 语言概述,历史.特点 是 SUN(Stanford Universit

黑马程序员 三、面向对象(1))

Java帮帮-IT资源分享网  三.黑马程序员-面向对象(1) 第三篇:  1.什么叫面向对象? 面向对象(Object-Oriented,简称 OO)就是一种常见的程序结构设计方法. 面向对象思想的基础是将相关的数据和方法放在一起,组合成一种新的复合数据类型,然后 使用新创建的复合数据类型作为项目的基础. 面向对象是一个很抽象的概念,它相对面向过程而言. 过程与对象都是一种解决问题的思想. 面向过程:强调的是功能行为,一种过程,先干啥,再干啥; 面向对象:将功能封装到对象里,强调的是具备某功能

黑马程序员 十八、银行业务系统等

Java帮帮-IT资源分享网 NumberManager 类.NumberMachine 类.CustomerType 枚举类.ServiceWindow 类.MainClass 类与 Constants 类  黑马程序员--面试题之银行业务系统  1.需求 模拟实现银行业务调度系统逻辑,具体需求如下:  银行内有 6 个业务窗口,1 - 4 号窗口为普通窗口,5 号窗口为快速窗口,6 号窗口为 VIP 窗口.  有三种对应类型的客户:VIP 客户,普通客户,快速客户(办理如交水电费.电话费

黑马程序员 十一、网络编程

Java帮帮-IT资源分享网  十一.黑马程序员-网络编程 第 11 天:网络编程(1)  1.什么是网络编程? 网络编程的本质是两个设备之间的数据交换,当然,在计算机网络中,设备主要指计算 机.数据传递本身没有多大的难度,不就是把一个设备中的数据发送给两外一个设备,然后 接受另外一个设备反馈的数据.现在的网络编程基本上都是基于请求/响应方式的,也就是 一个设备发送请求数据给另外一个,然后接收另一个设备的反馈.在网络编程中,发起连接 程序,也就是发送第一次请求的程序,被称作客户端(Client)

黑马程序员 四、面向对象(2)

Java帮帮-IT资源分享网四.黑马程序员-面向对象(2) 第四篇:  1.基本数据类型的包装类 引言:Java 提倡的万物皆对象,但是数据类型的划分出现了基本数据类型和引用数据类型, 那么我们怎么能把基本数据类型称为对象呢?  除了 Integer 和 Character 定义的名称和对应的基本类型差异大,其他六种都是将首字母大写 就可以了. Integer,Byte,Float,Double,Short,Long 都是 Number 类的子类.(Number 类后面讲): Character

黑马程序员 七、集合框架(1)

Java帮帮-IT资源分享网  七.黑马程序员-集合框架(1)  第七篇 集合框架(1)  1.集合类 & 容器 为什么出现集合类? 面向对象语言对事物的体现都是以对象的形式,所以为了方便对多个对象的操作,就要 对对象进行存储,集合就是存储对象最常用的一种方式. 数组和集合类同是容器,有何不同? 数组虽然也可以存储对象,但长度是固定的:集合长度是可变的.数组中可以存储任意 数据类型,集合只能存储对象. 集合类的特点 集合只用于存储对象,集合长度是可变的,集合可以存储不同类型的对象. 我的总结:集