10-多线程

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/linzhaoliangyan/article/details/88573186

基本概念

    * 程序、进程、线程的基本概念和关系

        * 程序:是计算机指令的集合.程序是一组静态的指令集,不占用系统运行资源,不能被系统调度,也不能作为独立运行的单位,它以文件的形式存储在磁盘上

        * 进程:

            * 是一个程序在其自身的地址空间中的一次执行活动。比如,打开一个记事本,就是调用了一个进程。进程是资源申请、调度和独立运行的单位,因此,它使用系统中的运行资源;而程序不能申请系统资源,一个程序可以对应多个进程,例如著名的浏览器

 * 线程: 一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务 

   * 为什么要进行多线程编程

        * 可以更好的实现并行

        * 恰当地使用线程时,可以降低开发和维护的开销,并且能够提高复杂应用的性能。

        * CPU在线程之间开关时的开销远比进程要少得多。因开关线程都在同一地址空间内,只需要修改线程控制表或队列,不涉及地址空间和其他工作。

        * 创建和撤销线程的开销较之进程要少。 

    * Java在多线程应用中的优势

         * Java 给多线程编程提供了内置的支持

           * 多线程操作会增加程序的执行效率。各线程之间切换执行,时间比较短,看似是多线程同时运行,但对于执行者CPU来说,某一个时刻只有一个线程在运行

* Java线程的生命周期     

新建状态:
使用 new 关键字和 Thread 类或其子类建立一个线程对象后,该线程对象就处于新建状态。它保持这个状态直到程序 start() 这个线程。

就绪状态:
当线程对象调用了start()方法之后,该线程就进入就绪状态。就绪状态的线程处于就绪队列中,要等待JVM里线程调度器的调度。

运行状态:
如果就绪状态的线程获取 CPU 资源,就可以执行 run(),此时线程便处于运行状态。处于运行状态的线程最为复杂,它可以变为阻塞状态、就绪状态和死亡状态。

阻塞状态:
如果一个线程执行了sleep(睡眠)、suspend(挂起)等方法,失去所占用资源之后,该线程就从运行状态进入阻塞状态。在睡眠时间已到或获得设备资源后可以重新进入就绪状态。可以分为三种:
等待阻塞:运行状态中的线程执行 wait() 方法,使线程进入到等待阻塞状态。
同步阻塞:线程在获取 synchronized 同步锁失败(因为同步锁被其他线程占用)。
其他阻塞:通过调用线程的 sleep() 或 join() 发出了 I/O 请求时,线程就会进入到阻塞状态。当sleep() 状态超时,join() 等待线程终止或超时,或者 I/O 处理完毕,线程重新转入就绪状态。

* 线程创建与调度

    * Thread类

          * 线程开发步骤

            * 1 定义线程类

public class MThread extends Thread{

	@Override
	public void run() {
		System.out.println("哥们为被执行:"+Thread.currentThread().getName());
		try {
			Thread.sleep(2000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		//像main执行想要执行代码
		System.out.println("另一个线程执行java代码..."+Thread.currentThread().getName());
	}
}

  * 2  创建线程对象并启动线程 

public class Test1 {
	public static void main(String[] args) {
		// 默认有个线程
		System.out.println("main线程的名字:"+Thread.currentThread().getName());
		
		// 新建状态
		MThread mThread=new MThread();
		// 就绪状态
		//mThread.run();// 直接调用,当成普通类来使用
		mThread.start();//等着CPU去调度进入运行状态
		//运行状态
		//死亡状态
	}

 * 3 匿名内部类编写

new Thread() {
			@Override
			public void run() {
				System.out.println(Thread.currentThread().getName());
			}
		}.start();

* Runnable接口

        * 直接继承Thread类实现线程的方法存在局限性:由于Java是典型的单亲继承体系,因此一旦类继承Thread之后就不能再继承其他父类,对于一些必须通过继承关系来传播的特性这种方式显然会造成困扰 ,可以通过实现Runnable接口的方式来实现线程

        

* 线程启动和停止

        * 主线程:任何一个Java程序启动时,一个线程立刻运行,它执行main方法,这个线程称为程序的主线程

            * 主线程的特殊之处在于: 它是产生其它线程;

            * 通常它必须最后结束,因为它要执行其它子线程的关闭工作。 

        * 任何Java程序都至少有一个线程

        * 线程的启动

            * 可以使用start方法来启动(调用后并不是立即执行多线程代码,而是使该线程变为就绪状态,什么时候运行是由操作系统调度决定的 )

            * 直接调用run方法,不是启动线程的方法,而是正常类型的对象调用跟线程无关。

            

 * 线程停止

            * 线程的停止远比线程的启动情况复杂

            * 在Thread线程类中提供了stop()方法用于停止一个已经启动的线程,但是它已经被废弃,不建议使用,因为它本质上就是不安全的,它会解锁线程所有已经锁定的监视程序,在这个过程中会出现不确定性,导致这些监视程序监视的对象出现不一致的情况,或者引发死锁

     * 如何安全的停止一个正在运行的线程呢?

               * 线程对象的run()方法所有代码执行完成后,线程会自然消亡,因此如果需要在运行过程提前停止线程,可以通过改变共享变量值的方法让run()方法执行结束

public class TestStopThread extends Thread{
	private boolean flag=true;
	@Override
	public void run() {
		while(flag) {
			System.out.println("xxx");
		}
	}
	
	public void stopThread() {
		flag=false;
	}
	
	public static void main(String[] args) {
		TestStopThread tt=new TestStopThread();
		tt.start();
		tt.stopThread();
	}
}

   * 如果一个线程由于等待某些事件的发生而被阻塞,又该怎样停止该线程呢?

            * 使用Thread提供的interrupt()方法,因为该方法虽然不会中断一个正在运行的线程,但是它可以使一个被阻塞的线程抛出一个中断异常,从而使线程提前结束阻塞状态,退出堵塞代码 

        

 

public class StopSleepThread extends Thread{
	private boolean flag=true;
	@Override
	public void run() {
		while(flag) {
			try {
				Thread.sleep(1000*60*60);
			} catch (InterruptedException e) {
				e.printStackTrace();
				flag=false;
			}
		}
	}
	
	public void stopThread() {
		flag=false;
	}
	
	public static void main(String[] args) {
		StopSleepThread tt=new StopSleepThread();
		tt.start();
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
//		tt.stopThread();
		tt.interrupt();
	}
}

     *  interrupt()方法并不能阻断I/O阻塞或线程同步引起的线程阻塞

            * 关闭底层I/O通道,人为引发异常从而进行共享变量重新赋值而跳出线程的run()方法 

        * nio支持非阻塞式的事件驱动读取操作,在这种模式下,不需要关闭底层资源即可通过interrupt()方法直接中断其等待操作

    * 线程不同状态之间的转换

各种状态一目了然,值得一提的是"blocked"这个状态:
线程在Running的过程中可能会遇到阻塞(Blocked)情况

调用join()和sleep()方法,sleep()时间结束或被打断,join()中断,IO完成都会回到Runnable状态,等待JVM的调度。
调用wait(),使该线程处于等待池(wait blocked pool),直到notify()/notifyAll(),线程被唤醒被放到锁定池(lock blocked pool ),释放同步锁使线程回到可运行状态(Runnable)
对Running状态的线程加同步锁(Synchronized)使其进入(lock blocked pool ),同步锁被释放进入可运行状态(Runnable)。
此外,在runnable状态的线程是处于被调度的线程,此时的调度顺序是不一定的。Thread类中的yield方法可以让一个running状态的线程转入runnable。

* 守护线程

        * Java中将线程划分为了两类

            * 守护线程 (Daemon Thread)

            * 用户线程 (User Thread) 

        * 守护线程 (Daemon Thread)

             * 是指在程序运行的时候在后台提供一种通用服务的线程,比如垃圾回收线程就是一个很称职的守护者,并且这种线程并不属于程序中不可或缺的部分。因 此,当所有的非守护线程结束时,程序也就终止了,同时会杀死进程中的所有守护线程。反过来说,只要任何非守护线程还在运行,程序就不会终止

        * 用户线程

           * 用户线程和守护线程两者几乎没有区别,唯一的不同之处就在于虚拟机的退出:如果用户线程已经全部退出运行了,只剩下守护线程存在了,虚拟机也就退出了。 因为没有了被守护者,守护线程也就没有工作可做了,也就没有继续运行程序的必要了 

    

 

public class DaemonThread extends Thread{

	@Override
	public void run() {
		for(;;) {
			
		}
	}
	
	public static void main(String[] args) {
		DaemonThread daemonThread=new DaemonThread();
		daemonThread.setDaemon(true);
		daemonThread.start();
	}
}

  * 线程类重要方法的作用 

        * setPriority()方法

            * Java线程可以有优先级的设定,高优先级的线程比低优先级的线程有更高的几率得到执行(注意是更高的几率,而不是优先级高的一定有优势,但是优先级在某一些线程调度方法中有特定的作用)

           * Java线程的优先级是一个整数,其取值范围是1 (Thread.MIN_PRIORITY ) - 10 (Thread.MAX_PRIORITY )

    * public static native void yield();

                * 暂停当前正在执行的线程对象,并执行其他线程。

                   *  yield() 方法只是使当前线程重新回到就绪可执行状态,所以执行yield()线程有可能在进入到就绪状态后马上又被执行,只能使相同或更高优先级的线程有执行的机会

                   * 不会释放锁资源

               *  public static native void sleep(long millis);

                   * 在指定的毫秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度程序精度和准确性的影响。

                    * Java并不保证线程在阻塞给定的时间后能够马上执行(事实上这几乎是不可能的事情),在阻塞时间到了之后,线程进入就绪状态,继续执行的时机取决于Java虚拟机的线程调度机制,唯一能够确定的是,线程中断执行的时间是大于等于给定的阻塞时长的,因此不要将sleep用作精确度要求非常高的定时任务调度 

                * 不会释放锁资源

public class ThreadYield implements Runnable{
	
	@Override
	public void run() {
		for (int i = 0; i < 10; i++) {
//			Thread.yield();
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println(Thread.currentThread().getName()+":"+i);
		}
	}

	public static void main(String[] args) {
		Thread t1=new Thread(new ThreadYield());
		Thread t2=new Thread(new ThreadYield());
		t1.start();
		t2.start();
	}
}

   * join(long millisec)方法

            * 等待该线程终止的时间最长为 millis 毫秒。

            * 默认是0毫秒:代表forever

            * 通常用于在main()主线程内,等待其它线程完成再结束main()主线程

import java.util.Date;

public class JoinTestThread implements Runnable{

	@Override
	public void run() {
		System.out.println(Thread.currentThread().getName()+" begin: "+new Date());
		try {
			Thread.sleep(5000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.out.println(Thread.currentThread().getName()+" end: "+new Date());
	}
	
	public static void main(String[] args) {
		Thread t1=new Thread(new JoinTestThread());
		Thread t2=new Thread(new JoinTestThread());
		t1.start();
		t2.start();
		System.out.println("main thread go out ");
	}

}

            

import java.util.Date;

public class JoinTestThread implements Runnable{

	@Override
	public void run() {
		System.out.println(Thread.currentThread().getName()+" begin: "+new Date());
		try {
			Thread.sleep(5000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.out.println(Thread.currentThread().getName()+" end: "+new Date());
	}
	
	public static void main(String[] args) {
		Thread t1=new Thread(new JoinTestThread());
		Thread t2=new Thread(new JoinTestThread());
		t1.start();
		t2.start();
		try {
			t1.join();
			t2.join();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println("main thread go out ");
	}

}

import java.util.Date;

public class JoinTestThread implements Runnable{

	@Override
	public void run() {
		System.out.println(Thread.currentThread().getName()+" begin: "+new Date());
		try {
			Thread.sleep(5000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.out.println(Thread.currentThread().getName()+" end: "+new Date());
	}
	
	public static void main(String[] args) {
		Thread t1=new Thread(new JoinTestThread());
		Thread t2=new Thread(new JoinTestThread());
		t1.start();
		t2.start();
		try {
			t1.join(1000);
			t2.join(1000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println("main thread go out ");
	}

}

 

* 线程同步

    * https://www.cnblogs.com/XHJT/p/3897440.html

    * 用线程同步解决问题

* 线程间通讯

    * wait/notify

    * https://www.cnblogs.com/hapjin/p/5492619.html

*  线程池

    * https://blog.csdn.net/liuchuanhong1/article/details/52042182

* 定时任务调度

    * Timer timer,线程池,quart

    * http://blog.51cto.com/zhangfengzhe/2064092

* 参考链接

    * http://www.runoob.com/java/java-multithreading.html

    * https://www.cnblogs.com/wxd0108/p/5479442.html

猜你喜欢

转载自blog.csdn.net/linzhaoliangyan/article/details/88573186