进程:
进程是用于指一个正在运行的可执行程序,它可以包含多个线程,注意:一个没有运行的程序不是一个进程。
进程的特点:每一个进程都有自己的独立的一块内存空间、一组资源系统。其内部数据和状态都是完全独立的。
进程的优点是提高CPU运行效率,在同一时间内执行多个程序,即并发执行。但是从严格上讲,也不是绝对的同一时刻执行多个程序,只不过CPU在执行时通过时间片等调度算法不同进程高速切换,由于速度太快,所以我感觉不到在切换 而已。
线程:
线程在一个进程 中负责了代码的执行,就是进程中执行路径,
可以理解为进程的多条执行线索,每条线索又对应着各自独立的生命周期。线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。一个线程可以创建和撤销另一个线程,同一个进程中的多个线程之间可以并发执行。
运行任何一个java程序,jvm在运行的时候都会创建一个main线程执行main方法中所有代码。
一个java应用程序至少有两个线程, 一个是主线程负责main方法代码的执行,一个是垃圾回收器线程,负责了回收垃圾。
Java中的线程要经历4个过程
1.创建
创建一个Java线程常见的有两种方式:
继承Thread类和实现Runnable接口这两种方式。
2.执行
线程创建后仅仅占有了内存资源,在JVM管理的线程中还没有该线程,该线程必须调用start方法通知JVM,这样JVM就会知道又有一个新的线程排队等候了。如果当前线程轮到了CPU的使用权限的话,当前线程就会继续执行。
3.中断
a.JVM将CPU的使用权限从当前线程切换到其它线程,使本线程让出CPU的使用权限而处于中断状态。
b.线程在执行过程中调用了sleep方法,使当前线程处于休眠状态。
c.线程在执行的过程中调用wait方法
d.线程在使用cpu资源期间,执行了某个操作而进如阻塞状态。
4.死亡
死亡的线程不在具有执行能力。线程死亡的原因有二:
a.线程正常运行结束而引起的死亡,即run方法执行完毕。
b.线程被提前强制终止。
多线程的好处:
1. 解决了一个进程能同时执行多个任务的问题。
2. 提高了资源的利用率。
多线程 的弊端:
1. 增加cpu的负担。
2. 降低了一个进程中线程的执行概率。
3. 引发了线程安全 问题。
4. 出现了死锁现象。
线程的创建方式:
第一种创建方式:创建Thread的子类
创建Thread类的子类,并重写run()方法,该方法代表了该线程完成的任务。run方法为线程执行体。
public class MyThread extends Thread {
public void run(){
System.out.println("MyThread running");
}
}
// 可以用如下方式创建并运行上述Thread子类
MyThread myThread = new MyThread();
myTread.start();```
一旦线程启动后start方法就会立即返回,而不会等待到run方法执行完毕才返回。就好像run方法是在另外一个cpu上执行一样。当run方法执行后,将会打印出字符串MyThread running。
也可以如下创建一个Thread的匿名子类:
Thread thread = new Thread(){
public void run(){
System.out.println("Thread Running");
}
};
thread.start();当新的线程的run方法执行以后,计算机将会打印出字符串
"Thread Running"```。
第二种创建方式:实现Runnable接口
第二种编写线程执行代码的方式是新建一个实现了java.lang.Runnable接口的类的实例,实例中的方法可以被线程调用。
public class MyRunnable implements Runnable {
public void run(){
System.out.println("MyRunnable running");
}
}```
为了使线程能够执行run()方法,需要在Thread类的构造函数中传入 MyRunnable的实例对象。示例如下:
Thread thread = new Thread(new MyRunnable());
thread.start();```
当线程运行时,它将会调用实现了Runnable接口的run方法。上例中将会打印出"MyRunnable running"
。
同样,也可以创建一个实现了Runnable接口的匿名类,如下所示:
Runnable myRunnable = new Runnable(){
public void run(){
System.out.println("Runnable running");
}
}
Thread thread = new Thread(myRunnable);
thread.start();
Java线程间通讯的小例子:模拟生产者和消费者,生产者生产一个,消费者购买一个,生产者未生产完成消费者就不能购买,需要使用到Object类的wait()和notify()方法,及锁对象
package com.sey.thread1;
/*
* 需求:模拟生产者和消费者,生产者生产苹果和香蕉,消费者购买,并且消费者购买一个生产者才会生产一个,不浪费产品
*
*/
// 产品类
class Product {
String name; // 产品的名称
double money; // 产品的价格
boolean flag = false; // 是否生产完成的标识,当生产完成时为true,销售完成后为false
}
// 生产者
class Producer extends Thread {
Product p; // 产品
public Producer(Product p) {
this.p = p;
}
@Override
public void run() {
int i = 0;
while(true) {
synchronized(p) {
// 当产品销售完成的时候,生产产品
if (p.flag == false) {
if (i % 2 == 0) {
// 生产苹果
p.name = "苹果";
p.money = 6.0;
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} else {
// 生产香蕉
p.name = "香蕉";
p.money = 3.0;
}
System.out.println("生产者生产了 " + p.name + "价格 " + p.money);
p.flag = true; // 记录生产完成
p.notify(); // 通知消费者我已经生产完成,消费者好赶紧购买,其实是唤醒以```锁对象为标识符```的线程池中其中一个线程
i++;
} else {
try {
p.wait(); // 等待消费者购买
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
}
// 消费者
class Consumer extends Thread {
Product p; // 产品
public Consumer(Product p) {
this.p = p;
}
@Override
public void run() {
// 当生产者生产完成时消费者就可以购买了
while(true) {
synchronized(p){
if (p.flag == true) {
System.out.println("消费者购买了" + p.name + "价格 " + p.money);
p.flag = false;
p.notify(); // 通知生产者我已经购买,生产者好进入生产状态
} else {
try {
p.wait(); // 产品还未生产完成,等待生产者生产完成
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
}
public class Demo1 {
public static void main(String[] args) {
// 创建产品对象
Product p = new Product();
// 创建生产者对象
Producer producer = new Producer(p);
producer.start();
// 消费者对象
Consumer consumer = new Consumer(p);
consumer.start();
}
使用代码
关于多线程的题
-1.同步代码块synchronized() {}
要4个要注意事项:
a. 任意的一个对象都可以做为锁对象。
b. 在同步代码块中调用了sleep方法并不是释放锁对象的。
c. 只有真正存在线程安全问题的时候才使用同步代码块,否则会降低效率的。
d. 多线程操作的锁 对象必须 是唯一共享 的。否则无效。
0.出现线程安全问题的两个根本原因:
a. 存在两个或者两个以上 的线程对象,而且线程之间共享着一个资源。
b. 有多个语句操作了共享资源。
1.Java中多线程同步是什么?
答:java中的线程程同步,是为了实现对共享资源的访问,如果没有过线程程同步机制,则会导致,当
某个线程在修改某个共享变量时,另一个线程程同时正在使用或更新同一个共享变量就会导致程序出错
2.线程有几种有哪些实现方法?
答:有两种:第一种通过继承Thread类,但这种方式只能单继承,第二种方法,通过继承Runnable类,这样可以
实现多继承。
3.Thread.start()和Thread.run()有什么区别?
答:Thread.start()方法是启动线程的,通过调用它,使线程进入就绪状态,当处于就绪状态的进程获得CPU后,
JVM会自动调用Thread.run()方法,使进程进入运行状态。
4.为什么需要run()和start()方法,我们可以只用run()方法来完成任务吗?
答:因为j由java创建一个单独的线程不同于其他方法的调用,这项工作由java的start()方法来
实现,另一个好处就是任何一个对象都可以作为进程运行,只要继承了runnable()方法,这就避免了因为
继承了Thread类而造成的多继承问题。
5.sleep(),suspend()和wait()有什么区别?
答:sleep(mills)方法使进程处于睡眠mills(s),在这段时间内进程处于非运行状态,线程一直持有
对象的监视器。sleep()方法是一个静态方法,只对当前进程有效;suspend()方法,也是使
进程进入阻塞状态,其他进程调用resume方法可将其唤醒,使用suspend()方法,容易造成死锁问题
wait(mills)使进程进入阻塞状态,在mills(s)内可由其他进程调用notity()方法将其唤醒,sleep()方法
和wait()方法的区别在于,sleep()方法是进程调用的,而wait()方法是对象调用的。
6.当一个同步方法已经执行,线程能调用对象上的非同步实例吗?
答:可以的,一个非同步方法总是可以被调用的。同步方法:synchronized修饰,把访问临界区的方法
定义为同步方法,java中没有对非同步方法做任何检查,锁对象仅仅会检查同步方法。
7.在一个对象上两个进程可以调用两个不同的同步实例方法吗?
答:不能,因为,当一个进程同步实例后,便获得了该进程的对象锁,只有在该进程的对象锁释放后
才能执行其他同步方法。
8.什么事死锁?
答:死锁就是两个或两个以上的进程无限的阻塞,一直相互等待所需资源。
9.进程和线程有什么区别?
答:进程是一个具有独立运行功能的程序关于某个数据集合的一次活动,是动态的,一个进程包含了多个线程,
进程是资源分配的最小单位,线程是独立运行和独立调度的基本单位,不同的进程拥有不同的内存空间,多个线程
利用相同的数据空间,每个线程都有单的的栈内存来存储本地数据。
10.什么事线程安全,Vector是一个线程安全类吗?
答:如果一个进程中有多个线程,多个线程同时运行这段代码,如果每次运行的结果和单线程运行的结果是一样的,
而且其他变量的值也都是和预期 的是一样的,那么就是线程安全的。Vector是线程安全的类。
11.java中如何停止一个线程?
答:java中并没用提供停止线程的API,像stop(),wait()等操作容易造成死锁问题,解决的方法就是在run()方法里设计
循环,利用boolean变量作为条件跳出循环结束线程,或者取消任务,不然只能等待进程执行结束。
12.java中notify()和notifyAll()有什么区别?notifyAll()
答:notify()不能唤醒某个指定等待的线程,所以只能用于只有一个等待进程的情况下,而notifyAll()唤醒所有
进程并允许他们抢锁确保了至少有一个进程能继续运行。
13.为什么notify和wait方法要在同步块中调用?
答:主要是因为Java API强制要求这样做,如果你不这么做,你的代码会抛出IllegalMonitorStateException异常。
还有一个原因是为了避免wait和notify之间产生竞态条件。
14.什么是线程池?为什么要调用它?
答:线程的创建时花费大量的资源和时间,如果等任务来了再创建的话响应的时间会比较长,而且一个进程能够创建的线程
数量是有限的,为了解决这个问题,在程序一开始执行的时候,便创建若干个线程响应处理,被称为线程池。利用Excutor框架可以,
不同的线程池。
15.如何写代码实现生产者消费者问题?
答:后面代码有答案
16.如何避免死锁?
答:进程造成死锁的必要条件为:互斥条件,请求和保持条件,不剥夺条件,循环等待条件,因此要避免死锁,就要破坏这些条件
中的一个,然而互斥条件是进程必须的,所以的破坏后三个条件,最简单的方法就是破坏循环等待条件。
17.线程中的yield方法有什么用?
答:当线程执行yield方法后,便放弃了对CPU的占用权。
18.java怎样唤醒一个阻塞的进程?
答:wait()方法导致阻塞,利用Notify()或notifyAll()方法来唤醒;
sleep方法阻塞,在睡眠mills(s)重新进入就绪状态;
susPend方法阻塞,利用resume()方法唤醒。