进程是操作系统的基础,是一个程序在一个数据集上运行的过程,也是系统进行资源分配和调度的基本单位。我们可以认为一个进程就是一个应用程序。
线程是操作系统调度的最小单元,程序执行的最小单位,在一个进程中可以创建多个线程。线程拥有独立的堆栈空间,可以共享内存变量。
Java的线程在运行的生命周期中可能会处理6种不同的状态
线程创建后,调用Thread的start方法开始进入运行状态,执行wait方法后,进入等待状态,这个时候需要其他线程的通知才能重新返回运行状态,超时等待相当于在等待上加了一个时间,当时间到的时候,线程自动返回运行状态,当线程调用到同步方法时,如果没有获得锁,则进入阻塞状态,获取到锁后会进入到运行状态,当线程执行完或者遇到意外的时候都会进入到终止状态。
//定义Thread子类
 public class MyThread extends Thread{//继承Thread类
  public void run(){
  //重写run方法
  }
}
public class Main {
  public static void main(String[] args){
    new MyThread().start();//创建并启动线程
  }
}
  2.实现Runnable接口并实现run方法
public class RunnableThreadTest implements Runnable  
{  
 
    public void run()  
    {  
        //集体逻辑
    }  
    public static void main(String[] args)  
    {  
        RunnableThreadTest runnble = new RunnableThreadTest();  
        new Thread(runnble,"新线程").start();  
    }   
}
  3.实现Callable接口
Callable接口与Runnable接口功能类似,不过提供了更强大的功能。主要有以下几点
public class CallableTest{
//创建callable线程
   public static class MyCallable implements Callable{
      public String call() throws Execption{
        return "hello";
      }   
   } 
   public static void main(String args[]){
     MyCallable callable = new MyCallable();
     ExecutorService executorService = Executors.newSingleThreadPool();
     Future future = executorService.submit(callable);
     try{
     //等待线程结果
     System.out.println(future.get());
     }catch(Execption e){
       e.printStackTrace
     }
   }
}
  线程的中断
一个线程可以调用其interrupt方法来中断线程。线程被中断了并不一定是被终止了,被终止是run方法执行完毕或者run方法中发生了异常而导致的。线程中断了还可以通过Thread.interrupted()方法进行复位。
想要安全的终止一个线程,可以在执行逻辑之前判断当前线程是不是中断的状态通过Thread.currentThread().idInterrupted()。或者通过一个boolean变量来判断,在Runnable中定义一个boolean变量使用volatile变量来修饰,vloatile可以保证这个变量的原子性,当别的线程修改这个变量的时候,所有的线程都会感受到这个变量的变化。
线程同步
在多线程应用中,如果两个或两个以上的线程同时对同一个对象进行修改的时候,就会产生问题,比如我仓库有一部手机,两个人在同一时间都下了订单并且都成功了,那手机给谁就不知道了。解决办法就是,当一个线程对此对象进行修改的时候,就给它一把锁,别的线程进不来,当它完成任务之后,在把锁给下一个进来的线程。在java中这个锁之一就是synchronized关键字。
使用synchronized可以给一个方法加上锁,wait()方法可以将一个线程添加到等待集中,notify或者notifyAll可以解除等待线程的阻塞状态。例如
public synchronized void add()throws InterruptedException{
 //等待
  for(int i = 0; i < 10;i++){
                 if(i == 3){
                   wait();
                   System.out.println("wait");
                 }
             }
//进行一系列的操作
....
//唤醒
   notifyAll();
System.out.println("notifyAll");
}
  也可以给一个对象上锁
public class MyRunnable implements Runnable {
     private Object flag;
     private String threadName;
 
     public MyThread(Object flag,String threadName) {
         this.flag = flag;
         this.threadName = threadName;
     }
 
     @Override
     public void run(){
         try{
             for(int i = 0; i < 10;i++){
                 if(i == 3){
                     synchronized (this.flag){
                     //进入等待状态
                         this.flag.wait();
                     }
                 }
                 System.out.println(this.threadName + " " + i);
                 Thread.sleep(1000);
             }
         } catch(InterruptedException e){
             e.printStackTrace();
         }
 
     }
 }
 
 public class TestMain {
    public static void main(String[] args) {
        Object object = new Object();
        MyRunnable myRunnable = new MyRunnable(object,"Runnable");
        Thread thread = new Thread(myRunnable1);
        thread.start();
        try{
            Thread.sleep(6000);
            System.out.println("6秒后唤醒线程");
            synchronized (object){
            //唤醒线程
                object.notify();
            }
            System.in.read();
        } catch(InterruptedException e){
            e.printStackTrace();
        } catch(IOException e){
            e.printStackTrace();
        }
    }
}
  synchronized, wait() ,notify() 必须是操控的同一个对象
除了synchronized,java还提供了一个重用锁ReentrantLock。比synchronized写起来麻烦,不过功能更加强大。
ReentrantLock lock = new ReentrantLock();
lock.lock();
try {
       ....        
} finally {
        lock.unlock(); // 看这里就可以
              
 }
  把unlock放在finally中是很有必要的,如果发生了异常确保锁是可以被释放的。synchronized中有wait(),notigy()等方法,在ReentrantLock需要配合Conditond 使用,Conditon提供了以下方法:
public interface Condition {
    void await() throws InterruptedException; // 类似于Object.wait()
    void awaitUninterruptibly(); // 与await()相同,但不会再等待过程中响应中断
    long awaitNanos(long nanosTimeout) throws InterruptedException;
    boolean await(long time, TimeUnit unit) throws InterruptedException;
    boolean awaitUntil(Date deadline) throws InterruptedException;
    void signal(); // 类似于Obejct.notify()
    void signalAll();
}
  例子
public class Test {
    private Lock lock = new ReentrantLock();
    public Condition condition = lock.newCondition();
    public void await() {
        try {
            lock.lock();
            System.out.println(" await时间为" + System.currentTimeMillis());
            condition.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
    public void signal() {
        try {
            lock.lock();
            System.out.println("signal时间为" + System.currentTimeMillis());
            condition.signal();
        } finally {
            lock.unlock();
        }
    }
public class MyThread extends Thread {
    private Test test;
    public ThreadA(Test test) {
        super();
        this.test = test;
    }
    @Override
    public void run() {
       //等待
        test.await();
    }
}
    public static void main(String[] args) throws InterruptedException {
        Test test = new Test();
        MyThread a = new MyThread(test);
        a.start();
        Thread.sleep(3000);
       //唤醒
        service.signal();
    }
  有时候如果只是给一个实例域使用同步的话,使用synchronize开销有点大,这个时候使用volatitle关键字修饰一个实例也可以做到线程安全。
当一个变量被volatitle关键字修饰以后,它就具备了两种含义:
Java中使用 堆内存 来存储对象的实例,堆内存是可以被所有线程共享的内存区域,因此它是内存可见的。
局部变量和方法定义的参数是不会再线程之间共享的,他们之间是内存不可见的,不受内存模型的影响。
Java的内存模型定义了线程和主存之间的抽象关系,线程之间的共享变量存储在主存当中,每个线程还有一个私有的本地内存(这是一个抽象的概念,不真实存在),本地内存中存储了该线程共享变量的副本,Java线程模型控制线程之间的通信,它决定一个线程对主存共享变量的写入何时对其他线程可见。
例如线程A和线程B之间要通信的话,需要经历两个步骤
比如 int i = 5
执行此语句的线程,必须先在自己的工作线程中对变量i所在的缓存进行赋值操作,然后在写入到主存当中,而不是直接把3写入到主存中。
volatitle关键字可以保证变量的可见性和有序性,但是不能保证变量的原子性。所以一般来说使用volatitle的时候需要具备以下条件
1 对变量的操作不依赖于当前的值
2 该变量没有包含在具有其他变量的不变式中
1 状态标记变量
volatile boolean flag = false;
 
while(!flag){
    doSomething();
}
 
public void setFlag() {
    flag = true;
}
  2 写单例的时候 双重校验
class Singleton{
    private volatile static Singleton instance = null;
     
    private Singleton() {}
    public static Singleton getInstance() {
        if(instance==null) {
            synchronized (Singleton.class) {
                if(instance==null)
                    instance = new Singleton();
            }
        }
        return instance;
    }
}