1.几乎所有的操作系统都支持同时多个任务,一个任务通常就是一个程序,每个运行中的程序就是一个进程。当一个程序运行时,内部可能包含多个顺序执行流,每个执行流就是一个线程。也就是说一个进程有1个或者多个线程构成。
当一个程序进入内存运行时,即变成一个进程。进程是处于运行过程中的程序,并且具有一定的独立功能,进程是系统进行资源分配和调度的一个独立单位。
一般而言,进程包含如下三个特征。
①独立性:进程是系统中独立存在的实体,她可以拥有自己独立的资源,每一个进程都拥有自己私有的地址空间。在没有经过进程本身允许的情况下,一个用户进程不可以直接访问其他进程的地址空间。
②动态性:进程和程序的区别在于,程序知识一个惊天的指令集和,而进程是一个正在系统中活动的指令集合。
③并发性:多个进程可以在单个处理器上并发执行,多个进程之间不会互相影响。
归纳:操作系统可以同时执行多个任务,每个任务就是进程,进程可以同时执行多个任务,每个任务就是线程。
注:并发性和并行性是连个概念,并行指在同一时刻,有多条指令在多个处理器上同时执行;并发指在同一时刻只能有一条指令执行,但多个进程指令别迅速轮换执行,使得在宏观上具有多个进程同时执行的效果。并行运行智能在多核CPU中发生。
线程是进程的组成部分,一个进程可以拥有多个线程,一个线程必须有一个父进程。线程可以拥有自己的堆栈、自己的程序计数器和自己的局部变量,但不拥有系统资源,她与父进程的其他线程共享该进程所拥有的全部资源。因为多个线程共享父进程里的全部资源,因此编程更加方便;但必须更加小心,因为需要确保线程不会妨碍进程里的其他线程。
线程共享的环境包括:进程代码段、进程的公有数据等。利用这些共享的数据,线程很容易实现互相之间的通信。
java使用Thread类代表线程,所有的线程对象都必须是Thread类或器子类的实例。
1.继承Thread类创建线程类
步骤:
①定义Thread类的子类,并且重写该类的run()方法,该run()方法的方法体就代表线程需要完成的任务。因此把run()方法称为线程执行体。
②创建Thr子类的实例,即创建了线程对象。
③调用线程对象的start()方法来启动该线程。
package com.bluemsun.study;
/**
* Created by mafx on 2018/9/16.
* @author mafx
*/
public class ExtendsThreadClassCreatThread extends Thread{
private int i;
@Override
public void run(){
for(;i<100;i++){
System.out.println(getName()+" "+i);
}
}
public static void main(String[] args) {
for(int i=0;i<100;i++){
System.out.println(Thread.currentThread().getName()+" "+i);
if(i==20){
//创建和启动线程
new ExtendsThreadClassCreatThread().start();
new ExtendsThreadClassCreatThread().start();
}
}
}
}
注:使用继承Thread类的方法来创建线程类时,多个线程之间无法共享线程类的实例变量。
2.实现Runable接口创建线程类
实现Runnable接口来创建并启动多线程的步骤如下。
①定义Runnable接口的实现类,并重写该接口的run()方法,该run()方法的方法体同样是该线程的线程执行体。
②创建Runnable实现类的实例,并以此实例作为Thread的target来创建Thread对象,该Thread对象才是真正的线程对象。
注:Runnable对象仅仅作为Thread对象的target,Runnable实现类里包含的run()方法仅仅作为线程执行体。二十几的线程对象依然是Thread实例,只是该Thread线程负责执行其target的run()方法。
package com.bluemsun.study;
/**
* Created by mafx on 2018/9/16.
*/
public class ImplementRunnableCreatThread implements Runnable{
private int i;
@Override
public void run(){
for(;i<100;i++){
System.out.println(Thread.currentThread().getName()+" "+i);
}
}
public static void main(String[] args) {
for(int i=0;i<100;i++){
System.out.println(Thread.currentThread().getName()+" "+i);
if(i==20){
//创建和启动线程
ImplementRunnableCreatThread st=new ImplementRunnableCreatThread();
new Thread(st,"线程 1").start();
new Thread(st,"线程 2").start();
}
}
}
}
3.使用Callable和Future创建线程
Callable接口提供了一个call()方法可以作为线程执行体,但是call()方法比run()方法功能更加强大。
*call()方法可以有返回值
*call()方法可以声明抛出异常
创建并启动有返回值得线程的步骤如下。
①创建Callable接口的实现类,并实现call()方法,该call()方法将作为线程执行体,且该call()方法有返回值,在创建Callable实现类的实例。
②使用FutureTask类来包装Callable对象,该FutureTask对象封装了该Callable对象的call()方法的返回值。
③使用FutureTask对象作为Thread对象的target创建并启动新线程。
④调用FutureTask对象的get()方法来获得子线程执行结束后的返回值。
package com.bluemsun.study;
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
/**
* Created by mafx on 2018/9/16.
*/
public class ImplementCallableCreatThread implements Callable{
/*FutureTask task=new FutureTask((Callable)()->{
int i=0;
for(;i<100;i++){
System.out.println(Thread.currentThread().getName()+" "+i);
}
return i;
});*/
@Override
public Object call() throws Exception {
int i=0;
for(;i<100;i++){
System.out.println(Thread.currentThread().getName()+" "+i);
}
return i;
}
public static void main(String[] args) {
ImplementCallableCreatThread ct=new ImplementCallableCreatThread();
FutureTask task=new FutureTask(ct);
for(int i=0;i<100;i++) {
System.out.println(Thread.currentThread().getName() + " " + i);
if (i == 20) {
new Thread(task, "线程 1").start();
}
}
try {
System.out.println("子线程的返回值:" + task.get());
}catch (Exception ex){
ex.printStackTrace();
}
}
}
1.join()方法:该方法是Thread提供的让一个线程等待另一个线程完成的方法。当在某个程序执行流中调用其他线程的join()方法时,调用线程将被阻塞,直到被join()方法加入的join线程执行完为止。
重载方法:join():等待被join()的线程执行完成
join(long millis):等待被join的线程的时间最长为millis毫秒。如果在millis毫秒内被join的线程还没有执行结束,则不再等待。
join(long millis,int nanos):等待时间毫秒加毫微妙
2.sleep():使线程睡眠,让线程放弃处理器资源进入阻塞状态
3.yield():和sleep()有点相似,但是它不会让线程进入阻塞状态而是再次进入就绪状态
4.setPriority(int newPriority)设置线程的优先级,getPriority()返回指定线程的优先级
............
1.同步代码块
为了解决多线程共享同一个资源点来的线程安全问题,java的多线程支持引入了同步监视器来解决这个问题,使用同步监视器的通用方法是同步代码块。语法如下:
synchronized(obj)
{
//此处的代码就是同步代码块
}
其中obj就是同步监视器,上面代码的含义是:线程开始执行同步代码块之前,必须先获得对同步监视器的锁定。任何时刻只能有一个线程可以活的对同步监视器的锁定,当同步代码块执行完成后,该线程会释放对该同步监视器的锁定。使用同步监视器的目的是阻止多个线程对同一资源进行并发访问,因此通常使用可能被并发访问的共享资源充当同步监视器。
2.同步方法
与同步代码块对应,java的多线程安全支持还提供了同步方法,同步方法就是使用synchronized关键字来修饰某个方法,则称该方法是同步方法。同步方法的同步监视器是this,也就是调用该方法的对象。也就是通过锁定整个对象来实现线程安全。
注:synchronized关键字可以修饰方法,可以修饰代码块,但不能修饰构造器、成员变量等。
线程在以下几种情况下释放对同步监视器的锁定:
1.当前线程的同步方法、同步代码块执行结束,当前线程即释放同步监视器。
2.当前线程在同步代码块、同步方法中遇到break、return终止了该代码块、该方法的继续执行,当前线程释放同步监视器
3.当前线程在同步代码块、同步方法中出现了未处理的Error或Exception,导致改代码块、该方法异常结束时,当前线程释放同步监视器。
4.当前线程执行同步代码块或同步方法时,程序执行了同步监视器对象的wait()方法,则当前线程暂停,并且释放同步监视器。
在如下几种情况中,线程不会释放同步监视器:
1.线程执行同步代码块或者同步方法时,程序调用Thread.sleep()、Thread.yield()方法来暂停当前线程的执行,当前线程不会释放同步监视器。
2.线程执行同步代码块时,其他线程调用了该线程的suspend()方法将该线程挂起,该线程不会释放同步监视器。
总结:
sleep()和wait()方法的区别:第一个区别就是这两个方法的来源不同,sleep()方法来源于Thread类,而wait()方法来源于Object,另一区别就是,线程调用sleep()方法不会释放同步监视器,而调用wait()方法会释放同步监视器。
sleep()和yield()方法的区别:它们都是Thread类的方法,线程调用sleep()会先使得线程进入阻塞状态,而调用yield()会使得线程进入就绪状态。
Lock是控制多线程对共享资源进行访问的工具。通常,锁提供了堆共享资源的独占访问,每次只能有一个线程对lock对象加锁,线程开始访问共享资源之前应先获得lock对象。在实现线程安全的控制中,比较常用的是ReentrantLock(可重入锁)。使用该Lock对象可以显示的加锁、释放锁,其语法大致如下:
class X{
private final ReentrantLock lock=new ReenTranLock;
//......
//定义要保证线程安全的方法
public void m(){
lock.lock();//加锁
try(){
//需要保证线程安全的代码
}finally{
lock.unlock();//释放锁
}
}
}
volatile作用:让其他线程能够马上感知到某一线程对某个变量的修改
(1)保证可见性
对共享变量的修改,其他的线程马上能感知到
不能保证原子性 读、写、(i++)
(2)保证有序性
重排序(编译阶段、指令优化阶段)
输入程序的代码顺序并不是实际执行的顺序
重排序后对单线程没有影响,对多线程有影响
volatile规则:
对于volatile修饰的变量:
(1)volatile之前的代码不能调整到他的后面
(2)volatile之后的代码不能调整到他的前面(as if seria)
(3)霸道(位置不变化)
(1)使用上的区别
Volatile只能修饰变量,synchronized只能修饰方法和语句块
(2)对原子性的保证
synchronized可以保证原子性,Volatile不能保证原子性
注:Volatile关键字值通知其他线程,当前线程对某变量进行了修改,让其他线程去内存中读取数据,而不是继续操作自己工作空间的数据
(3)对可见性的保证
都可以保证可见性,但实现原理不同
Volatile对变量加了lock,synchronized使用monitorEnter和monitorexit monitor JVM
(4)对有序性的保证
Volatile能保证有序,synchronized可以保证有序性,但是代价(重量级)并发退化到串行
(5)其他
synchronized引起阻塞
Volatile不会引起阻塞
public class ShutDowsnDemmo extends Thread{
private volatile boolean started=false;
@Override
public void run() {
while(started){
dowork();
}
}
public void shutdown(){
started=false;
}
}
2.双重检查锁定(double-checked-locking)
DCL(7)
public class Singleton {
private volatile static Singleton instance;
public static Singleton getInstance(){
if(instance==null){
synchronized (Singleton.class){
instance=new Singleton();
}
}
return instance;
}
}
3.需要利用顺序性