首页 > 编程语言 > 详细

Java进阶——线程与多线程

时间:2020-09-17 13:55:51      阅读:48      评论:0      收藏:0      [点我收藏+]

线程和多线程

技术分享图片

概念

  • 程序

    程序是一段静态代码。
  • 进程

    进程是程序的一次动态执行过程(从代码加载、执行、执行完毕的完整过程)。进程是资源分配的最小单位。
  • 线程

    线程是CPU调度的最小执行单位。程序执行过程中可以产生多个线程。

进程和线程的区别

  1. 进程:一个应用程序对应一个进程;进程是资源分配的最小单位;通过多线程占据系统资源;进程之间数据状态完全独立。
  2. 线程:一个进程可以有多个线程;线程是执行程序的最小单元;线程是占用CPU的基本单位;线程之间共享一块内存空间。

线程的生命周期

  • 新建状态

    线程对象创建,还未调用start()方法。
  • 就绪状态

    调用start()方法,但调度程序还未将其选为可运行线程。
  • 运行状态

    线程调度程序从可运行池中选择一个线程作为当前线程。
  • 阻塞状态

    • 等待状态
    • 阻塞状态
    • 睡眠状态
      线程是活的,但没有条件运行;当某事件发生后,可返回到就绪状态。
  • 死亡状态

    线程run()方法完成。线程一旦死亡,不可复生(调用start()会抛异常)。

Java的线程

主线程

每个Java程序都有一个默认的主线程。

当JVM加载代码,发现main方法后,会立即启动一个线程(主线程)
主线程特点

  1. 产生其他子线程的线程
  2. 不一定是最后完成执行的线程

创建线程

  1. 继承Thread类
    • 重写run()方法
    • new一个线程对象
    • 调用对象的start()方法启动线程
  2. 实现Runnable接口
    • 实现run()方法
    • 创建一个Runnable类的对象
    • 创建Thread类对象,将Runnable对象作为参数
    • 调用Thread对象的start()方法启动线程
  • 一个线程只能被启动一次run()方法执行结束后,线程结束。
  • 一个程序多个线程,线程只能保证开始时间,结束时间和执行顺序无法确定。
  • 线程调度采用队列形式;JVM线程调度程序决定执行就绪状态的某个线程。
  • 运行的线程有名字
    • 可通过JVM默认线程名字
    • 自定义线程名字setName()
//给子线程命名
//默认为Thread-
Thread1 t1=new Thread1();
Thread t=new Thread(t1);
t.start();
t.setName("t1");

//获取主线程,并命名
Thread.currentThread().setName("mm");
System.out.println(Thread.currentThread().getName());

创建线程方法对比

  1. 继承Thread类
    1. 编写简单,访问当前线程this
    2. java是单继承机制,不能继承其他父类;没有达到资源共享
  2. 实现Runnable接口
    1. 编程复杂,访问当前线程Thread.currentThread()
    2. java是多接口实现,可继承其他类;可多个线程共享同一个目标对象。
//Runnable
//多线程解决同一问题
Thread1 t1 = new Thread1();
new Thread(t1).start();
new Thread(t1).start();
new Thread(t1).start();

//Thread
//资源不共享
Thread t1 = new Thread1();
Thread t2 = new Thread2();
Thread t3 = new Thread3();
t1.start();
t2.start();
t3.start();

线程的方法

方法名 功能
start() 启动线程,让线程从新建状态进入就绪状态队列
run() 普通方法,线程对象被调度后执行的操作
sleep() 暂停线程的执行,休眠线程
yield() 暂停正在执行的线程,让同等优先级的线程运行
join() 暂停当前线程的执行,等调用该方法的线程执行完后,线程返回就绪状态
interrupt() 唤醒休眠的线程
stop() 终止线程
isAlive() 测试线程的状态;新建/死亡状态=false
currentThread() 返回当前正在执行线程对象的引用
//输出 (新建状态、死亡状态为false)
//false  false
//join方法将主线程暂停,先执行Thread1线程
Thread1 t1=new Thread1();
Thread t=new Thread(t1);
System.out.print(t.isAlive());
t.start();
t.join();
System.out.print(t.isAlive());

设置线程优先级

  • 通过Thread的setPriority()方法设置线程优先级
    • Thread.MIN_PRIORITY ——1
    • Thread.NORM_PRIORITY ——5
    • Thread.MAX_PRIORUTY ——10
  • 通过thread的getPriority()方法得到线程优先级
  • 线程默认优先级为创建其的运行状态线程的优先级

线程让步

当线程池中的线程具有相同优先级

  1. 选择一个线程运行,知道线程阻塞或运行结束
  2. 时间分片,为线程池中每个线程提供均等运行机会

多线程运行时,JVM按优先级调度,级别相同的由操作系统按时间片分配。

阻止线程执行

线程睡眠 sleep()

当线程睡眠时,暂停执行;苏醒前不会回到就绪状态;
当睡眠时间到期,线程回到就绪状态。

  • 线程睡眠可帮助其他线程获得运行机会的最好方法
  • 线程苏醒后,返回到就绪状态
  • sleep()指定时间为最短睡眠时间
  • sleep()为静态方法,只能控制当前运行的线程

线程等待 yield()

线程让步,暂停当前正在执行的线程对象,并执行同等优先级的其他线程
yield()使线程从运行状态——>就绪状态;让步的线程也有可能被线程调度程序选中。

线程阻塞 join()

线程A中调用线程B的join()方法,让线程A置于线程B的尾部。
在线程B执行完毕之前,线程A一直处于阻塞状态,只有当B线程执行完毕时,A线程才能继续执行

当join(100)带有参数时,如果A线程中掉用B线程的join(100),则表示A线程会等待B线程执行100毫秒,100毫秒过后,A、B线程并行执行;同时join(0)==join()

join方法必须在线程start方法调用之后调用才有意义

在主线程中执行程序:创建A、B两个子线程,首先调用线程A的start()方法执行线程A;
调用线程A的join()方法,使主线程进入阻塞状态,只有当线程A执行完毕后,才能执行主线程
线程A执行完毕后,主线程才可执行,调用线程B的start()方法执行线程B。

Thread a = new ThreadA();
Thread b = new ThreadB();
//线程A开始执行
a.start();
//线程A调用join()
a.join();
//线程B开始执行
b.start();

多线程

对象互斥锁

Java每个对象都对应一个互斥锁的标记。
每个对象只有一个锁(lock)与之相关联.
synchronized关键字与对象互斥锁联合使用,保证对象在任意时刻只能由一个线程访问。
避免多个线程进行访问导致数据不同步的问题。

  • 修饰代码块,该代码块在任意时刻只能由一个线程访问
    • 作用范围:代码块{}的内容
    • 作用对象:调用该代码块的对象
//实现Runnable接口
public class Thread1 implements Runnable {

	private static int count;

	@Override
	public void run() {
		// TODO Auto-generated method stub
		// 1. 修饰代码块
		// 同步语句块
		synchronized (this) {
			for (int i = 0; i < 5; i++) {
				count++;
				System.out.println(Thread.currentThread().getName() + ":" + count);
			}
		}

	}

}
// 同一个对象
//实现资源共享、资源同步
Thread1 thread = new Thread1();
new Thread(thread).start();
new Thread(thread).start();

// 不同对象
//实现资源同步、多线程进行处理
Thread1 thread = new Thread1();
Thread1 thread1 = new Thread1();
new Thread(thread).start();
new Thread(thread1).start();
  • 修饰方法,表示该方法在任意时刻只能由一个线程访问
    • 作用范围:方法内容
    • 作用对象:调用方法的对象
    • 关键字synchronized不可继承
    • 定义接口不可使用关键字synchronized修饰
    • 构造方法不可使用关键字synchronized修饰,但可以用同步代码块
public synchronized void print(){
      //todo
}
  • 修饰静态方法,
    • 作用范围:静态方法内容
    • 作用对象:这个类的所有对象
public synchronized static void print(){
      //todo
}
  • 修饰类,表示该类的所有对象公用一把锁
    • 作用范围:{}包括的所有内容
    • 作用对象:这个类的所有对象
class ClassTest{
      public void method(){
            synchronized(ClassTest.class){
                  //todo
            }
      }
}

多线程同步

为了更好的解决多个交互线程之间的运行进度。
引入wait()方法与notify()方法
wait()方法:使当前线程进行等待状态
notify()方法:通知那些等待该对象锁的其他线程,使其重新获取该对象的对象锁。

  • wait()notify()方法必须配合synchronized关键字使用
  • wait()会释放锁,notify()不会释放锁
  • wait()方法执行后,执行interrupt()方法会报异常

死锁

死锁:当两个或两个以上的线程在执行过程中时,因争夺资源造成互相等待,若无外力作用,线程都无法推进下去的现象。
必要条件

  1. 互斥条件
    • 线程对分配到的资源进行排他性使用;其他线程不可使用。
  2. 请求、保持条件
    • 线程保持至少一个资源,但又提出新的资源请求。
  3. 不可剥夺条件
    • 线程获得的资源在使用完之前,不可被剥夺;只能在使用完后自主释放。
  4. 环路等待条件
    • 发生死锁时,必然存在线程请求资源、资源被另一线程占用的环。

线程池

[java 线程方法join的简单总结][join]
[Java中Runnable和Thread的区别][thread]
[Java中Synchronized的用法][synchronized]
[join]:https://www.cnblogs.com/lcplcpjava/p/6896904.html
[thread]:https://developer.51cto.com/art/201203/321042.htm
[synchronized]:https://www.cnblogs.com/fnlingnzb-learner/p/10335662.html

Java进阶——线程与多线程

原文:https://www.cnblogs.com/occlive/p/13673559.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!