进程和线程
说到线程,不得不提到进程,首先我们宏观的了解一下进程和线程。
进程,进程是具有一定独立功能的程序,进程是系统进行资源分配和调度的一个独立单位,竟争计算机系统资源的基本单位。每一个进程都有一个自己的物理地址空间,即进程空间或(虚空间)。一个进程崩溃后,在保护模式下不会对其它进程产生影响。
线程,线程是进程下的一个实体,由CPU调度和分配的基本单位,线程基本上不拥有系统资源,只拥有一点在运行过程中必不可少的资源。但是它可以和同属于一个进程的其他线程共享进程所拥有的资源。在网络或多用户环境下,一个服务器通常需要接收大量且不确定数量用户的并发请求,为每一个请求都创建一个进程显然是行不通的,无论是从系统资源开销方面或是响应用户请求的效率方面来看。因此,操作系统中线程的概念便被引进了。一个线程可以创建和撤销另一个线程,同一个进程中的多个线程之间可以并发执行。
创建线程
创建线程有如下两种方式:
1,继承java.lang.Thread类,该类有两个主要的方法:
run方法:在流程中直接线程的run方法方法,需要等待run方法体运行完毕才能执行下面的代码;这样就类似一个普通的方法,没有达到写线程目的。
start方法:调用该方法用来启动线程,真正实现了多线程的运行。调用该方法就无需等待run方法体执行完毕,可以继续执行主线程下面的代码。调用start()方法来启动一个线程,这时此线程是处于就绪状态,并没有运行。当该线程分到CPU时间片时,才调用run()来完成其运行操作,这里run()方法称为线程体,它包含了要执行的这个线程的内容,run()方法运行结束,此线程终止。然后CPU再调度其它线程。一个线程只能调用一次start()方法,第二次启动时将会抛出java.lang.IllegalThreadExcetpion异常。
2,实现Runnable接口
线程生命周期
初始状态:用new语句创建的线程对象时,这时候该线程就处于初始状态,跟普通的Java对象一样,仅仅分配了堆空间,不过要注意这时调用Thread的isAlive()方法,方法返回的是false。
可运行状态(就绪状态):线程创建以后,调用了该线程的start()方法,它就进入了就绪状态。这个状态的线程位于可运行池中,等待获得CPU的时间片。
运行状态:该线程占用CPU,执行run方法。
阻塞状态:线程处于阻塞状态时,JVM不会给线程分配CPU,直到线程重新进入就绪状态,它才有机会转到可运行状态。
阻塞的情况:
1、线程运行时,如果执行了某个对象的wait()方法,JVM就会把线程放到这个对象的等待队列中,可以通过notify和notifyAll方法唤醒。
2、线程试图获取每个对象的同步锁或者是某个类的类锁时,如果想要获取的锁被其他线程占用了,则JVM会把该线程放到这个对象的锁池中。
3、线程执行了sleep方法。
4、调用了其他线程的join方法,在等待其他线程执行完毕的时候。
5、线程发出了I/O请求,在等待I/O的时候。
终止状态:
1,运行完run方法就是终止状态,结束了线程的生命周期。
2,异常退出。
终止状态,调用Thread的isAlive()方法,方法返回的是false,除了初始状态和终止状态,其他都是true。
线程调度
JVM是采用抢占式的调度方式,线程的调度不是跨平台的,他不但取决于JVM,也和操作系统密不可分。
如果要调度线程,可以采用以下几种方式:
1,调整线程的优先级。
线程的优先级可以通过Thread类的setPriority(int)和getPriority()来设置和读取线程的优先级,线程的优先级Thread定义了MAX_PRIORITY、NORM_PRIORITY、MIN_PRIORITY,用户也不适用Thread类中定义的这三个值,但是必须在MIN_PRIORITY < 自定义优先级值 < MAX_PRIORITY,为了保证代码在各个系统的移植性,最好用Thread类中定义的这三个值。
2,线程调用Thread.sleep方法。
调用Thread.sleep方法可以让线程放弃CPU,转为阻塞状态。
3,线程调用Thread.yield方法。
调用Thread.yield方法时,如果此时有相同优先级的其它线程处于就绪状态,那么yield()方法CPU执行权给同等级的线程,如果没有相同级别的线程在等待CPU的执行权,则该线程继续执行。
4,线程调用另一个线程的join()方法
调用另一个线程的join()方法,当前线程会等待被调用的线程执行完毕后在执行。比如线程A调用了线程B的join方法,那么线程A会等待线程B执行完毕后在执行。
sleep和yield的区别
Thread.sleep方法和Thread.yield方法都是Thread类中的静态变量,都会让出CPU执行权给其他线程,他们的区别在于:
1,Thread.sleep方法让出CPU执行权,但是不管线程优先级的高低,仅仅把CPU的执行权让出来,这样无论线程的优先级是高还是低都有机会获取到CPU的执行权。Thread.yield方法只会给相同优先级或者更高优先级的线程一个运行的机会。
2,Thread.sleep(long millis)方法阻塞状态,参数millis指定睡眠时间;当线程执行Thread.yield()方法后,将转到可运行状态(就绪状态)。
3,Thread.sleep方法抛出InterruptedException异常,而yield()方法没有声明抛出任何异常。
4,Thread.sleep方法比Thread.yield方法具有更好的移植性。
线程同步
线程同步是为了保证操作的原子性,在Java规范中,对于基本类型的赋值或者返回值操作,是原子操作。只有long,double这些占用64位的会有些特殊,因为JVM的基础储存单位是32位,所以64位无法再一个时钟内完成。
为了保证操作的原子性,Java引入的同步快的概念,具体的做法是在原子操作的程序块前加synchronized标记,这样的代码块就是同步代码块了。同步代码块需要一个锁,就叫做同步锁,Java中每个对象和类都有且只有一个同步锁,一个同步锁只能被一个线程拥有。后续写一篇文章专门说一下锁。
线程通信
Java.lang.Object类中提供了两个用于线程通信的方法
1,wait:执行该方法的线程释放了对象锁,JVM会把该线程放到对象的等待池中,该线程等待被其他线程通过notify/notifyAll唤醒。
2,notify:执行该方法的线程唤醒在对象的等待池中等待的一个线程,JVM从对象的等待池中随机选择一个线程,把它转到对象的锁池中。
死锁
当一个线程等待由另一个线程持有的锁,而后者正在等待已被第一个线程持有的锁时,就会发生死锁。
一个简单避免死锁的方式是让每个线程按照同样的顺序去访问他们。
分享到:
相关推荐
多线程是Java的一大亮点,它使得程序能够同时执行多个任务。线程同步、互斥、死锁等概念,以及synchronized关键字、wait()、notify()方法的使用,都是Java并发编程的重要知识点。 IO流处理是Java与外部世界的桥梁,...
《Java编程那些事儿》是陈跃峰先生的一本关于Java编程技术的著作,该书深入浅出地介绍了Java编程中的重要概念和实践技巧。这里我们主要聚焦于压缩包中的部分内容,涉及Java集合框架、时间日期处理、文件操作、多线程...
5. **多线程**:线程的概念、创建和管理,同步机制如synchronized关键字、wait()、notify()方法,以及线程池的使用。 6. **Java内存管理**:深入理解Java垃圾回收机制,了解堆内存和栈内存的区别,以及内存泄漏和...
此外,文档可能会讲解到异常处理、文件I/O操作、集合框架(如ArrayList、LinkedList、HashMap等)以及多线程编程,这些都是实际开发中不可或缺的知识点。 在类库和API的使用方面,读者可能会学到如何使用Java标准库...
- 并发优化:合理利用多线程,避免线程安全问题,使用并发集合类如ConcurrentHashMap、CopyOnWriteArrayList等。 - JIT优化:让热点代码尽早进入编译,提升运行效率。 4. 实战经验: - 使用JVM工具定期监控应用...
对于大规模、高性能的爬虫项目,Java可能更适合,因为它能够处理更高的并发量,且在多线程和分布式爬虫方面有优势。而Python则适合小型项目或快速开发,其丰富的库和社区支持使得问题解决更快速。 文件名“fgsfg”...
深入理解Java的核心语言特性,如面向对象编程、异常处理、泛型、多线程等,对于编写高效稳定的Java应用至关重要。 #### 框架与库 熟悉Spring Boot、Hibernate等流行框架和技术栈可以帮助Java程序员快速搭建应用程序...
6. **多线程**:Java支持多线程编程,通过Thread类或Runnable接口创建和管理线程,理解同步机制如synchronized关键字、wait()、notify()等,可以实现并发操作。 7. **反射**:反射机制允许在运行时动态获取类的信息...
学习Java,首先要掌握其基本语法,包括类、对象、接口、包等概念,以及异常处理、多线程、I/O流等核心特性。同时,熟悉Java集合框架(如List、Set、Map)和设计模式也是必不可少的,它们是解决实际问题的基础。 ...
6. **多线程编程**:Java提供了丰富的多线程支持,书里会详细阐述线程的创建、同步、通信以及死锁避免等技术。 7. **网络编程**:Java程序员经常需要处理网络通信,书中会讲解Socket编程、HTTP协议以及基于Java的...