线程和高并发

 

线程:作为一个进程里面最小的执行单元它就叫一个线程,用简单的话讲一个程序里不同的执行路径就叫做一个线程

进程:做一个简单的解释,你的硬盘上有一个简单的程序,这个程序叫QQ.exe,这是一个程序,这个程序是一个静态的概念,它被扔在硬盘上也没人理他,但是当你双击它,弹出一个界面输入账号密码登录进去了,OK,这个时候叫做一个进程。进程相对于程序来说它是一个动态的概念

  private static class T1 extends Thread {
     @Override
     public void run() {
      for(int i=0; i<10; i++) {
        try {
          TimeUnit.MICROSECONDS.sleep(1);
        } catch (InterruptedException e) {
          e.printStackTrace();
        }
        System.out.println("T1");
      }
    }
  }
   public static void main(String[] args) {
     //new T1().run();当用run跑起程序的时候线程是先进行第一个进程,后进行第二个进程
     new T1().start();//当用start方法跑起程序的时候线程运行交替进行
     for(int i=0; i<10; i++) {
       try {
         TimeUnit.MICROSECONDS.sleep(1);
      } catch (InterruptedException e) {
         e.printStackTrace();
      }
       System.out.println("main");
    }
  }
 }

启动线程的三种方式:

1.从Thread类中集成

2.实现Runnable接口

3.从线程池中启动

 public class T02_HowToCreateThread {
   static class MyThread extends Thread {
     @Override
     public void run() {
       System.out.println("Hello MyThread!");
    }
  }
   static class MyRun implements Runnable {
     @Override
     public void run() {
       System.out.println("Hello MyRun!");
    }
  }
     static class MyCall implements Callable<String> {
     @Override
     public String call() {
       System.out.println("Hello MyCall");
       return "success";
    }
  }
   //启动线程的5种方式
   public static void main(String[] args) {
     new MyThread().start();   //继承
     new Thread(new MyRun()).start();//实现接口
     new Thread(()->{
       System.out.println("Hello Lambda!");
    }).start();//表达式
     Thread t = new Thread(new FutureTask<String>(new MyCall()));
     t.start();
     ExecutorService service = Executors.newCachedThreadPool();
     service.execute(()->{
       System.out.println("Hello ThreadPool");
    });
     service.shutdown();
  }
 }

获得当前线程的名字:Thread.currentThread().getName()

睡眠:sleep

谦让的退出一下:yield

等待另个线程结束,可以按顺序进行:join

线程正常结束即可关闭,不建议用stop。

回顾并发小程序

 
 public class Test03 implements Runnable{
     private int i=10;
     @Override
     public synchronized void run() {
         while (true){
          if (i<=0){
             break;
        }
             try {
                 Thread.sleep(200);
            } catch (InterruptedException e) {
                 e.printStackTrace();
            }
             System.out.println(Thread.currentThread().getName()+"拿到了第"+i--+"张票");
            }
         System.out.println("thread test");
    }
 
     public static void main(String[] args) {
         Test03 thread= new Test03();
         new Thread(thread,"小明").start();
         new Thread(thread,"小弘").start();
         new Thread(thread,"小张").start();
    }
 }

有锁将票数完整输出

synchronized

我们对一个数字做递增,两个程序对它一块儿来做递增,递增就是把一个程序往上加1啊,如果两个线程共同访问的时候,第一个线程一读它是0,然后把它加1,在自己线程内部内存里面算还没有写回去的时候而第二个线程读到了它还是0,加1在写回去,本来加了两次,但还是1,那么我们在对这个 数字递增的过程当中就上把锁,就是说第一个线程对这个数字访问的时候是独占的,不允许别的线程来访问,不允许别的线程来对它计算,我必须加完1收释放锁,其他线程才能对它继续加。实质上,这把锁并不是对数字进行锁定的, 你可以任意指定,想锁谁就锁谁。

 /**
 *synchronized关键字
 *对某个对象加锁
 *@author mashibing
 */
 package com.mashibing.juc.c_001;
 public class T {
     private int count = 10;
     private Object o = new Object();
     public void m() {
         synchronized(o) { //任何线程要想执行下面的代码,必须先拿到o的锁
             count--;
             System.out.println(Thread.currentThread().getName() + " count = " +
                                count);
        }}}

如果说你每次都定义个一个锁的对象Object o 把它new出来那加锁的时候太麻烦每次都要new一个新的对象出来,所以呢,有一个简单的方式就是 synchronized(this)锁定当前对象就行

 /**
 * synchronized关键字
 * 对某个对象加锁
 * @author mashibing
 */
 package com.mashibing.juc.c_002;
 
 public class T {
     private int count = 10;
     public void m() {
         synchronized(this) { ߳//任何线程想要执行那个下面的代码,必须先要拿到this的锁
             count--;
                             System.out.println(Thread.currentThread().getName() + " count = " + count);}}}

如果你要是锁定当前对象呢,你也可以写成如下方法。synchronized方法和synchronized(this)执行这段代码它是等值的

 package com.mashibing.juc.c_003;
 
 public class T {
     private Aint count = 10;
     public synchronized void m() { //等同于在方法的代码执行时要synchronized(this)
         count--;
         System.out.println(Thread.currentThread().getName() + " count = " + count);
    }
 }

有锁无锁同时使用

 package com.mashibing.juc.c_007;
 public class T {
     public synchronized void m1() {
         System.out.println(Thread.currentThread().getName() + " m1 start...");
         try {
             Thread.sleep(10000);
        } catch (InterruptedException e) {
             e.printStackTrace();
        }
         System.out.println(Thread.currentThread().getName() + " m1 end");
    }
     public void m2() {
         try {
             Thread.sleep(5000);
        } catch (InterruptedException e) {
             e.printStackTrace();
        }
         System.out.println(Thread.currentThread().getName() + " m2 ");
    }
     public static void main(String[] args) {
         T t = new T();
         /*new Thread(()->t.m1(), "t1").start();
 new Thread(()->t.m2(), "t2").start();*/
         new Thread(t::m1, "t1").start();
         new Thread(t::m2, "t2").start();
         /*
 //1.8之前的写法
 new Thread(new Runnable() {
 @Override
 public void run() {
 t.m1();
 }
 });
 */
    }
 }

可重入

如果是一个同步方法调用另外一个同步方法,有一个方法加了锁,另外一个方法也需要加锁,加的是同一把锁也是同一个线程,那这个时候申请仍然会得到该对象的锁。比如说是synchronized可重入的,有一个方法m1 是synchronized有一个方法m2也是synchrionzed,m1里能不能调m2。我们m1开始的时候这个线程得到了这把锁,然后在m1里面调用m2,如果说这个时候不允许任何线程再来拿这把锁的时候就死锁了。这个时候调m2它发现是同一个线程,因为你m2也需要申请这把锁,它发现是同一个线程申请的这把锁,允许,可以没问题,这就叫可重入锁。

当一个有锁的线程遇上了异常,则做会被破坏另一个程序运行

posted @ 2020-06-23 17:49  我是小杨  阅读(338)  评论(0)    收藏  举报