黑马程序员技术交流社区
标题: 呈上上课总结,多线程 [打印本页]
作者: 热工v 时间: 2016-5-6 22:36
标题: 呈上上课总结,多线程
多线程:
1.1线程的概述:
线程是程序执行的一条路径, 一个进程中可以包含多条线程
线程并发执行可以提高程序的效率, 可以同时完成多项工作
1.2多线程的应用场景:
屏幕共享程序
各式可多个文件同时下载的下载软件
聊天工具软件
同时工作的服务器软件等
2.多线程并行和并发的区别(了解就好):
以两方概述进行比对
多线程并行的概述:
2.1.1并行就是两个任务同时运行,就是甲任务进行的同时,乙任务也在进行。(需要多核CPU)
2.1.2并发是指两个任务都请求运行,而处理器只能按受一个任务,就把这两个任务安排轮流进行,由于时间间隔较短,使人感觉两个任务都在运行。
2.1.3比如我跟两个网友聊天,左手操作一个电脑跟甲聊,同时右手用另一台电脑跟乙聊天,这就叫并行。
多线程并发的概述:
2.2.1如果用一台电脑我先给甲发个消息,然后立刻再给乙发消息,然后再跟甲聊,再跟乙聊。这就叫并发。
3.Java程序运行原理和JVM的启动是多线程的论证:
3.1 Java程序运行原理:
Java命令会启动java虚拟机,启动JVM,等于启动了一个应用程序,也就是启动了一个进程。该进程会自动启动一个“主线程” ,然后主线程去调用某个类的main 方法。
3.2 JVM的启动是多线程的论证:
原理上: JVM启动至少启动了垃圾回收线程和主线程,所以是多线程的。
代码证实:
public class Demo1_Thread {
Public static void main(String[] args) {
For(int I = 0; I <100000; i++){
New Demo();
}
For(int i = 0; I < 10000; i++){
System.out.println(“我是主线程的执行代码”);
}
}
}
Class Demo {
Public void finalize() {
System.out.println(“垃圾被清扫了”);
}
}
4.多线程程序实现的方法
4. 1 继承Thread
定义类继承Thread
重写run方法
把新线程要做的事写在run方法中
创建线程对象
开启新线程, 内部会自动执行run方法
实现代码:
public static void main(String[] args) {
MyThreadmt = new MyThread();
mt.start();
for(inti = 0; i < 1000; i++) {
System.out.println("bb");
}
}
}
class MyThread extends Thread {
publicvoid run() {
for(inti = 0; i < 1000; i++) {
System.out.println("aaaaaaaaaaaa");
}
}
4.2实现Runnable
定义类实现Runnable接口
实现run方法
把新线程要做的事写在run方法中
创建自定义的Runnable的子类对象
创建Thread对象, 传入Runnable
调用start()开启新线程, 内部会自动调用Runnable的run()方法
实现代码:
public static void main(String[] args) {
MyRunnablemr = new MyRunnable();
//Runnabletarget = mr; mr = 0x0011
Threadt = new Thread(mr);
t.start();
for(inti = 0; i < 1000; i++) {
System.out.println("bb");
}
}
}
class MyRunnable implements Runnable {
publicvoid run() {
for(inti = 0; i < 1000; i++) {
System.out.println("aaaaaaaaaaaa");
}
}
作者: 热工v 时间: 2016-5-6 22:37
5.多线程(实现Runnable的原理)(了解就好):
实现原理方法: 查看源码
5.1看Thread类的构造函数,传递了Runnable接口的引用
5.2通过init()方法找到传递的target给成员变量的target赋值
5.3查看run方法,发现run方法中有判断,如果target不为null就会调用Runnable接口子类对象的run方法
6.多线程(两种方法的区别:
6.1 通过查看源码的区别:
a.继承Thread : 由于子类重写了Thread类的run(), 当调用start()时, 直接找子类的run()方法
b.实现Runnable : 构造函数中传入了Runnable的引用, 成员变量记住了它,start()调用run()方法时内部判断成员变量Runnable的引用是否为空, 不为空编译时看的是Runnable的run(),运行时执行的是子类的run()方法
6.2 两种方法分别的好处和弊端:
6.2.1继承Thread
好处是:可以直接使用Thread类中的方法,代码简单
弊端是:如果已经有了父类,就不能用这种方法
6.2.2实现Runnable接口
好处是:即使自己定义的线程类有了父类也没关系,因为有了父类也可以实现接口,而且接口是可以多实现的
弊端是:不能直接使用Thread中的方法需要先获取到线程对象后,才能得到Thread的方法,代码复杂
注:一般来说编程时会推荐使用继承Thread,因为相对来说继承Thread的运用代码会相对比较简单,而实现Runnable接口在原理上来看可以看成是继承Thread的一种补充.
7.多线程(匿名内部类实现线程的两种方式):
7.1继承Thread类
7.1.1继承Thread类
7.1.2重写run方法
7.1.3将要执行的代码写在run方法中
7.1.4开启线程
7.2实现Runnable接口
开启线程:
7.2.1将Runnable的子类对象传递给Thread的构造方法
7.2.2将run方法重写
代码演示:
public static void main(String[] args) {
newThread() {
publicvoid run() {
for(inti = 0; i < 1000; i++) {
System.out.println("aaaaaaaaaaaaaa");
}
}
}.start();
newThread(new Runnable() {
publicvoid run() {
for(inti = 0; i < 1000; i++) {
System.out.println("bb");
}
}
}).start();
}
}
8.多线程(获取名字和设置名字)
8.1 获取名字
通过getName()方法获取线程对象的名字
8. 2 设置名字
通过构造函数可以传入String类型的名字
实现代码1:
public static void main(String[] args) {
newThread("芙蓉姐姐") {
publicvoid run() {
System.out.println(this.getName()+ "....aaaaaaaaa");
}
}.start();
newThread("凤姐") {
publicvoid run() {
System.out.println(this.getName()+ "....bb");
}
}.start();
}
}
实现代码2:
public static void main(String[] args) {
Threadt1 = new Thread() {
publicvoid run() {
System.out.println(this.getName()+ "....aaaaaaaaaaaaa");
}
};
Threadt2 = new Thread() {
publicvoid run() {
System.out.println(this.getName()+ "....bb");
}
};
t1.setName("张三");
t2.setName("李四");
t1.start();
t2.start();
}
作者: 热工v 时间: 2016-5-6 22:38
9. 多线程(获取当前线程的对象)(重点掌握):
Thread.currentThread(),主线程也可以获取.获取当前正在执行的线程.
返回当前线程方法的使用.(返回的是Thread)
实现代码:
public static void main(String[] args) {
newThread() {
publicvoid run() {
System.out.println(getName()+ "....aaaaaa");
}
}.start();
newThread(new Runnable() {
publicvoid run() {
System.out.println(Thread.currentThread().getName()+ "...bb");
}
}).start();
Thread.currentThread().setName("我是主线程");
System.out.println(Thread.currentThread().getName());
}
}
10. 多线程(休眠线程)
Thread.sleep(毫秒,纳秒), 控制当前线程休眠若干毫秒1秒= 1000毫秒 1秒 = 1000 *1000 * 1000纳秒 1000000000
实现代码1:
Public static void main(String[] args) {
For(intI = 20; I >= 0; i--) {
Thread.sleep(1000);
System.out.println("倒计时第" +i+ "秒");
}
}
实现代码2:
public static void main(String[] args)throws InterruptedException {
//demo1();
newThread() {
publicvoid run() {
for(inti = 0; i < 10; i++) {
try{
Thread.sleep(1000);
}catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(getName()+ "...aaaaaaaaaa");
}
}
}.start();
newThread() {
publicvoid run() {
for(inti = 0; i < 10; i++) {
try{
Thread.sleep(1000);
}catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(getName()+ "...bb");
}
}
}.start();
}
11.多线程(守护线程)
setDaemon(), 设置一个线程为守护线程, 该线程不会单独执行, 当其他非守护线程都执行结束后, 自动退出
课上举例:象棋,守护线程相当于除了帅棋以外的其他棋子,当帅棋被吃掉之后,所有的其余棋子判输,停止一切运动.
实现代码:
public static void main(String[] args) {
Threadt1 = new Thread() {
publicvoid run() {
for(inti = 0; i < 2; i++) {
System.out.println(getName()+ "...aaaaaaaaaaaaaaaaaaaa");
}
}
};
Threadt2 = new Thread() {
publicvoid run() {
for(inti = 0; i < 50; i++) {
System.out.println(getName()+ "...bb");
}
}
};
t2.setDaemon(true);
t1.start();
t2.start();
}
}
12.多线程之加入线程:
12.1 join(), 当前线程暂停, 等待指定的线程执行结束后,当前线程再继续
12.2 join(int), 可以等待指定的毫秒之后继续
实现代码: public static void main(String[] args) {
finalThread t1 = new Thread() {
publicvoid run() {
for(inti = 0; i < 10; i++) {
System.out.println(getName()+ "...aaaaaaaaaaaaa");
}
}
};
Threadt2 = new Thread() {
publicvoid run() {
for(inti = 0; i < 10; i++) {
if(i== 2) {
try{
//t1.join();
t1.join(1);
}catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(getName()+ "...bb");
}
}
};
t1.start();
t2.start();
}
}
注:插队指定的时间,时间到达之后,交替执行直至完毕
作者: wh19900105 时间: 2016-5-6 22:40
还没有学啊
作者: 热工v 时间: 2016-5-6 22:40
13 多线程(礼让线程)(了解就好)
yield让出cpu
实现代码:
public static void main(String[] args) {
newMyThread().start();
newMyThread().start();
}
}
class MyThread extends Thread {
publicvoid run() {
for(inti = 1; i <= 1000; i++) {
if(i% 10 == 0) {
Thread.yield(); }
System.out.println(getName()+ "..." + i);
}
}
}
14.设置线程的优先级(也是了解就好!!!):
setPriority()设置线程的优先级
实现代码:
public static void main(String[] args) {
Threadt1 = new Thread(){
publicvoid run() {
for(inti = 0; i < 100; i++) {
System.out.println(getName()+ "...aaaaaaaaa" );
}
}
};
Threadt2 = new Thread(){
publicvoid run() {
for(inti = 0; i < 100; i++) {
System.out.println(getName()+ "...bb" );
}
}
};
t1.setPriority(Thread.MIN_PRIORITY); //设置最小的线程优先级
t2.setPriority(Thread.MAX_PRIORITY); //设置最大的线程优先级
t1.start();
t2.start();
}
}
设置最值(最大和最小)得优先级
15多线程(同步代码块)(掌握!):
15.1同步代码块的使用情况:
15.1.1当多线程并发, 有多段代码同时执行时, 我们希望某一段代码执行的过程中CPU不要切换到其他线程工作. 这时就需要同步.
15.1.2如果两段代码是同步的, 那么同一时间只能执行一段,在一段代码没执行结束之前, 不会执行另外一段代码.
15.2同步代码块的概述:
使用synchronized关键字加上一个锁对象来定义一段代码, 这就叫同步代码块
注: 多个同步代码块如果使用相同的锁对象, 那么他们就是同步的
实现代码:
public static void main(String[] args) {
finalPrinter p = new Printer();
newThread() {
publicvoid run() {
while(true){
p.print1();
}
}
}.start();
newThread() {
publicvoid run() {
while(true){
p.print2();
}
}
}.start();
}
}
class Printer {
Demod = new Demo();
publicvoid print1() {
synchronized(d){
System.out.print("黑");
System.out.print("马");
System.out.print("程");
System.out.print("序");
System.out.print("员");
System.out.print("\r\n");
}
}
publicvoid print2() {
synchronized(d){
System.out.print("传");
System.out.print("智");
System.out.print("播");
System.out.print("客");
System.out.print("\r\n");
}
}
}
class Demo{}
注: 1,同步代码块,锁机制,锁对象可以是任意的
2,锁对象不能使匿名对象,因为匿名对象不是同一个对象.
16.多线程(同步方法)
使用synchronized关键字修饰一个方法, 该方法中所有的代码都是同步的
注: 1,非静态同步函数的锁对象是this
2,静态的同步函数的锁是该类的字节码对象
实现代码:
public static void main(String[] args) {
finalPrinter2 p = new Printer2();
newThread() {
publicvoid run() {
while(true){
p.print1();
}
}
}.start();
newThread() {
publicvoid run() {
while(true){
p.print2();
}
}
}.start();
}
}
class Printer2 {
Demod = new Demo();
publicstatic synchronized void print1() {
System.out.print("黑");
System.out.print("马");
System.out.print("程");
System.out.print("序");
System.out.print("员");
System.out.print("\r\n");
}
public static void print2() {
synchronized(Printer2.class){
System.out.print("传");
System.out.print("智");
System.out.print("播");
System.out.print("客");
System.out.print("\r\n");
}
}
}
当这个方法里面所有的对象都需要同步的时候,才能使用同步方法.
作者: 热工v 时间: 2016-5-6 22:42
17. 多线程(线程安全问题)(重点掌握)
17.1多线程安全问题的概述:
17.1.1多线程并发操作同一数据时, 就有可能出现线程安全问题
17.1.2使用同步技术可以解决这种问题, 把操作数据的代码进行同步, 不要多个线程一起操作
17.2通过案例学习线程安全问题(铁路售票问题):
实现代码:
public static void main(String[] args) {
newTicket().start();
newTicket().start();
newTicket().start();
newTicket().start();
}
}
class Ticket extends Thread {
privatestatic int ticket = 100;
//privatestatic Object obj = new Object();
publicvoid run() {
while(true){
synchronized(Ticket.class){
if(ticket<= 0) {
break;
}
try{
Thread.sleep(10);
}catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(getName()+ "...这是第" + ticket-- + "号票");
}
}
}
}
18. 多线程(火车站卖票的例子):
用实现Runnable接口
实现代码:
public static void main(String[] args) {
MyTicketmt = new MyTicket();
newThread(mt).start();
newThread(mt).start();
newThread(mt).start();
newThread(mt).start();
/*Threadt1 = new Thread(mt);
t1.start();
t1.start();
t1.start();
t1.start();*/
}
}
class MyTicket implements Runnable {
privateint tickets = 100;
@Override
publicvoid run() {
while(true){
synchronized(this){
if(tickets<= 0) {
break;
}
try{
Thread.sleep(10);
}catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+ "...这是第" + tickets-- + "号票");
}
}
}
注: 1,多次启动一个线程是非法的.
19. 多线程(死锁)(避免)
19.1多线程同步的时候, 如果同步代码嵌套, 使用相同锁, 就有可能出现死锁
19.2尽量不要嵌套使用
实际代码(莫写莫作死!)
private static String s1 = "筷子左";
privatestatic String s2 = "筷子右";
publicstatic void main(String[] args) {
newThread() {
publicvoid run() {
while(true){
synchronized(s1){
System.out.println(getName()+ "...获取" + s1 + "等待" + s2);
synchronized(s2){
System.out.println(getName()+ "...拿到" + s2 + "开吃");
}
}
}
}
}.start();
newThread() {
publicvoid run() {
while(true){
synchronized(s2){
System.out.println(getName()+ "...获取" + s2 + "等待" + s1);
synchronized(s1){
System.out.println(getName()+ "...拿到" + s1 + "开吃");
}
}
}
}
}.start();
}
}
20. 多线程(以前的线程安全的类回顾)
20.1 回顾以前说过的线程安全问题:
20.1.1看源码:Vector,StringBuffer,Hashtable,Collections.synchroinzed(xxx)
20.1.2 Vector是线程安全的,ArrayList是线程不安全的
20.1.3 StringBuffer是线程安全的,StringBuilder是线程不安全的
20.1.4 Hashtable是线程安全的,HashMap是线程不安全的
作者: 热工v 时间: 2016-5-6 22:44
居然没法一次性发上来我也是整个人都不好了
作者: 热工v 时间: 2016-5-8 15:36
往上顶一个
欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) |
黑马程序员IT技术论坛 X3.2 |