JAVA 线程和进程
进程
进程是一个静态的概念
事实上,进程在执行指的是进程里面主线程开始执行了
线程
- 线程是一个程序里面不同的执行路径
多线程:
-sleep()和wait()
这两个函数都能让在休息时间内让其他线程执行。
sleep是线程被调用时,占着cpu去睡觉,其他线程不能占用cpu,os认为该线程正在工作,不会让出系统资源,wait是进入等待池等待,让出系统资源,其他线程可以占用cpu,一般wait不会加时间限制,因为如果wait的线程运行资源不够,再出来也没用,要等待其他线程调用notifyall方法唤醒等待池中的所有线程,才会在进入就绪序列等待os分配系统资源,
sleep是静态方法,是谁掉的谁去睡觉,就算是在main线程里调用了线程b的sleep方法,实际上还是main去睡觉,想让线程b去睡觉要在b的代码中掉sleep
-notify()和notifyAll()
如果对象调用了wait方法就会使持有该对象的线程把该对象的控制权交出去,然后处于等待状态。
如果对象调用了notify方法就会通知某个正在等待这个对象的控制权的线程可以继续运行。
如果对象调用了notifyAll方法就会通知所有等待这个对象控制权的线程继续运行。线程同步化
- 同步方法
即有synchronized关键字修饰的方法。
由于java的每个对象都有一个内置锁,当用此关键字修饰方法时,
内置锁会保护整个方法。在调用该方法前,需要获得内置锁,否则就处于阻塞状态。
代码如:
- 同步方法
public synchronized void save(){}
注: synchronized关键字也可以修饰静态方法,此时如果调用该静态方法,将会锁住整个类
- 同步代码块
即有synchronized关键字修饰的语句块。
被该关键字修饰的语句块会自动被加上内置锁,从而实现同步
代码如:
synchronized(object){
}
注:同步是一种高开销的操作,因此应该尽量减少同步的内容。
通常没有必要同步整个方法,使用synchronized代码块同步关键代码即可。
代码实例:
package com.xhj.thread;
/**
* 线程同步的运用
*
* @author XIEHEJUN
*
*/
public class SynchronizedThread {
class Bank {
private int account = 100;
public int getAccount() {
return account;
}
/**
* 用同步方法实现
*
* @param money
*/
public synchronized void save(int money) {
account += money;
}
/**
* 用同步代码块实现
*
* @param money
*/
public void save1(int money) {
synchronized (this) {
account += money;
}
}
}
class NewThread implements Runnable {
private Bank bank;
public NewThread(Bank bank) {
this.bank = bank;
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
// bank.save1(10);
bank.save(10);
System.out.println(i + "账户余额为:" + bank.getAccount());
}
}
}
/**
* 建立线程,调用内部类
*/
public void useThread() {
Bank bank = new Bank();
NewThread new_thread = new NewThread(bank);
System.out.println("线程1");
Thread thread1 = new Thread(new_thread);
thread1.start();
System.out.println("线程2");
Thread thread2 = new Thread(new_thread);
thread2.start();
}
public static void main(String[] args) {
SynchronizedThread st = new SynchronizedThread();
st.useThread();
}
}
这里附带一篇用于理解进程和线程的,写的很好的文章,很生动! 转载:进程和线程理解
JAVA多线程
多线程的作用
1)发挥多核CPU的优势
2)防止阻塞
- 进程:每个进程都有独立的代码和数据空间(进程上下文),进程间的切换会有较大的开销,一个进程包含1–n个线程,进程是资源分配的最小单位。
- 线程:同一类线程共享代码和数据空间,每个线程有独立的运行栈和程序计数器(PC),线程切换开销小,线程是CPU调度的最小单位。
实现线程的方式
继承Thread或者实现Runnable接口
线程的状态
1、新建状态(New):新创建了一个线程对象。
2、就绪状态(Runnable):线程对象创建后,其他线程调用了该对象的start()方法。该状态的线程位于可运行线程池中,变得可运行,等待获取CPU的使用权。
3、运行状态(Running):就绪状态的线程获取了CPU,执行程序代码。
4、阻塞状态(Blocked):阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。阻塞的情况分三种:
(一)、等待阻塞:运行的线程执行wait()方法,JVM会把该线程放入等待池中。
(二)、同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池中。
(三)、其他阻塞:运行的线程执行sleep()或join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。
5、死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。
线程的优先级
Java线程的优先级用整数表示,取值范围是1~10,Thread类有以下三个静态常量:
static int MAX_PRIORITY
线程可以具有的最高优先级,取值为10。
static int MIN_PRIORITY
线程可以具有的最低优先级,取值为1。
static int NORM_PRIORITY
分配给线程的默认优先级,取值为5。
Thread类的setPriority()和getPriority()方法分别用来设置和获取线程的优先级。
每个线程都有默认的优先级。主线程的默认优先级为Thread.NORM_PRIORITY。
线程的优先级有继承关系,比如A线程中创建了B线程,那么B将和A具有相同的优先级。
JVM提供了10个线程优先级,但与常见的操作系统都不能很好的映射。如果希望程序能移植到各个操作系统中,应该仅仅使用Thread类有以下三个静态常量作为优先级,这样能保证同样的优先级采用了同样的调度方式。
注意:
不要假定高优先级的线程一定先于低优先级的线程执行,不要有逻辑依赖于线程优先级,否则可能产生意外结果。
线程的联合
Join
在很多情况下,主线程生成并起动了子线程,如果子线程里要进行大量的耗时的运算,主线程往往将于子线程之前结束,但是如果主线程处理完其他的事务后,需要用到子线程的处理结果,也就是主线程需要等待子线程执行完成之后再结束,这个时候就要用到join()方法了。
线程的同步
synchronized
1)同步方法
2)同步代码块
synchronized关键字不能被继承
死锁
线程的休眠
sleep
sleep()使当前线程进入停滞状态(阻塞当前线程),让出CUP的使用、目的是不让当前线程独自霸占该进程所获的CPU资源,以留一定时间给其他线程执行的机会;
sleep()是Thread类的Static(静态)的方法;因此他不能改变对象的机锁,所以当在一个Synchronized块中调用Sleep()方法是,线程虽然休眠了,但是对象的机锁并木有被释放,其他线程无法访问这个对象(即使睡着也持有对象锁)。
在sleep()休眠时间期满后,该线程不一定会立即执行,这是因为其它线程可能正在运行而且没有被调度为放弃执行,除非此线程具有更高的优先级。
线程间的通信(线程协作)
wait()方法和notify()/notifyAll()方法在调用前都必须先获得对象的锁。
void notify()
唤醒在此对象监视器上等待的某一个线程.具体是哪一个可以认为是不确定的.
void notifyAll()
唤醒在此对象监视器上等待的所有线程。
void wait()
导致当前的线程等待,直到其他线程调用此对象的 notify()方法或 notifyAll()方法.
wait会释放占有的锁,notify和notifyAll不会释放占用的锁。
用户线程和守护线程
线程池
虚拟机内存模型
类型 | 默认值 |
---|---|
byte | 0 |
short | 0 |
int | 0 |
long | 0L |
float | 0.0f |
double | 0.0d |
char | ‘\u0000’ |
boolean | false |
All reference types | null |
内部类
内部类是定义在其他类内部的类:
1) 内部类对象能够访问创建它的对象的实现-包括那些私有数据;
2) 内部类能够隐藏起来,不为同一包中的其他类所见;
3) 匿名内部类可以方便地定义运行时回调;
4) 所有内部类在编写事件程序时用起来很方便。
1、静态内部类
1) 内部类的最简单形式;
2) 不能和外部类同名;
3) 被编译成一个独立的类文件;
4) 只能访问外部类的静态方法、静态实例变量(包括私有类型);
5) 静态内部类创建实例的形式:外部类名.内部类名 实例名 = new外部类名.内部类名(参数),注意:在不同的包才需要加外部类名。
2、实例内部类
1) 定义在类内部,没有使用static修饰符;
2) 像实例变量;
3) 能访问外部类的所有实例变量;
4) 在一个外部类里创建一个实例内部类实例:this.new Innerclass();
5) 在一个外部类外创建一个实例内部类实例:(new OuterClass()).new Innerclass();
6) 在一个内部类里访问一个实例内部类:Outerclass.this.member;
3、局部内部类
1) 定义在方法里;
2) 最少的一种内部类;
3) 和局部变量类似, 不能被定义为public,protected,private和static;
4) 局部内部类访问外部类的属性和方法使用“外部类名.this.属性名”和“外部类名.this.方法名(参数)”的形式;
5) 只能访问final型局部变量。
6) 局部内部类只在本作用域中可见。
4、匿名内部类
1) 没有类名;
2) 没有class关键字;
3) 没有继承和声明;
4) 没有构造函数;
Object
“==”操作符和equals()方法
在自己设计的实体类中,建议覆盖equals()方法,定义自己的比较逻辑,经典写法步骤:
1、如果传入对象为null,返回false
2、如果传入对象与this地址相等返回true
3、如果类型不匹配返回false
4、类型转换后比较属性
toString()方法
1、和equals()方法一样,toString()是Object类另外一个重要的方法;
2、它返回一个代表该对象值的字符串。几乎每个类都会覆盖该方法,以便打印该对象当前状态的表示。
3、无论何时对象同字符串相连接,那么就可以使用”+”操作符,这时Java编译器会自动调用toString方法获得对象的字符串表示。(把字符串先写在前面)
String&StringBuffer& StringBuilder
java.lang.String———–>不变字符对象,对象创建后其内容不能修改,线程安全。
java.lang.StringBuffer—–>可变字符对象,对象创建后可直接在原地址上修改,线程安全。
java.lang.StringBuilder同StringBuffer,但是非线程安全。