2016.6.16笔记(1)-多线程(1)
2016-06-17 15:45
537 查看
早上没什么事,看了下多线程,做个笔记。
进程是操作系统资源管理的最小单位,进程是一个动态的实体,他是程序的一次执行过程。也叫作一个控制单元
线程的概念:
线程是进程中独立的控制单元,线程控制着进程的执行。一个进程中至少有一个线程。
java VM(java虚拟机)在运行时启动了一个进程—java.exe; 该进程在执行时,至少有一个线程在控制着java程序的运行,并且这个线程存在于java的main函数中,该线程称之为java的主线程。
扩展:在JVM运行时,除了main函数中的线程在运行外,还有JVM中负责Java垃圾回收的线程在运行。因此,java不是单线程运行程序。
线程的5个状态图(线程和进程一样,具有5个状态:创建、就绪、运行、阻塞、终止):
程序启动运行main时候,java虚拟机启动一个进程,主线程main在main()调用时候被创建。随着调用MitiSay的两个对象的start方法,另外两个线程也启动了,这样,整个应用就在多线程下运行。
注意:start()方法的调用后并不是立即执行多线程代码,而是使得该线程变为可运行态(Runnable)(应该就是就绪状态吧),什么时候运行是由操作系统决定的。
步骤是:
1) 创建一个类继承Thread;
2) 覆写run方法,目的是存放多线程要执行的自定义代码;
3) 在main函数中创建该类;
4) 使用start()方法调用该线程(start方法有两种含义:1,启动多线程。2,调用线程中的run方法);
我们发现运行同一个程序,他们的运行结果却不太相同,
这是多个线程都获取系统的CPU资源,CPU执行到谁,谁就运行。
CPU在某一时刻只能执行一个程序(多核除外),CPU在做着快速的切换,以达到看上去是在同时执行的效果。
我们通过打印输出,来判断到底是哪一个线程抢占到了CPU的资源。
Thread类的意义—–用于描述线程。
该类定义了一个功能,用于存储线程要运行的代码,而这个存储功能就是run方法。
run方法中存储线程要执行的 自定义代码块。
而start方法用于启动线程,并自动执行run方法。
Thread类中提供的常用的方法:
- static Thread currentThread: 返回当前正在执行的线程对象的引用
- String getName();返回当前线程的名称。
- 当使用Thread的无参构造创建线程实例时,java虚拟机会自动为线程创建一个名字。(以Thread-编号的格式).
- static void sleep(long time) 使线程休眠time时间。
步骤:
1) 创建类实现Runnable接口
2) 实现Runnable接口中的run方法
3) 创建Thread对象
4) 将Runnable对象作为实际参数传递给Thread的构造方法
5) 调用Thread类的start方法,自动执行Runnable对象中的run方法.
单继承的局限性是指,只能有一个demothread类继承父类,而且,类之间的耦合性增加,父类的改动会直接影响子类。
Runnable则不一样呀,可以有多个demorunnable类实现Runnable类。
2、多线程执行代码位置不同:
- 继承Thread类:代码存放在Thread类的run方法中
- 实现Runnable类:代码存放在Runnable接口的run方法中。
Exception in thread “main” java.lang.IllegalThreadStateException
at java.lang.Thread.start(Unknown Source)
at com.multithread.learning.Main.main(Main.java:31)
4000
nnable接口来实现多线程,最终还是通过Thread的对象的API来控制线程的,熟悉Thread类的API是进行多线程编程的基础。
Run()方法是多线程程序的一个约定。所有的多线程代码都在run方法里面。
Thread类实际上也是实现了Runnable接口的类。在启动的多线程的时候,需要先通过Thread类的构造方法Thread(Runnable target) 构造出对象,然后调用Thread对象的start()方法来运行多线程代码。
输出结果是:
B运行 count= 5
A运行 count= 5
B运行 count= 4
B运行 count= 3
B运行 count= 2
B运行 count= 1
A运行 count= 4
A运行 count= 3
A运行 count= 2
A运行 count= 1
上面可以看出,不同的线程之间count是不同的,这对于卖票系统来说就会有很大的问题,当然,这里可以用同步来作。这里我们用Runnable来做下看看:
输出:
C运行 count= 15
D运行 count= 14
E运行 count= 13
D运行 count= 12
D运行 count= 10
D运行 count= 9
D运行 count= 8
C运行 count= 11
E运行 count= 12
C运行 count= 7
E运行 count= 6
C运行 count= 5
E运行 count= 4
C运行 count= 3
E运行 count= 2
这里要注意每个线程都是用同一个实例化对象,如果不是同一个,效果就和上面的一样了!
总结:
实现Runnable接口比继承Thread类所具有的优势:
1):适合多个相同的程序代码的线程去处理同一个资源
2):可以避免java中的单继承的限制
3):增加程序的健壮性,代码可以被多个线程共享,代码和数据独立
提醒一下大家:main方法其实也是一个线程。在java中所有的线程都是同时启动的,至于什么时候,哪个先执行,完全看谁先得到CPU的资源。
在java中,每次程序运行至少启动2个线程。一个是main线程,一个是垃圾收集线程。因为每当使用java命令执行一个类的时候,实际上都会启动一个JVM,每一个JVM实习在就是在操作系统中启动了一个进程。
1、新建状态(New):新创建了一个线程对象。
2、就绪状态(Runnable):线程对象创建后,其他线程调用了该对象的start()方法。该状态的线程位于可运行线程池中,变得可运行,等待获取CPU的使用权。
3、运行状态(Running):就绪状态的线程获取了CPU,执行程序代码。
4、阻塞状态(Blocked):阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。阻塞的情况分三种:
(一)、等待阻塞:运行的线程执行wait()方法,JVM会把该线程放入等待池中。
(二)、同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池中。
(三)、其他阻塞:运行的线程执行sleep()或join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。
5、死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。
多线程的概念
进程的概念:进程是操作系统资源管理的最小单位,进程是一个动态的实体,他是程序的一次执行过程。也叫作一个控制单元
线程的概念:
线程是进程中独立的控制单元,线程控制着进程的执行。一个进程中至少有一个线程。
java VM(java虚拟机)在运行时启动了一个进程—java.exe; 该进程在执行时,至少有一个线程在控制着java程序的运行,并且这个线程存在于java的main函数中,该线程称之为java的主线程。
扩展:在JVM运行时,除了main函数中的线程在运行外,还有JVM中负责Java垃圾回收的线程在运行。因此,java不是单线程运行程序。
线程的5个状态图(线程和进程一样,具有5个状态:创建、就绪、运行、阻塞、终止):
程序启动运行main时候,java虚拟机启动一个进程,主线程main在main()调用时候被创建。随着调用MitiSay的两个对象的start方法,另外两个线程也启动了,这样,整个应用就在多线程下运行。
注意:start()方法的调用后并不是立即执行多线程代码,而是使得该线程变为可运行态(Runnable)(应该就是就绪状态吧),什么时候运行是由操作系统决定的。
实现多线程的第一种方式(单继承方式):
继承自Thread类实现多线程步骤是:
1) 创建一个类继承Thread;
2) 覆写run方法,目的是存放多线程要执行的自定义代码;
3) 在main函数中创建该类;
4) 使用start()方法调用该线程(start方法有两种含义:1,启动多线程。2,调用线程中的run方法);
public class Day1{ public static void main(String args[]){ DemoThread dt = new DemoThread(); dt.start(); //用于启动线程,并自动执行run方法。 dt.run(); //只是单纯的对象调用,在主线程中执行,并不开启子线程。 for(int i=0;i<50;i++){ System.out.println("main run-----"+i); } } } class DemoThread extends Thread{ public void run(){ for(int i=0;i<50;i++) System.out.println("demo run-----"+i); } }
我们发现运行同一个程序,他们的运行结果却不太相同,
这是多个线程都获取系统的CPU资源,CPU执行到谁,谁就运行。
CPU在某一时刻只能执行一个程序(多核除外),CPU在做着快速的切换,以达到看上去是在同时执行的效果。
我们通过打印输出,来判断到底是哪一个线程抢占到了CPU的资源。
Thread类的意义—–用于描述线程。
该类定义了一个功能,用于存储线程要运行的代码,而这个存储功能就是run方法。
run方法中存储线程要执行的 自定义代码块。
而start方法用于启动线程,并自动执行run方法。
Thread类中提供的常用的方法:
- static Thread currentThread: 返回当前正在执行的线程对象的引用
- String getName();返回当前线程的名称。
- 当使用Thread的无参构造创建线程实例时,java虚拟机会自动为线程创建一个名字。(以Thread-编号的格式).
- static void sleep(long time) 使线程休眠time时间。
创建线程的第二种方式(实现Runnable接口):
实现Runnable 接口来实现多线程:步骤:
1) 创建类实现Runnable接口
2) 实现Runnable接口中的run方法
3) 创建Thread对象
4) 将Runnable对象作为实际参数传递给Thread的构造方法
5) 调用Thread类的start方法,自动执行Runnable对象中的run方法.
public class Day1Runable{ public static void main(String args[]){ DemoRunnable dr = new DemoRunnable(); Thread t1 = new Thread(dr); Thread t2 = new Thread(dr); Thread t3 = new Thread(dr); Thread t4 = new Thread(dr); t1.start(); t2.start(); t3.start(); t4.start(); } } class DemoRunnable implements Runnable{ private int ticket=100; public void run(){ while(true){ if(ticket>0) System.out.println(Thread.currentThread().getName()+"--售票:"+ticket--); } } }
问题:继承方式与实现方式有什么区别
1、避免了单继承的局限性单继承的局限性是指,只能有一个demothread类继承父类,而且,类之间的耦合性增加,父类的改动会直接影响子类。
Runnable则不一样呀,可以有多个demorunnable类实现Runnable类。
2、多线程执行代码位置不同:
- 继承Thread类:代码存放在Thread类的run方法中
- 实现Runnable类:代码存放在Runnable接口的run方法中。
问题
但是start方法重复调用的话,会出现java.lang.IllegalThreadStateException异常。Thread1 mTh1=new Thread1("A"); Thread1 mTh2=mTh1; mTh1.start(); mTh2.start();
Exception in thread “main” java.lang.IllegalThreadStateException
at java.lang.Thread.start(Unknown Source)
at com.multithread.learning.Main.main(Main.java:31)
本质
实际上所有的多线程代码都是通过运行Thread的start()方法来运行的。因此,不管是扩展Thread类还是实现Ru4000
nnable接口来实现多线程,最终还是通过Thread的对象的API来控制线程的,熟悉Thread类的API是进行多线程编程的基础。
Run()方法是多线程程序的一个约定。所有的多线程代码都在run方法里面。
Thread类实际上也是实现了Runnable接口的类。在启动的多线程的时候,需要先通过Thread类的构造方法Thread(Runnable target) 构造出对象,然后调用Thread对象的start()方法来运行多线程代码。
举个例子,Thread和Runnable的区别
package com.multithread.learning; /** *@functon 多线程学习,继承Thread,资源不能共享 *@author 林炳文 *@time 2015.3.9 */ class Thread1 extends Thread{ //这个count不是共享的 private int count=5; private String name; public Thread1(String name) { this.name=name; } public void run() { for (int i = 0; i < 5; i++) { System.out.println(name + "运行 count= " + count--); try { sleep((int) Math.random() * 10); } catch (InterruptedException e) { e.printStackTrace(); } } } } public class Main { public static void main(String[] args) { Thread1 mTh1=new Thread1("A"); Thread1 mTh2=new Thread1("B"); mTh1.start(); mTh2.start(); } }
输出结果是:
B运行 count= 5
A运行 count= 5
B运行 count= 4
B运行 count= 3
B运行 count= 2
B运行 count= 1
A运行 count= 4
A运行 count= 3
A运行 count= 2
A运行 count= 1
上面可以看出,不同的线程之间count是不同的,这对于卖票系统来说就会有很大的问题,当然,这里可以用同步来作。这里我们用Runnable来做下看看:
/** *@functon 多线程学习 继承runnable,资源能共享 *@author 林炳文 *@time 2015.3.9 */ package com.multithread.runnable; class Thread2 implements Runnable{ private int count=15; @Override public void run() { for (int i = 0; i < 5; i++) { System.out.println(Thread.currentThread().getName() + "运行 count= " + count--); try { Thread.sleep((int) Math.random() * 10); } catch (InterruptedException e) { e.printStackTrace(); } } } } public class Main { public static void main(String[] args) { Thread2 my = new Thread2(); new Thread(my, "C").start();//同一个mt,但是在Thread中就不可以,如果用同一个实例化对象mt,就会出现异常 new Thread(my, "D").start(); new Thread(my, "E").start(); } }
输出:
C运行 count= 15
D运行 count= 14
E运行 count= 13
D运行 count= 12
D运行 count= 10
D运行 count= 9
D运行 count= 8
C运行 count= 11
E运行 count= 12
C运行 count= 7
E运行 count= 6
C运行 count= 5
E运行 count= 4
C运行 count= 3
E运行 count= 2
这里要注意每个线程都是用同一个实例化对象,如果不是同一个,效果就和上面的一样了!
总结:
实现Runnable接口比继承Thread类所具有的优势:
1):适合多个相同的程序代码的线程去处理同一个资源
2):可以避免java中的单继承的限制
3):增加程序的健壮性,代码可以被多个线程共享,代码和数据独立
提醒一下大家:main方法其实也是一个线程。在java中所有的线程都是同时启动的,至于什么时候,哪个先执行,完全看谁先得到CPU的资源。
在java中,每次程序运行至少启动2个线程。一个是main线程,一个是垃圾收集线程。因为每当使用java命令执行一个类的时候,实际上都会启动一个JVM,每一个JVM实习在就是在操作系统中启动了一个进程。
1、新建状态(New):新创建了一个线程对象。
2、就绪状态(Runnable):线程对象创建后,其他线程调用了该对象的start()方法。该状态的线程位于可运行线程池中,变得可运行,等待获取CPU的使用权。
3、运行状态(Running):就绪状态的线程获取了CPU,执行程序代码。
4、阻塞状态(Blocked):阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。阻塞的情况分三种:
(一)、等待阻塞:运行的线程执行wait()方法,JVM会把该线程放入等待池中。
(二)、同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池中。
(三)、其他阻塞:运行的线程执行sleep()或join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。
5、死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。
相关文章推荐
- java对世界各个时区(TimeZone)的通用转换处理方法(转载)
- java-注解annotation
- java-模拟tomcat服务器
- java-用HttpURLConnection发送Http请求.
- java-WEB中的监听器Lisener
- Android IPC进程间通讯机制
- Android Native 绘图方法
- Android java 与 javascript互访(相互调用)的方法例子
- Python3写爬虫(四)多线程实现数据爬取
- 介绍一款信息管理系统的开源框架---jeecg
- 聚类算法之kmeans算法java版本
- java实现 PageRank算法
- PropertyChangeListener简单理解
- c++11 + SDL2 + ffmpeg +OpenAL + java = Android播放器
- 插入排序
- 冒泡排序
- 堆排序
- 快速排序