首先提出问题
什么是多线程,其实说到多线程就得说到进程,那么什么是进程?它和多线程是什么关系?
1:进程指的就是正在运行中的程序,是系统来进行资源分配和调度的独立单位。
每一个进程都有自己独立的内存空间与系统资源
2:线程 线程其实就是进程中单个顺序控制流(任务),是一条执行路径
一个进程有一条路径就是单线程
一个程序有多条路径就是多线程
3:举例:
扫雷程序与迅雷下载
大家注意两个词汇:并行与并发。
前者是逻辑上同时发生,指在某一时间内同时运行多个程序
后者是物理上同时发生,指在莫一时间点同时云行多个程序
java程序的运行原理
由java命令启动JVM,JVM启动就相当与启动了一个进程
接着有该进程创建了一个主线程去调用main方法。
问题:
JVM虚拟机的启动是单线程还是多线程?
是多线程的。
原因是垃圾回收线程也要启动,否则很容易会出现内存溢出。
现在的垃圾回收线程加前面的主线程,最低启动了两个线程,所以,jVM的启动其实是多线程的
如何实现多线程程序?
由于线程是依赖系统而存在的,所以我们应该先创建一个进程出来。
而进程是由系统创建的,所以我们应该去调用系统功能创建一个进程
java是不可以直接调用系统功能的,所以我们没有办法直接实现多线程程序
但是java可以调用C/C++写的程序来实现多线程程序(由C/C++去调用系统功能创建进程,然后由java调用这些东西)
然后提供一些类供我们使用,然后由Java去写多线程程序
那么Java提供的类是什么呢?
Thread
通过查看API,我们知道了有2中方式实现多线程程序。
方式1:继承Thread类。
步骤
A:自定义类MyThread继承Thread类。
B:MyThread类里面重写run()?
为什么是run()方法呢?(不是类中的所有代码都需要被线程执行的,所以Thread 提供run()来包含那些需要执行的多线程序)
C:创建对象
D:启动线程
注意:这里启动线程是对象调用start()方法而不是run()
因为
run():仅仅是封装被线程执行的代码,直接调用是普通方法
start():首先启动了线程,然后再由jvm去调用该线程的run()方法。
可能会遇到的异常: IllegalThreadStateException:非法的线程状态异常
为什么呢?因为这个相当于是my线程被调用了两次。而不是两个线程启动。
class MyThread extends Thread {
public void run(){
//自己写代码
for (int x = 0; x < 200; x++) {
System.out.println(getName()+"抢到数字:"+x);
}
}
}
public class MyThreadTest {
public static void main(String[] args) {
MyThread mt=new MyThread();
MyThread mt2=new MyThread();
mt.setName("李世民");
mt2.setName("杨广");
mt.start();
mt2.start();
}
}
如何获取线程对象的名称呢?
public final String getName():获取线程的名称。
如何设置线程对象的名称呢?
public final void setName(String name):设置线程的名称 (或者在新建线程对象的时候就可以用代参构造方法传入名称参数)
针对不是Thread类的子类中如何获取线程对象名称呢?
public static Thread currentThread():返回当前正在执行的线程对象
Thread.currentThread().getName()
/*在没有设定线程名称时,名称为什么是:Thread-? 编号
看下面源码:因为Thread在初始化时会调用init方法,而里面就把名称写成了"Thread-",而且会用类变量记录其数量编码,最后将其返回给我们
/**
* Allocates a new {@code Thread} object. This constructor has the same
* effect as {@linkplain #Thread(ThreadGroup,Runnable,String) Thread}
* {@code (null, null, gname)}, where {@code gname} is a newly generated
* name. Automatically generated names are of the form
* {@code "Thread-"+}<i>n</i>, where <i>n</i> is an integer.
*/
*/
class Thread {
private char name[];
public Thread() {
init(null, null, "Thread-" + nextThreadNum(), 0);
}
private void init(ThreadGroup g, Runnable target, String name,
long stackSize) {
init(g, target, name, stackSize, null);
}
private void init(ThreadGroup g, Runnable target, String name,
long stackSize, AccessControlContext acc) {
//大部分代码被省略了
this.name = name.toCharArray();
}
public final void setName(String name) {
this.name = name.toCharArray();
}
private static int threadInitNumber; //0,1,2
private static synchronized int nextThreadNum() {
return threadInitNumber++; //return 0,1
}
public final String getName() {
return String.valueOf(name);
}
}
class MyThread extends Thread {
public MyThread() {
super();
}
}
线程调度:每台电脑都只有一个cpu,线程是通过获取cpu的执行权限,也就是时间片后才会执行指令,但是在某时某刻cpu只能执行一条线程
那么cpu是怎样进行调度的呢?
1:分时调度模型线程 线程按时间轮流执行
2:抢占式调度模型线程 线程优先按优先级高的执行。
java是第二种:抢占式调度模型线程
class MYThreadPriority extends Thread {
public MYThreadPriority(String name){
super(name);
}
public void run(){
for(int x=1;x<=100;x++){
System.out.println(getName()+"卖出第"+x+"个馒头");
}
System.err.println(getName()+"已卖完");
}
}
/*
*
* public final int getPriority()返回线程的优先级。
*
* public final void setPriority(int newPriority) 设置线程优先级
*
*
*
* */
public class ThreadPriority {
public static void main(String[] args) {
MYThreadPriority MTP = new MYThreadPriority("摊位1");
MYThreadPriority MTP2 = new MYThreadPriority("摊位2");
MYThreadPriority MTP3 = new MYThreadPriority("摊位3");
// 查看线程优先级
System.out.println(MTP.getPriority());
System.out.println(MTP2.getPriority());
System.out.println(MTP3.getPriority());
/*
* 我们发现线程的优先级默认值是5,那么线程的最小和最大的优先级是多少?
* 查看api文档
* public final void setPriority(int newPriority)更改线程的优先级。
* 首先调用线程的 checkAccess方法,且不带任何参数。这可能抛出 SecurityException。
* 在其他情况下,线程优先级被设定为指定的 newPriority 和该线程的线程组的最大允许优先级相比较小的一个。
*
*
* 参数: newPriority - 要为线程设定的优先级 抛出: IllegalArgumentException - 如果优先级不在
* MIN_PRIORITY 到 MAX_PRIORITY 范围内。
* MAX_PRIORITY=10
* MIN_PRIORITY=1
*
*也就是说线程的最高优先级是10.最低是1,不在这个范围之内就会抛出IllegalArgumentException异常
*
*/
//设置线程中第一个线程为最高线程,第二个线程为默认线程,第三个线程为最低线程
MTP.setPriority(10);
MTP2.setPriority(1);
MTP3.setPriority(1);
MTP.start();
MTP2.start();
MTP3.start();
}
}
我们的线程没有设置优先级,肯定有默认优先级。
那么,默认优先级是多少呢?
如何获取线程对象的优先级?
public final int getPriority():返回线程对象的优先级
如何设置线程对象的优先级呢?
public final void setPriority(int newPriority):更改线程的优先级。
注意:
线程默认优先级是5。
线程优先级的范围是:1-10。
线程优先级高仅仅表示线程获取的 CPU时间片的几率高,但是要在次数比较多,或者多次运行的时候才能看到比较好的效果。
IllegalArgumentException:非法参数异常。
抛出的异常表明向方法传递了一个不合法或不正确的参数。
线程控制:
•线程休眠
•public static void sleep(long millis)
class ThreadSleep {
public static void main(String[] args) {
MyThreadSleep MT=new MyThreadSleep("军队");
MT.start();
}
}
import java.util.Date;
/*
* static void sleep(long millis)
在指定的毫秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度程序精度和准确性的影响。
static void sleep(long millis, int nanos)
在指定的毫秒数加指定的纳秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度程序精度和准确性的影响。
* */
public class MyThreadSleep extends Thread {
MyThreadSleep(){
super();
}
MyThreadSleep(String name){
super(name);
}
@Override
public void run() {
//super.run();
int x=1;
while(x<100){
System.out.println(getName()+ "出动"+x+"号队员" + new Date());
try {
//Thread.sleep(1000); 1000毫秒为1秒
Thread.sleep(900,100);
} catch (InterruptedException e) {
e.printStackTrace();
}
x++;
}
}
}
•等待线程,加入线程 当一个线程对象调用此方法后,只有此线程执行完毕才会与运行其他线程
•public final void join()
/*
* 加入线程
* public final void join(long millis) throws InterruptedException
* 等待该线程终止的时间最长为 millis 毫秒。超时为 0 意味着要一直等下去。
*
* */
public class ThreadJoin {
public static void main(String[] args) throws InterruptedException {
MyThreadJoin MT=new MyThreadJoin("海军");
MyThreadJoin MT2 =new MyThreadJoin("陆军");
MyThreadJoin MT3 =new MyThreadJoin("空军");
MT.start();
MT.join();
MT2.start();
MT3.start();
}
}
class MyThreadJoin extends Thread {
MyThreadJoin(){}
MyThreadJoin(String name){super(name);}
@Override
public void run() {
for(int x=0;x<100;x++){
System.out.println(getName()+"正在出动"+x+"士兵");
try{
Thread.sleep(1000);
}catch(InterruptedException e){
e.printStackTrace();
}
}
}
}
•线程礼让
•public static void yield()
class MyThreadYield extends Thread {
public MyThreadYield() {
}
public MyThreadYield(String name){
super(name);
}
@Override
public void run() {
for (int i = 0; i <100; i++) {
System.out.println(getName()+"出动"+i+"号士兵");
try{Thread.sleep(1000);}catch(InterruptedException e){e.printStackTrace();}
Thread.yield();
}
}
}
/*礼让线程,你一下我一下
*
* public static void yield()暂停当前正在执行的线程对象,并执行其他线程。
*
*
* */
public class THreadyYield {
public static void main(String[] args) {
MyThreadYield MT =new MyThreadYield("男士优先");
MyThreadYield MT2 =new MyThreadYield("女士优先");
MT.start();
MT2.start();
}
}
•后台线程
•public final void setDaemon(boolean on)
当正在运行的线程都是守护线程时,Java 虚拟机退出。 该方法必须在启动线程前调用。可用游戏:坦克大战理解。
public class MyThreadDaemon extends Thread {
MyThreadDaemon(){}
MyThreadDaemon(String name){super(name);}
@Override
public void run() {
for (int i = 0; i <100; i++) {
System.out.println(getName()+"正在喝酒");
try{Thread.sleep(1000);}catch(InterruptedException e){e.printStackTrace();}
}
}
}
/*public final void setDaemon(boolean on)将该线程标记为守护线程或用户线程。当正在运行的线程都是守护线程时,Java 虚拟机退出。
该方法必须在启动线程前调用。
该方法首先调用该线程的 checkAccess 方法,且不带任何参数。这可能抛出 SecurityException(在当前线程中)。
* */
public class ThreadDaemon {
public static void main(String[] args) {
//MyThreadDaemon Mt =new MyThreadDaemon("刘备");
MyThreadDaemon Mt2 =new MyThreadDaemon("关羽");
MyThreadDaemon Mt3 =new MyThreadDaemon("张飞");
//Mt.start();
Mt2.setDaemon(true);
Mt3.setDaemon(true);
Mt2.start();
Mt3.start();
Thread.currentThread().setName("刘备");
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName()+"正在喝酒"+i);
}
System.out.println("刘备结束了");
}
}
•中断线程
•public final void stop()
•public void interrupt()
public class ThreadStop {
public static void main(String[] args) {
MtThreadStop Mt =new MtThreadStop("摊位1");
MtThreadStop Mt2 =new MtThreadStop("摊位2");
Mt.start();
try {
Thread.sleep(3000);
// Mt.stop();
Mt.interrupt();
} catch (InterruptedException e) {
e.printStackTrace();
}
//Mt2.start();
}
}
/*
•public final void stop() 已过时,有点暴力直接杀死线程 后面的代码不在执行
,•public void interrupt() 中断线程。 把线程的状态终止,并抛出一个InterruptedException。
*/
public class MtThreadStop extends Thread {
MtThreadStop(){}
MtThreadStop(String name){super(name);}
@Override
public void run() {
//for (int i = 0; i <100; i++) {
//System.out.println(getName()+"卖出"+i);
System.out.println("开始时间 "+new Date());
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
System.out.println("线程被中止了");
//e.printStackTrace();
}
System.out.println(getName()+"结束执行" +"时间"+new Date() );
// }
}
}
线程的第二种实现方式:
实现runnable接口
1:创建自定义类MyRunnable实现Runnnable接口
2:重写run()方法
3:创建测试类RunnableDemo
4: 创建MyRunnable对象,并且将其作为构造参数,放入新建的Thread构造参数中。
class MyRunnable implements Runnable {
@Override
public void run() {
for (int i = 0; i <100; i++) {
System.out.println(Thread.currentThread().getName()+"是"+i);
}
}
}
public class RunnableDemo {
public static void main(String[] args) {
MyRunnable my=new MyRunnable();
Thread t1=new Thread(my,"窗口1");
Thread t2=new Thread(my,"窗口2");
//t1.start();
//t2.start();
System.err.println(t1.getName()+t1.getPriority());
System.err.println(t2.getName()+t2.getPriority());
}
}
1:有了第一个方法为什么还要第二个?
A:避免了java单继承带来的局限性
B:第二种适合多个相同程序去处理同一资源的情况,把线程同程序的代码,数据有效的分离,较好的体现了面向对象的设计思想。(你想,第一种先线程类都是一个对象,里面成员变量之类的都会一人有一个,当使用第二个将其作为对象传入Thread中时,就会只有一份资源,因为线程类对象就一个)