——- android培训、java培训、期待与您交流! ———-
什么是进程?
Ctrl+Shift+Esc 打开windows任务管理器
进程是操作系统动态执行和资源分配的一个基本单元,就是程序执行的过程,在这个过程中会占用内存空间和CPU资源。在操作系统中,程序的体现就是进程。
一个程序至少有一个进程,一个复杂的应用程序可能启动并使用多个进程,例如谷歌Chrome浏览器、腾讯的QQ等采用的就是多进程架构设计。
进程间相互独立,不能互相访问或共享资源
相关性,看你CPU的核心有几个(双核处理器、四核处理器、八核处理器)
线程是比进程更小的能独立运行的基本单位,它是进程的一部分
一个进程可以拥有多个线程,但至少要有一个线程,即主执行线程(java的main方法),我们之前写的都是单线程程序。
线程不能够单独执行,它必须运行在处于活动状态的进程中,它可与同属一个进程的其他线程共享进程所拥有的全部资源
生活案例:
1. 火车可以拥有多节车厢,但至少要有一节车厢,火车就是进程,每个
车厢就是一个线程
2. QQ可以打开多个聊天窗口,QQ就是进程,每个聊天窗口就是一个线
程
在一个程序中能让两个for循环同时执行吗?一个进程中的多个线程可以并发(同时)执行,因此用多线程编程即可解决这个问题。
有必要搞多个线程吗?有什么用?多线程编程能解决什么问题?
在一些执行时间长、需要等待的任务上(文件读写和网络传输等),多线程就比较有用了。
例如:多图片上传
方案1:一张一张来
方案2:多张同时开始(多线程编程)
多线程是为了共享内存、充分利用CPU,是为了同时完成多项任务,是为了提高资源(内存和CPU)使用率从而提高程序的执行效率。
一个(程序)进程中如果只有一条生产线,这个程序称为单线程。
一个(程序)进程中如果有多条生产线,这个程序称为多线程。
class Demo extends Thread { //生产线 (线程类)
public void run(){
for(int x=30; x<50; x++){
System.out.println("Thread----"+x);
}
}
}
class ThreadDemo {
public static void main(String[] args) { //主线程(必须有的)
Demo d1 = new Demo(); //创建一个生产线(线程)
d1.start(); //启动线程
for(int x=0; x<30; x++){
System.out.println("main----"+x);
}
}
}
C++这种语言传统上一直是性能之王。但是,C++对多线程技术的支持非常有限。标准C++甚至没有提到线程这个概念。要想使用C++开发多线程程序是非常困难的。
Java从一开始就在语言层面上支持多线程。使用Java编写多线程的程序是非常简单的。因此,基本上所有的Java程序,包括J2ME、J2SE、J2EE程序都支持多线程技术。在传统单核、单进程CPU上,Java多线程程序在性能上无法与C++单进程程序相比。但是,随着多核、超线程CPU时代的到来,将成就Java性能之王的地位!
Java中有两类线程:User Thread(用户线程)、Daemon Thread(守护线程或后台服务线程) 。守护线程的作用是为其他线程的运行提供服务,比如说GC线程。
在Java中实现多线程有两种方式:
方式一. 继承Thread类
方式二. 实现Runnable接口
方式一:
java.lang.Thread是Java提供的线程类,我们通常可以借助于它来实现多线程编程。
实现步骤:
自定义线程类即生产线(继承Thread类)
重写Thread类中的run()方法,把希望能同时运行的代码放在run()方法中。
创建生产线,即创建线程类的对象并调用start()方法启动生产线
该类常用方法:
public long getId(),获得该线程的标识符(线程 ID) ,它在创建该线程时生成。线程 ID 是唯一的,并终生不变。
public final void setName(String name),给线程命名,不命名的话都有默认的,格式为:Thread-0、Thread-1...
public final String getName(),获得线程的名字
public void start(),使该线程开始执行,JVM会自动调用该线程的 run() 方法。
public static void sleep(long millis),让线程休眠(暂停)多少毫秒
public static Thread currentThread(),获得当前正在执行的线程对象的引用。(方式二会用到)
多线程执行流程:
每个Java程序都有一个默认的主线程。Java程序总是从主类的main方法开始执行。当JVM加载代码,发现main方法后就启动一个线程,这个线程就称作"主线程",该线程负责执行main方法。在main方法中再创建的线程就是其他线程。
如果main方法中没有创建其他线程,那么当main方法执行完时JVM就会结束Java应用程序。但如果main方法中创建了其他线程,那么JVM就要在主线程和其他线程之间轮流切换,保证每个线程都有机会使用CPU资源,即使main方法结束(主线程结束)JVM也不会结束,要一直等到该程序所有线程全部结束才结束Java程序
方式二:
java.lang.Runnable接口是Java提供的实现多线程编程的另一种方式,该接口只定义了一个
无参的run方法。
具体实现步骤:
创建假线程类即假生产线,覆盖Runnable接口中的run方法,把希望能同时运行的代码放在run()方法中。
创建生产线,即通过Thread类创建一个线程(构造方法需要一个线程类对象)。
启动生产线,即调用Thread类对象的start方法开启线程。
class Shengchanxian implements Runnable { //创建线程类(不是真正的生产线)
public void run(){
for(int j=0; j<30; j++){
System.out.println(Thread.currentThread().getName()+"-"+j);
}
}
}
class Demo{
public static void main(String[] args) { //主线程
Shengchanxian scx=new Shengchanxian();
Thread t1=new Thread(scx); //创建第一个生产线
t1.start(); //启动
Thread t2=new Thread(scx); //创建第二个生产线
t2.start(); //启动
}
}
线程安全问题:
模拟实现:除夕那天北京到郑州的动车票只剩下10张,两个窗口同时在卖
public class SaleWindow implements Runnable {
private int ticket = 10; // 车票编号,共享资源
public void run() {
for (int i = 1; i <= 10; i++) { // 卖票
if (ticket >= 1) {
System.out.println(Thread.currentThread().getName()
+ "卖编号"+ ticket + "票");
ticket - -;
Thread.sleep(500); // 模拟卖票速度,半秒一张
}
}
}
public static void main(String[] args) {
SaleWindow sw = new SaleWindow();
new Thread(sw).start(); // 窗口1
new Thread(sw).start(); // 窗口2
}
}
解决线程安全问题:
出现线程安全问题的主要原因是:线程之间是独立的,多个线程之间共享的数据不同步
判断多线程程序是否存在安全隐患:
1. 代码中存在共享数据
2. 多个线程可能同时操作共享数据
解决线程安全问题的办法:java中提供了一个同步机制(锁)。即让操作共享数据的代码在某一时间段,只被一个线程执行(锁住),在执行过程中,其他线程不可以参与进来,这样共享数据就能同步了。简单来 说,就是给代码加把锁。
Java同步机制的实现方式:
同步代码块
同步方法(函数)
死锁问题:
虽然同步锁机制解决了线程安全问题,但是也带来一些弊端:
1. 效率会降低,每次都需要判断锁
2. 可能引发死锁(彼此占用所需要的资源),出现的概率非常小,非常特殊
线程之间彼此占用对方所需的资源,就是死锁
解决死锁问题:
该放手时要放手,之前是等到当前线程执行完时才释放锁,现在必须更及时的人工去释放锁(开锁)
因为任何对象都有锁,所以Object类提供了释放锁的方法:
public final void wait(),让当前线程等待,同时释放锁,直到被再次唤醒
public final void wait(long timeout),在指定时间内让当前线程等待,同时释放锁
notify方法:
wait()和sleep()都可以让当前线程等待,区别:
1,sleep():释放执行权(等待),不释放锁
2,wait():释放执行权(等待),同时释放锁
如果调用的是无参的wait()方法,那锁就一直释放,当前线程一直等待,还需要唤醒。Object类提供了notify()方法用来唤醒某个被wait()的锁,也就是唤醒线程
细节:
wait()和notify()方法都是操作锁的,而锁存在于同步中,也就是说这两个方法必须出现在同步中(同步代码块或同步方法)。同步锁不仅可以解决昨天的线程安全问题,还可以实现线程间的通信
单例设计模式(singleton):
单例即只有一个实例,该模式的作用是保证程序中某个类的对象只有一个。
一些窗口就设计成了单例设计模式,例如“任务管理器”
实际应用:将来的数据库连接类(就业班)
怎么实现?
1. 构造方法私有化,不让new
2. 提供一个公有静态方法返回一个实例
懒汉式的线程安全:
//懒汉式
class Single{
private static Single s = null;
private Single(){}
public static Single getInstance(){
if (s == null){
s = new Single();
}
return s;
}
}
饿汉式:
//饿汉式
public class Singleton{
private static Singleton s = new Singleton ();
private Singleton (){}
public static Singleton getInstance(){return s;}
}