黑马程序员_多线程概述

---------------------- ASP.Net+Unity开发.Net培训、期待与您交流! ----------------------


1、线程的概念
1).了解线程之前需要先了解进程:
进程:是当前正在执行的程序,代表一个应用程序在内存中的执行区域。
线程:是程序执行的一条路径, 一个进程中可以包含多条线程
多线程并发执行可以提高程序的效率, 可以同时完成多项工作
2.多线程的应用场景
迅雷开启多条线程一起下载
QQ同时和多个人一起视频
服务器同时处理多个客户端请求

2、如何开启新线程
1).继承Thread
定义类继承Thread
重写run方法
把新线程要做的事写在run方法中
创建线程对象
开启新线程, 内部会自动执行run方法
public class Demo_02ThreadExtend {
	public static void main(String[] args) {
		MyThread mt = new MyThread();			//4,创建Thread类子对象
		mt.start();					//5,调用start方法开启程序
//		mt.run();
		for (int i = 0; i < 10000; i++) {
			System.out.println("--");
		}
	}
}

class MyThread extends Thread {		<span style="white-space:pre">			</span>//1,自定义类继承Thread
	public void run() {					//2,重写run方法
		for (int i = 0; i < 10000; i++) {		//3,执行代码
			System.out.println("+++++++++++++++++++");
		}
	}
}
线程与线程的执行顺序是随机的

2).实现Runnable
定义类实现Runnable接口
实现run方法
把新线程要做的事写在run方法中
创建自定义的Runnable对象
创建Thread对象, 传入Runnable
调用start()开启新线程, 内部会自动调用Runnable的run()方法
public class Demo {
	/**
	 * Runnable target = new MyRunnable();
	 * mr = 0x0011
	 */
	public static void main(String[] args) {
		MyRunnable mr = new MyRunnable();			//4,创建自定义类对象
		//Thread t = new Thread(mr);				//5,将这个对象当作参数传递给Thread的构造函数
		//t.start();						//6,开启线程
		new Thread(mr).start();
		for(int i = 0; i < 1000; i++) {
			System.out.println("bb");
		}
	}
}
class MyRunnable implements Runnable {				<span style="white-space:pre">	</span>//1,自定义类实现Runnable接口
	public void run() {						//2,重写run方法
		for(int i = 0; i < 1000; i++) {				//3,将要执行的代码写在run方法中
			System.out.println("aaaaaaaaaaaaaaaaaaaaaaa");
		}
	}	
}


3).两种方式的区别
区别一:
a.由于子类重写了Thread类的run(), 当调用start()时, 直接找子类的run()方法
b.构造函数中传入了Runnable的引用, 成员变量记住了它, start()调用run()方法时内部判断成员变量Runnable是否为空, 不为空则执行Runnable的run()
区别二:
a.继承Thread只能是单继承,如果自己定义的线程类已经有了父类,就不能再继承了
b.实现Runnable接口可以多实现,即使自己定义线程类已经有父类可以实现Runnable接口
总结:继承Thread的好处是:可以直接使用Thread类中的方法,代码简单
弊端是:如果已经有了父类,就不能用这种方法
实现Runnable接口的好处是:即使自己定义的线程类有了父类也没关系,因为有了父类也可以实现接口,而且接口是可以多实现的
弊端是:不能直接使用Thread中的方法需要先获取到线程对象后,才能得到Thread的方法,代码复杂

3、Thread类常用方法
1).设置名字
通过构造函数可以传入String类型的名字
new Thread("线程1") {			//给线程传入名字为“线程1”
			public void run(){
				System.out.println(getName() + " = 111111111111111111");		//线程1 = 111111111111111111		
			}
		}.start();
通过setName(String)方法可以设置线程对象的名字
Thread t1 = new Thread() {<span style="white-space:pre">		</span>
			public void run(){
				System.out.println("111111111111111111");		
			}
		};
		t1.setName("线程1");<span style="white-space:pre">		</span>//<span style="font-size:18px;">通过setName(String)方法可以设置线程对象的名字</span>
2).获取名字
通过getName()方法获取线程对象的名字
3).获取当前线程对象
Thread.currentThread(), 主线程也可以获取
4).休眠
Thread.sleep(毫秒,纳秒), 控制当前线程休眠若干毫秒1秒= 1000毫秒 1秒 = 1000 * 1000 * 1000纳秒 1000000000

for (int i = 10; i >=0; i--) {
			System.out.println("倒数第" + i + "秒。");
			Thread.sleep(500);				//可以使程序短暂暂停
		}
<span style="white-space:pre">		</span>/*
<span style="white-space:pre">		</span> * Thread.sleep()方法多线程的时候,可以比较清楚看到各个线程的执行
<span style="white-space:pre">		</span> * 会出异常,只能自己解决,不能抛出
<span style="white-space:pre">		</span> * 因为其父类方法没有抛出异常,所以子类也不能抛出异常
<span style="white-space:pre">		</span> */

5).守护
setDaemon(), 设置一个线程为守护线程, 该线程不会单独执行, 当其他非守护线程都执行结束后, 自动退出
public class Demo {
	public static void main(String[] args) {
		Thread t1 = new Thread() {
			public void run() {
				for (int i = 0; i < 200; i++) {
					System.out.println("守护线程 1 = " + "11111111111111111111" + i);
				}
			}
		};
		Thread t2 = new Thread() {
			public void run() {
				for (int i = 0; i < 2; i++) {
					System.out.println("非守护线程 1 = " + "22");
				}
			}
		};
		t1.setDaemon(true);		//设置t1为守护线程
		t1.start();
		t2.start();
	}
}
事实上当非守护线程运行完毕后,守护线程还会执行一段时间。(具体应用例如,QQ社交软件,当开启聊天窗口,而直接关闭QQ软件的时候,聊天窗口作为守护软件,它还会运行)

6).加入
join(), 当前线程暂停, 等待指定的线程执行结束后, 当前线程再继续
join(int), 可以等待指定的毫秒之后继续
7).暂停
yidld(),暂停当前正在执行的线程对象,并执行其他线程。
8).setPriority()更改线程的优先级

4、线程之间的同步问题
当多线程并发, 有多段代码同时执行时, 我们希望某一段代码执行的过程中CPU不要切换到其他线程工作. 这时就需要同步.
如果两段代码是同步的, 那么同一时间只能执行一段, 在一段代码没执行结束之前, 不会执行另外一段代码.
1
).同步代码块
使用synchronized关键字加上一个锁对象来定义一段代码, 这就叫同步代码块
多个同步代码块如果使用相同的锁对象, 那么他们就是同步的
2).同步方法
使用synchronized关键字修饰一个方法, 该方法中所有的代码都是同步的
非静态同步方法默认使用当前对象this作为锁对象
静态方法同步默认的锁对象是,所在类的字节码对象
3).线程安全问题
多线程并发操作同一数据时, 就有可能出现线程安全问题
使用同步技术可以解决这种问题, 把操作数据的代码进行同步, 不要多个线程一起操作
4).死锁问题
多线程同步的时候, 如果同步代码嵌套, 使用相同锁, 就有可能出现死锁
尽量不要嵌套使用
5、线程之间的通信问题
多个线程并发执行时, 在默认情况下CPU是随机切换线程的
如果我们希望他们有规律的执行, 就可以使用通信, 例如每个线程执行一次打印
1).怎么通信
如果希望线程等待, 就调用wait()
如果希望唤醒等待的线程, 就调用notify();
这两个方法必须在同步代码中执行, 并且使用同步锁对象来调用
2).多个线程通信的问题
notify()方法是随机唤醒一个线程
notifyAll()方法是唤醒所有线程
JDK5之前无法唤醒指定的一个线程
如果多个线程之间通信, 需要使用notifyAll()通知所有线程, 用while来反复判断条件

6、JDK5之后的线程控制
1).同步
使用ReentrantLock类的lock()和unlock()方法进行同步
2).通信
使用ReentrantLock类的newCondition()方法可以获取Condition对象
需要等待的时候使用Condition的await()方法, 唤醒的时候用signal()方法
不同的线程使用不同的Condition, 这样就能区分唤醒的时候找哪个线程了

---------------------- ASP.Net+Unity开发.Net培训、期待与您交流! ----------------------
详细请查看:www.itheima.com
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值