线程

1.并发

同一时刻只能有一个线程执行,但是多个线程被快速的轮换执行,使得在宏观上具有多个线程同时执行的效果,但在微观上并不是同时执行的,只是把CPU运行时间划分为若干个时间段,再将时间段分配给线程执行。

2.进程

作为资源分配的单位。再操作系统中能同时运行多个任务(程序),系统在运行的时候会为每个进程分配不同的内存区域。没有线程的进程是可以看作单线程的。如果一个进程内拥有多个线程,则执行过程不是一条线,而是多条线(线程)共同完成。

3.线程

调度和执行的单位。线程可以看成轻量级的进程,同一类线程共享代码和数据空间,每个线程有独立的运行栈和程序计数器(PC),线程切换的开销小。除了CPU 外,不会为线程分配内存(线程所使用的资源是它所属的进程的资源),线程内只能共享资源。线程是进程的一部分。

*总结

  • 一个应用程序至少对应一个进程。

  • 一个进程可以包含多个线程。

  • 一个程序关闭了,在系统中的进程就没了,程序中的线程也结束了。

5.创建线程的三种方法

1.第一种

  1. package com.yang.thread;
    /*
     [1] 继承Thread类,重写run方法
     [2] 使用start() 开启子线程
     [3] 我们调用start(),底层调用的是start0(),底层不是java书写的--->run().
    */
    //线程实现的第一种方式
    public class Thread01 {
       public static void main(String[] args) {

           ThreadTest threadTest = new ThreadTest();
           threadTest.setName("兔子");//给线程设置名字
           threadTest.start();//开启线程

           ThreadTest threadTest1 = new ThreadTest();
           threadTest1.setName("乌龟");
           threadTest1.start();

      }
    }

    class ThreadTest extends Thread{//继承线程
       @Override
       public void run() {//重写run方法
           for (int i = 1; i <=100 ; i++) {
               System.out.println(Thread.currentThread().getName()+i+"m");
          }
      }
    }
package com.yang.thread;

public class Thread02 {
   public static void main(String[] args) {
       //匿名内部类
       Thread thread = new Thread(){
           @Override
           public void run() {
               for (int i = 1; i <=100 ; i++) {
                   System.out.println(Thread.currentThread().getName()+i+"m");
              }
          }
      };
       thread.setName("兔子");
       thread.start();

  }
}

2.第二种

package com.yang.thread;
//创建线程的第二种方法
/*
  实现Runnable接口,重写run方法。
  直接调用start()不可以开启线程。需要使用Thread有参构造进行开启线程。
*/
public class Thread03 {
   public static void main(String[] args) {
       //创建两个对象,和创建一个对象的区别在于资源是否共享。
       MyThread my = new MyThread();
       MyThread my1 = new MyThread();


       Thread t = new Thread(my,"兔子");
       Thread t1 = new Thread(my1,"乌龟");
       t.start();
       t1.start();

       try {
           Thread.sleep(1000);
      } catch (InterruptedException e) {
           e.printStackTrace();
      }
       /*
       有三个线程,一个主线程main,还有两个子线程,,他们都是同时运行的。只是有主线程运行的快,所以先输出完。
        */
       System.out.println("我在输出语句了");

  }
}
class MyThread implements Runnable{

   @Override
   public void run() {
       for (int i = 1; i <=100 ; i++) {
           System.out.println(Thread.currentThread().getName()+i+"m");
      }
  }
}
package com.yang.thread;

public class Thread04 {
   public static void main(String[] args) {
       //使用匿名内部类
       new Thread(new Runnable() {
           @Override
           public void run() {
               //代码块
          }
      }, "线程A").start();

        //使用lambda表达式 ()->{} 相当于重写了runnable中的run()方法。
       new Thread(()->{

      },"线程B").start();

  }
}

3.第三种

package com.yang.thread;

import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

//实现线程的第三种方式
//实现Callable
/*
可以有返回值
*/
//底层原理
//start() --> start0()-->run()->FutureTask 中的run()方法中调的是-->result = c.call()-->set(result)
// -->outcome = v返回值-->get()得到返回值。
public class Demo05 {
   public static void main(String[] args) throws ExecutionException, InterruptedException {

       MyCallable my = new MyCallable();
       MyCallable my1 = new MyCallable();//这个一般写两个;主要看资源是不是共享

// FutureTask(Callable<V> callable)--->FutureTask<V> implements RunnableFuture<V>--->RunnableFuture<V> extends Runnable
       //相等于一个中间转换FutureTask
       //这是开启了两个线程
       FutureTask<Integer> task = new FutureTask<>(my);
       FutureTask<Integer> task1 = new FutureTask<>(my1);

       Thread thread = new Thread(task,"兔子");
       Thread thread1 = new Thread(task,"兔子");
       thread.start();
       thread1.start();
       //获得当前线程结束后的返回值。thread.start();不能写在这之后,理解一下。
       Integer integer = task.get();
       Integer integer1 = task1.get();
       System.out.println(integer);
       System.out.println(integer1);


  }
}

class MyCallable implements Callable<Integer> {

   @Override
   public Integer call() throws Exception {
       int i = new Random().nextInt(10);
       System.out.println(Thread.currentThread().getName()+i+"m");
       return i;
  }
}
package com.yang.thread;

import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

public class Demo06 {
   public static void main(String[] args) throws ExecutionException, InterruptedException {

       FutureTask<Integer> futureTask = new FutureTask<>(new Callable<Integer>() {
           @Override
           public Integer call() throws Exception {
               int i = new Random().nextInt(10);
               System.out.println(Thread.currentThread().getName()+i+"m");
               return i;
          }
      });
       new Thread(futureTask,"兔子").start();
       System.out.println(futureTask.get());

  }
}

*总结

  • 后两种方式可以避免Java中的单继承的局限性

  • 需要返回值只能使用想实现Callable接口

  • 不需要返回值推荐使用Runnable接口

  • 线程池只能使用Runnable或者Callable类型参数,不能使用Thread类。

6. LOCK

6.1 验证lock锁是否时可重入锁

package com.yang.lock;

import java.util.concurrent.locks.ReentrantLock;

//验证Lock可重入锁
public class Demo06 {
   public static void main(String[] args) {

       ReentrantLock y = new ReentrantLock();//遥控器锁
       ReentrantLock d = new ReentrantLock();//电池锁

       Thread th = new Thread(new XiaoMing1(y,d));
       Thread th1 = new Thread(new XiaoHong2(y,d));
       th.start();
       th1.start();

  }
}
class XiaoMing1 implements Runnable{

   private ReentrantLock ykq;
   private ReentrantLock dc;

   public XiaoMing1(ReentrantLock y, ReentrantLock d) {
       this.ykq = y;
       this.dc = d;
  }

   @Override
   public void run() {
       ykq.lock();
       System.out.println("小明强到遥控器壳");
       if (dc.isLocked()) {//判断是否有锁
           ykq.unlock();
      }
            dc.lock();
             System.out.println("小明强到电池");
            dc.unlock();
       if (ykq.isLocked()){//有锁就解锁
           ykq.unlock();
      }



  }
}
class XiaoHong2 implements Runnable{

   private ReentrantLock ykq;
   private ReentrantLock dc;

   public XiaoHong2(ReentrantLock y, ReentrantLock d) {
       this.ykq = y;
       this.dc = d;
  }

   @Override
   public void run() {

       dc.lock();
         System.out.println("小红强到遥控器壳");
            ykq.lock();
            System.out.println("小红强到电池");
            ykq.unlock();
       dc.unlock();

  }
}

6.2 Lock锁

package com.yang.lock;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/*
    Lock锁:
    1] 多个线程使用时一定要保证同一个锁对象
    2]lock.lock()加锁。lock.unlock()解锁。
    3]如果代码出现了异常,程序不会自动解锁,所以使用Lock的时候 最好写到try catch finally代码中
*/
public class Demo05 {
   public static void main(String[] args) {
       //两个对象
       Thread th = new Thread(new TicketTwo(),"窗口A");
       Thread th1 = new Thread(new TicketTwo(),"窗口B");
       th.start();
       th1.start();

  }
}

class TicketTwo implements Runnable {
   static int num = 1;

   static Lock lock = new ReentrantLock();//创建Lock锁 两个对象加一个static

   @Override
   public void run() {

       while (num<=100){

           lock.lock();
           try {
               if (num <= 100) {
                   System.out.println(Thread.currentThread().getName() + "卖了票" + num + "张票");
                   num++;
              } else {
                   System.out.println("票已经卖完了");
              }
              //int a = 10/0;//异常
          } catch (Exception e) {
               e.printStackTrace();
          } finally {
               lock.unlock();//将unLock放入finally中,有异常后,也可以将锁给释放
          }
      }



  }
}

7.Lock和synchronized的选择

  1. 类型不同

    • synchronized 是关键字,修饰方法,修饰代码块

    • Lock是接口

  2. 加锁和解锁机制同步

    • synchronized 是自动枷锁和解锁,程序员不需要控制

    • Lock 是需要程序员手动加锁和解锁,要注意的是:出现异常不会自动解锁的情况。

  3. 异常机制

    • synchronized 遇到异常会自动解锁,不会出现死锁的情况

    • Lock 不会自动解锁,遇到异常可能会出现死锁的情况,所以要放到finally()中。

  4. Lock功能更强大

    • Lock中可以有tryLock()/ isLock()方法判断是否上锁。synchronized 不可以。

  5. Lock锁性能更优

    • 如果多线程竞争锁,Lock更好,如果线程不多差距不大

  6. 锁的内容不同

    • synchronized 可以锁方法,锁代码块

    • lock只可以锁代码块

    •