多线程学习 (一)

版权声明: https://blog.csdn.net/qq_33278885/article/details/81294173

首先提出问题

什么是多线程,其实说到多线程就得说到进程,那么什么是进程?它和多线程是什么关系?

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中时,就会只有一份资源,因为线程类对象就一个)

猜你喜欢

转载自blog.csdn.net/qq_33278885/article/details/81294173