1. 什么是进程?什么是线程?
进程是一个应用程序(一个进程是一个软件),线程是一个进程中的执行场景/执行单元,一个进程可以启动多个线程。
对于Java程序来说,当在DOS命令窗口输入:Java HelloWorld回车换行之后会启动JVM,而JVM就是一个进程。JVM在启动一个主线程调用main方法。同时在启动一个垃圾回收线程负责看护,现在的Java程序中至少有两个线程并发,一个是垃圾回收线程,一个执行main方法的主线程。
2. 进程和线程之间的关系
例:
阿里巴巴:进程
马云:阿里巴巴的一个线程
童文红:阿里巴巴的一个线程
注意:
进程A和进程B的内存独立不共享。(阿里巴巴和京东资源不共享)
线程A和线程B,堆内存和方法区内存共享
但是栈内存独立,一个线程和一个栈。
java中之所以有多线程机制,目的是为了提高程序的处理效率
main方法结束只是主线程结束了,主栈空了,其它的栈可能还在压栈弹栈
3. 堆和方法区共享栈独立
4.多线程并发的理解
分析一个问题:对于单核的CPU来说,真的可以做到真正的多线程并发吗?
对于多核的CPU电脑来说,真正的多线程并发是没问题的。
4核cPU表示同一个时间点上,可以真正的有4个进程并发执行。
什么是真正的多线程并发?
t1线程执行t1的。
t2线程执行t2的.
t1不会影响t2, t2也不会影响t1.这叫做真正的多线程并发。
单核的CPU表示只有一一个大脑:
不能够做到真正的多线程并发,但是可以做到给人一种多线程并发"的感觉。对于单核的CPU来说,在某一个时间点上实际上只能处理一件事情,但是由于CPU的处理速度极快,多个线程之间频繁切换执行,跟人来的感觉是:多个事情同时在做!!!
线程A:播放音乐
线程B:运行魔兽游戏
线程A和线程B频繁切换执行,人类会感觉音乐一直在播放,游戏- -直在运行,给我们的感觉是同时并发的。
电影院采用胶卷播放电影,一个胶卷-一个胶卷播放速度达到一-定程度之后,
人类的眼睛产生了错觉,感觉是动画的。这说明人类的反应速度很慢,就像
–根钢针扎到手上,到最终感觉到疼,这个过程是需要很长的"时间的,在
这个期间计算机可以进行亿万次的循环。所以计算机的执行速度很快.
4. 实现线程的第一种方式
5.run和start的区别
6. 实现线程的第二种方式
6.1 采用匿名内部类方式实现
7. 线程的生命周期
新建状态、就绪状态、运行状态、阻塞状态、死亡状态
8. 获取线程的名字
9. 获取当前对象
10. 线程的sleep方法
11. 终止线程的睡眠
12. 合理的终止一个线程
13. 线程调度
13.1 线程调度概述
关于线程的调度:
* 常见的线程调度模型有哪些?
抢占式调度模型:
哪个线程的优先级比较高,抢到的CPU时间片的概率就高一些/多一些,Java采用的就是抢占式调度模型。
均分式调度模型:
平均分配CPU时间片。每个线程占有的CPU时间片时间长度一样,有一些编程语言线程调度采用这种方式。
13.2 线程调度方法
Java提供了哪些方法是和线程调度有关系的呢?
实例方法:
void setPriority(int newPrority) 设置线程的优先级
int getPriority() 获取线程优先级
最低优先级 1
默认优先级 5
最高优先级 10
优先级比较高的获取CPU时间片可能会多一些。(但也不完全是,大概率是多的)
静态方法:
static void yield() 让位方法
暂停当前正在执行的线程对象,并执行其他线程
yield() 方法不是阻塞方法。让当前线程让位,让给其他线程使用。
yield() 方法的执行就绪之后,有可能会再次抢到
实例方法:
void join()
合并线程
class MyThread1 extends Thread {
public void doSome(){
MyThread2 t = new MyThread2();
t,join(); //当前线程进入阻塞,t线程执行,直到t线程结束。当前线程才可以
}
}
class MyThread2 extends Thread{
}
14. 线程安全
关于多线程并发环境下,数据的安全问题。
1.为什么这个是重点?
以后在开发中,我们的项目都是运行在服务器当中,而服务器已经将线程的定义,线程的对象创建,线程的启动等,都已经实现完了。这些代码我们都不需要编写。
重要的是:你要知道,你编写的程序需要放到一个多线程的环境下运行,你更需要关注的是这些数据在多线程并发环境下是否是安全的。
2.什么时候数据在多线程并发的环境下会存在的安全问题?
三个条件:
条件一:多线程并发
条件二:有共享数据
条件三:共享数据有修改的行为
满足以上3个条件之后,就会存在线程安全问题
14.1 举例
14.2 如何解决线程安全
怎么解决线程安全问题?
当多线程并发的环境下,有共享数据,并且这个数据还会被修改,此时就存在线程安全问题,如何解决?
线程排队执行(不能并发)
用排队执行解决线程安全问题
这种机制被称为:线程同步机制
专业术语叫做:线程同步,实际上就是线程不能并发了。线程必须排队执行
使用线程同步机制可以解决线程安全问题,线程同步就是线程排队,线程排队会牺牲一部分效率。
14.3 同步和异步的理解
关于线程同步有异步编程模型和同步编程模型:
异步编程模型:
线程t1和线程t2,各自执行各自的,t1不管t2,t2不管t1,谁也不需要等谁,这种编程模型叫做:异步编程模型。也就是多线程并发(效率较高)
异步就是并发
同步编程模型:
线程t1和线程t2,在线程t1执行的时候,必须等待t2线程执行结束,或者说在t2线程执行的时候,必须等待t1线程执行结束,两个线程之间发生了等待关系,这就是同步编程模型。效率较低,线程排队执行。
同步就是排队
15. 同步代码块synchronized
线程同步机制的语法是:
synchronized(){
编程同步代码块
}
synchronized后面小括号中传的这个“数据“,是多线程共享的数据。才能达到多线程排队
16. Java中的三大变量
Java中有三大变量?
实例变量:在堆中
静态变量:在方法区中
局部变量:在栈中
以上三大变量中:
局部变量永远不会存在线程安全问题,因为局部变量不共享(一个线程一个栈),局部变量在栈中。所以局部变量永远都不会共享。
实例变量在堆中,堆只有一个
静态变量在方法区中,方法区只有一个
堆和方法都是多线程共享的,可能会存在线程安全问题。
17. synchronized的三种写法
第一种:同步代码块(灵活)
synchronized(线程共享对象){
同步代码块
}
第二种:在实例方法上使用synchronized
表示共享对象一定是this,并且同步代码块是整个方法体。
第三种:在静态方法上使用synchronized
表示找类锁,类锁永远只有一把,就算创建了100个对象,那类锁也只有一把。
对象锁:1个对象1把锁,100个对象100把锁。
类锁:100个对象,也可能只有1把类锁
18. 死锁概述
public class Test01 {
public static void main(String[] args) {
Object o1 = new Object();
Object o2 = new Object();
Thread t1 = new Mythread1(o1,o2);
Thread t2 = new Mythread2(o1,o2);
t1.start();
t2.start();
}
}
class Mythread1 extends Thread{
Object o1;
Object o2;
public Mythread1(Object o1,Object o2){
this.o1=o1;
this.o2=o2;
}
@Override
public void run() {
synchronized (o1){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (o2){
}
}
}
}
class Mythread2 extends Thread{
Object o1;
Object o2;
public Mythread2(Object o1,Object o2){
this.o1=o1;
this.o2=o2;
}
@Override
public void run() {
synchronized (o2){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (o1){
}
}
}
}
19. 开发中如何解决线程安全问题
是不是一上来选择线程同步?
不是,synchronized会让程序的执行效率降低,用户体验不好。系统的用户吞吐量降低。用户体验差。在不得已的情况下在选择线程同步机制。
第一种方案:尽量使用局部变量代替“实例变量”和“静态变量”
第二种方案:如果必须是实例变量,那么可以考虑创建多个对象,这样实例变量的内存就不共享了。(一个线程对应1个对象,100个线程对应100个对象,对象不共享,就没有数据安全问题)
第三种方案:如果不能使用局部变量,对象也不能创建多个,这个时候就只能选择synchronized了。线程同步机制
20. 守护线程概述
守护线程的特点:
一般守护线程是一个死循环,所有的用户线程只要结束,守护线程自动结束。
注意:主线程main方法是一个用户线程
守护线程用在什么地方?
举例:每天00:00的时候系统数据自动备份。这个时候需要使用到定时器,并且我们可以将定时器设置为守护线程。一直在那里看着,每到00:00的时候就备份一次。所有的用户线程如果结束了,守护线程自动退出,没有必要进行数据备份了。
public class Test06 {
public static void main(String[] args) {
Thread t1 = new Mythread6();
t1.setName("备份数据系统");
//启动线程之前将线程设置为守护线程
t1.setDaemon(true);
t1.start();
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName()+"主线程"+i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class Mythread6 extends Thread{
@Override
public void run() {
int i = 0;
//即使是死循环,但由于该线程是守护者,当用户线程结束,守护线程自动终止
while (true){
System.out.println(Thread.currentThread().getName()+"--->"+(++i));
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
21. 实现定时器
22. 实现线程的第第三种方式
23. 生产者和消费者模式
关于Object类中的wait方法和notify方法(生产者和消费者模式)
第一:wait和t.notify方法不是线程对象的方法,是Java中任何一个Java对象都有的方法,因为这两个方法是Object类中自带的。
wait方法和notify方法不是通过线程对象调用
举例:t.wait()和t.notify()都是错误的
第二:wait()方法作用?
Object o = new Object();
o.wait();
表示:让正在o对象上活动的线程进入等待状态,无期限等待,直到被唤醒为止。
o.wait() 方法的调用,会让“当前线程(正在o对象上活动的线程)”进入等待状态
第三:notify()方法作用?
Object o = new Object();
o.notify();
表示:唤醒正在o对象上等待的线程。
还有一个notifyAll()方法:唤醒o对象上处于等待的所有进程。
所有资料均来自网络个人收集整理,仅供所有Java编程爱好者学习参考!!!