完全转帖:
黑马程序员 _Java中的进程、线程和多线程
一、进程:进程就是一个正在执行的程序。
二、线程:线程是进程执行的一条线索或路径。进程中至少有一个线程存在。
三、多线程:顾名思义,一个进程中的多个线程。
线程的开始:当要运行一个程序时,JVM首先会找到main函数,然后从main函数开始执行(也就是说,程序是从main函数开始运行的),
此时,程序就成为一个进程,既然是进程肯定有线程的存在。此时的线程就是主线程,主线程会向下顺序执行代码。
如果程序中存在一个庞大的循环语句,主程序就会一直在这里运行,直到循环语句结束,下面的代码才能被执行到。
这可能要花费较长的时间,影响了程序运行的效率。所以,为了提高程序的效率,就引入了多线程。由主线程开辟另一个或多个线程,
让这些线程都去执行代码。
创建线程的两种方法:
一、1.定义一个类继承Thread,重写Thread中的run方法,run方法中存放自定义的要让该程序执行的代码。
2.用子类new一个线程对象,线程对象调用线程中的start方法,来开启线程。
class Demo extends Thread
{
public void run()
{
System.out.println("我要运行的代码");
}
}
class ThreadDemo
{
public static void main(String[] args)
{
Demo d=new Demo();
d.start();
}
}
二、1.实现Runnable接口,复写run方法。
2.用Thread来new一个线程对象,并将Runnable的子类对象传入Thread的构造方法中。
class Demo implements Runnable
{
public void run()
{
System.out.println("Hello World");
}
}
class ThreadDemo
{
public static void main(String[] args)
{
Thread t=new Thread(new Demo());
t.start();
两种方法的比较:Java中的类只能继承一个类,第一种方法中,当Demo继承了Thread之后,就不能再继承其
他类,但Demo很有可能是另一个类的子类,这时就出现了局限。第二种方法中,Demo实现了接口Runnable,
同时还能成为其他类的子类,使用较多。
单核状态下,在某一时刻只有一个线程在运行。某一时刻可能有多个进程,每个进程中也可能有多个线程,CPU就在这
些线程间来回的切换,线程也只有在获得CPU的执行权时才能运行。
四、线程的四个运行状态:
1.运行:既有执行资格又有执行权。
2.堵塞(临时状态):有执行资格没有执行权。等待CPU的执行权,一旦获得就进入运行状态。
3.冻结:没有执行资格也没有执行权。CPU不会分配执行权给它,直到它被唤醒。
从运行状态进入冻结状态:sleep(time)或wait();
被唤醒:sleep的时间到或notify().
4.消亡:run方法结束,或stop()。
五、多线程的安全问题:
什么时候会出现线程安全问题?
满足3个条件时,会出现:
1.存在多个线程
2.多个线程共享数据(共享数据一般是成员变量,局部变量不是)。
3.操作共享数据的语句至少要有两条。
出现安全问题的原因:多个线程执行共享数据的代码块时,其中的一个线程还没有执行完代码块,另一个线程就开始执行代码块,
这会造成共享数据的错误。从而出现安全问题。
六、多线程安全问题的解决:同步。因为同步可以保证多线程代码只能被持有锁的线程运行,其它线程不能运行。
任意时刻,一个锁只能被一个线程拥有。
(持有锁的线程会在执行完多线程代码时释放锁,这样锁就能被其它线程拥有。)
1.同步代码块;将要同步的代码放synchronized中,并加锁。
synchronized(对象)//只要是个对象就行,这个对象就是锁
{
要被同步的代码;
}
2.同步函数:synchronized能将代码封装并同步,函数只能将代码封装,所以用synchronized修饰函数,让函数既能封装又能同步。
同步函数也有锁。非静态同步函数的锁是this,同步函数所属对象的引用;静态同步函数的锁是:类名.class,函数
所属类的字节码文件对象。
需注意的是:1.不需要同步的代码不要放入同步函数中。2.synchronized放在函数返回值类型前
public synchronized void show()
{
要被同步的代码;
}
找到 要被同步的代码 的方法:
1.先找到 多线程所有要运行的代码(可能是一个run方法被多个线程使用同,也可能是一个run方法只被其中的一个线程使用);
2.多线程的共享数据(一般成员变量都是);
3.多线程要运行的代码 中操作 共享数据 的代码就是 要被同步的代码。
注意:有时使用了同步仍然不成功,出现这种现象的原因可能是:需要被同步的代码可能放在程序的不同位置,有的代码要用到同步代码块,有的要用同步函数
那么怎么才能使同步成功?
1.首先判断要被同步的代码是否正确,确定所有要被同步的代码都被同步了。
2.同步代码块和同步函数中的锁是同一个锁,即必须是 多个线程使用同一个锁。。
七、同步时要避免的问题:死锁。
同步嵌套同步时会发生死锁。
if(flag)
{
while(true)
{
synchronized(Lock1)
{
System.out.println(".......");
synchronized(Lock2)
{
System.out.println("???????");
}
}
}
}
else
{
while(true)
{
synchronized(Lock2)
{
System.out.println(".......");
synchronized(Lock1)
{
System.out.println("???????");
}
}
}
}
当线程1满足if条件,只执行到 System.out.println("......."); 间片到了,线程2获取执行权,并满足else条件时,执行到一半,时间片也到了,
此时,线程一持有锁Lock1,线程二持有锁Lock2,都在等待对方执行完后释放锁,但是都无法执行完,这时就会形成死锁。
八、线程间通信;多个线程共享一段数据,但是对数据的操作不同。
等待唤醒机制:wait(); notify(); notifyAll();三个方法都是Object类中的方法,都用来操作持有锁的线程,所以都只能用于同步。
notify()方法,唤醒内存中等待线程池中的第一个在等待的线程。
方法的使用:1.这三个方法在使用时,都必须标明它们将要操作的线程持有的锁,
即lock.wait()、lock.notify()、lock.notifyAll()
2.等待和唤醒只能是同一个锁。即,只有同一个锁上的被等待线程,可以被同一个锁上的notify唤醒。
不可以对不同锁中的线程进行唤醒。
为什么三个方法都在Object中定义?
因为使用这三个方法在使用时,都通过锁来调用,而锁可以是任意对象,所以要定义到Object中。
一个生产者一个消费者:if notify
多个生产者多个消费者:while notifyAll
升级的多个生产者多个消费者,JDK1.5新特性:1.synchronized被Lock替代--->lock.lock()获取锁 lock.unlock()释放锁
2.wait、notify、notifyAll三个方法被Condition代替--->condition.await()等待,condition.signal()唤醒一个线程,condition.signalAll()唤醒所有线程。
通过不同的Condition实例condition_con.signal() condition_pro.signal(),可以实现 只唤醒消费者线程,旧方法只能唤醒所有线程。
获取Condition实例,用Lock中的方法,lock.newCondition();
停止线程:有两种方法--->run方法结束、stop()
stop方法已经过期,所以只能使run方法结束。
分析:程序中之所以使用多线程,通常是因为出现了较大的循环结构,即,多线程运行的代码通常是循环结构。
所以,只要控制住循环,就可以使run方法结束,也就结束了线程。使用标记。
特殊情况:当线程处于冻结状态(wait()了),就不会读取到标记,那么线程就无法结束。
这时,可以使用Thread中的interrupted()中断线程,中断线程是将线程从冻结状态强制恢复到运行状态,清除掉了冻结状态,这样就能继续读取标记。
九、Thread类中的几个方法
1.setDaemon():可以将线程标记为守护线程,该方法必须在启动线程前调用---->thread.setDaemon(); thread.start();
守护线程可以理解为后台线程,后台线程在开启之后会和前台线程共同抢占cup执行权,当所有前台线程都结束时,后台线程会自动结束。这样也可以实现停止线程。
2.join():等待该线程终止.当A线程执行了B线程的.join()方法时,A就会等待,等B线程都执行完,A才会执行。-->b.join();
若B线程执行到中间wait了,那么A就会一直等,这时可以使用b.interupt()强行使B线程切换到运行状态。
3.setPriority():设置线程的优先级。线程的优先级表示抢占CPU的频率,值越大优先级越高。共有10个等级,从1到10,所有线程默认的优先级是5。
Thread类中,1,5,10分别用静态常量表示:MIN_PRIORITY、MORM_PRIORITY、MAX_PRIORITY
使用--->thread.setPriority(MAX_PRIORITY); thread.setPriority(3);
synchronized关键字详解:
synchronized 方法:通过在方法声明中加入 synchronized关键字来声明 synchronized 方法。如:
public synchronized void accessVal(int newVal);
synchronized 方法控制对类成员变量的访问:每个类实例对应一把锁,每个 synchronized 方法都必须获得调用该方法的类实例的锁方能执行,否则所属线程阻塞,方法一旦执行,就独占该锁,直到从该方法返回时才将锁释放,此后被阻塞的线程方能获得该锁,重新进入可执行状态。这种机制确保了同一时刻对于每一个类实例,其所有声明为 synchronized 的成员函数中至多只有一个处于可执行状态(因为至多只有一个能够获得该类实例对应的锁),从而有效避免了类成员变量的访问冲突(只要所有可能访问类成员变量的方法均被声明为 synchronized)。
在 Java 中,不光是类实例,每一个类也对应一把锁,这样我们也可将类的静态成员函数声明为 synchronized ,以控制其对类的静态成员变量的访问。
synchronized 方法的缺陷:若将一个大的方法声明为synchronized 将会大大影响效率,典型地,若将线程类的方法 run() 声明为 synchronized ,由于在线程的整个生命期内它一直在运行,因此将导致它对本类任何 synchronized 方法的调用都永远不会成功。当然我们可以通过将访问类成员变量的代码放到专门的方法中,将其声明为 synchronized ,并在主方法中调用来解决这一问题,但是 Java 为我们提供了更好的解决办法,那就是 synchronized 块。
2. synchronized 块:通过 synchronized关键字来声明synchronized 块。语法如下:
synchronized(syncObject) {
//允许访问控制的代码
}
synchronized 块是这样一个代码块,其中的代码必须获得对象 syncObject (如前所述,可以是类实例或类)的锁方能执行,具体机制同前所述。由于可以针对任意代码块,且可任意指定上锁的对象,故灵活性较高。
对 synchronized(this)的一些理解
一、当两个并发线程访问同一个对象object中的这个synchronized(this)同步代码块时,一个时间内只能有一个线程得到执行。另一个线程必须等待当前线程执行完这个代码块以后才能执行该代码块。
二、然而,当一个线程访问object的一个synchronized(this)同步代码块时,另一个线程仍然可以访问该object中的非synchronized(this)同步代码块。
三、尤其关键的是,当一个线程访问object的一个synchronized(this)同步代码块时,其他线程对object中所有其它 synchronized(this)同步代码块的访问将被阻塞。
四、第三个例子同样适用其它同步代码块。也就是说,当一个线程访问object的一个synchronized(this)同步代码块时,它就获得了这个object的对象锁。结果,其它线程对该 object对象所有同步代码部分的访问都被暂时阻塞。
五、以上规则对其它对象锁同样适用
分享到:
相关推荐
"JAVA线程与进程的区别" JAVA语言中,线程(Thread)和进程(Process)是两个基本概念,它们都是操作系统所体会的程序运行的基本单元,系统利用该基本单元实现系统对应用的并发性。但是,它们之间有着本质的区别。 ...
2. **线程的启动与生命周期**:通过调用Thread对象的start()方法启动线程,线程会经历新建(New)、就绪(Runnable)、运行(Running)、等待/阻塞(Blocked/Wait)和终止(Terminated)五个状态。 3. **线程同步**...
本文中,我们还提供了一些相关的Java多线程编程资源,例如《Java进程与线程操作技巧总结》、《Java数据结构与算法教程》、《Java操作DOM节点技巧总结》等。这些资源可以帮助读者更好地理解和掌握Java多线程编程的...
综上所述,这个“JAVA进程管理模拟”项目涵盖了Java进程与线程管理、图形用户界面设计、事件驱动编程和线程同步等多个核心知识点。通过这个项目,开发者不仅可以深入理解这些概念,还能直观地观察到它们在实际应用中...
了解和掌握进程与线程的区别和交互机制,对于编写高效的多线程程序至关重要。通过互斥锁和信号量等同步工具,我们可以有效地控制线程的并发访问,确保程序的正确性和性能。在实际开发中,合理地使用进程和线程,结合...
JAva进程和线程管理
为了获取这些信息,我们可以使用jstack工具,它是Java开发工具包(JDK)的一部分,能够输出Java进程的线程堆栈跟踪信息。 jstack命令通常可以输出以下类型的信息: 1. 线程的完整堆栈跟踪,包括本地方法。 2. 显示...
在计算机科学中,程序、进程和线程是操作系统的基础概念,尤其在Java编程语言中,理解和掌握这些概念对于开发高效、并发的软件至关重要。本文将深入探讨Java的多线程特性,以及程序、进程和线程的基本定义和它们之间...
"操作系统中的进程、线程与Java的多线程" 操作系统中的进程是指特定的代码序列在指定数据集上的一次执行活动,是指并行程 序的的一次执行过程。在Windows 95中,就是一个EXE文件的执行过程。这是一个动态概念,具有...
wait(), notify()和notifyAll()方法用于线程间的同步与通信,实现线程间的数据交换。 此外,Java还提供了Callable和Future接口,以及Executor框架,用于创建带返回值的线程和管理线程池,这在处理复杂并发场景时...
在计算机科学领域,进程与线程是操作系统中最基础且至关重要的概念。进程是程序执行时的一个实例,每个进程都有自己的独立内存空间,包括代码、数据、堆栈等资源。线程则是进程内的一个执行单元,它共享进程的内存...
进程和线程是计算机操作系统中的两个基本概念,对于任何软件开发者,尤其是系统级或服务器...通过阅读“进程和线程.doc”文档,可以进一步深入学习这两个主题的细节,包括它们的生命周期、调度策略、同步与通信方法等。
#### 一、Java进程与线程概述 在Java中,一个JVM(Java虚拟机)实例本质上就是一个进程。进程在Java中由`java.lang.Process`类表示,它允许Java程序与操作系统进行交互,执行外部程序。进程间的数据空间是完全隔离...
Java多线程与并发编程是Java开发中至关重要的一部分,它涉及到如何高效地利用CPU资源,以实现程序的并行执行。在操作系统层面,多任务和多进程是通过分配不同的内存空间来实现的,而线程则共享同一进程的内存,这...
`bin`目录可能包含编译后的可执行文件或脚本,用于启动和管理这些Java进程。为了深入了解实现细节,需要查看源代码并理解其逻辑。 总的来说,Java实现守护进程、监听进程以及管理多个进程和JVM涉及多线程、网络编程...
Java并发编程是Java开发中的重要领域,涉及到进程与线程的概念,这两个概念是理解多任务执行的基础。在现代计算机系统中,进程和线程被广泛用于实现高效的资源管理和任务调度。 **1. 进程** 进程是操作系统分配...
本文将详细探讨Java中的线程和进程的区别,以及它们在程序设计和运行时的角色和重要性 理解线程和进程的区别对于Java程序员来说至关重要。线程提供了一种高效的方式来实现并发执行,而进程则是操作系统资源分配的...
更多java相关内容感兴趣的读者可查看本站专题:《Java进程与线程操作技巧总结》、《Java数据结构与算法教程》、《Java操作DOM节点技巧总结》、《Java文件与目录操作技巧汇总》和《Java缓存操作技巧汇总》希望本文所...