基本概念

程序(program)

是为完成特定任务,用某种语言编写的一组指令的集合,一段静态的代码,是一个静态的东西

进程(process)

是程序的一次执行过程,或者说正在运行的一个程序。是一个动态的过程,有塔自身产生,存在,消亡的过程。进程的生命周期。
比如QQ,网易云音乐,这些是程序,一旦运行起来,就加载到内存中变成进程了

线程(thread)

线程是在进程里面的,是一个程序内部的一条执行路径。只有一线程,程序排队执行,如果有8线程,程序分为8队同时进行。每个线程拥有独立的运行栈和程序计数器。多线程可以共享进程的内存地址
线程可能有安全隐患。线程开的越多,cpu内存占用越多。
java应用程序至少有三个线程,main()主线程,gc()垃圾回收线程,异常处理线程

并行

多个cpu同时执行多个任务,比如:多个人同时做不同的事

并发

一个cpu同时执行多个任务,比如:限时秒杀,多个人同时做一件事,多个人好比多个线程

线程创建

1. 继承于Thread类

  1. public class test1 {
  2. public static void main(String[] args) {
  3. //3. 创建Thread类的子类对象
  4. MyThread t1 = new MyThread();
  5. //4. 通过这个对象调用start方法
  6. t1.start();//一个线程只能start一次,如果还要调用分线程,就在new一个线程对象
  7. //上面的创建t1和调用start是主线程做的,但是t1是新开的线程,t1里面的run方法里面的代码是新的线程在做
  8. //从start开始这时候t1和main一起执行
  9. for(int i=0;i<10000;i++){
  10. if(i%3==0)
  11. System.out.println("****");
  12. }
  13. }
  14. }
  15. //1. 创建一个继承于Thread类的子类
  16. class MyThread extends Thread{
  17. //2. 重写Thread类的run方法,将此线程要执行的事写在run方法中
  18. public void run() {
  19. for(int i=0;i<10000;i++){
  20. if(i%3==0)
  21. System.out.println(i);
  22. }
  23. }
  24. }

运行结果发现,不是先打印**,也不是先打印数字,两个循环一起执行,由于cpu算的太快,打印穿插起来了
image.png

2. 实现Runnable接口

  1. public class test1 {
  2. public static void main(String[] args) {
  3. //3. 创建实现类对象
  4. InterThread interThread = new InterThread();
  5. //4. 将此对象作为参数传递到Thread构造器中,创建Tread对象
  6. Thread thread = new Thread(interThread);
  7. //5. 通过Thread对象调用start方法
  8. thread.start();
  9. }
  10. }
  11. //1. 创建一个实现了Runnable接口的类
  12. class InterThread implements Runnable{
  13. //2. 重写run方法
  14. public void run() {
  15. for(int i=0;i<100;i++){
  16. System.out.println(i);
  17. }
  18. }
  19. }

3. 实现Callable接口

  1. public class test1 {
  2. public static void main(String[] args) {
  3. //3. 实现类实例化
  4. myThread t1 = new myThread();
  5. //4. 创建一个FutureTask对象,传入实现类对象,支持泛型,call返回什么类型,就写什么类型
  6. FutureTask<Integer> futureTask = new FutureTask<Integer>(t1);
  7. //5. 实例化Thread对象,传入futureTask对象,开始线程
  8. new Thread(futureTask).start();
  9. //如果对call方法的返回值不感兴趣,就不用调下面的get方法
  10. try{
  11. //6. 可以通过FutureTask对象的get方法获得线程方法的返回值
  12. Object sum = futureTask.get();
  13. System.out.println("总和:"+sum);
  14. }catch (Exception e){
  15. e.printStackTrace();
  16. }
  17. }
  18. }
  19. //1. 实现Callable接口
  20. class myThread implements Callable{
  21. int sum=0;
  22. //2. 重写call方法,有返回值
  23. @Override
  24. public Object call() throws Exception {
  25. for(int i =1;i<1000;i++){
  26. System.out.println(i);
  27. sum+=i;
  28. }
  29. return sum;
  30. }
  31. }

4. 线程池

经常创建销毁线程,对性能影响很大。
提前创建好多个线程,放入线程池中,使用时,直接获取,使用完放回池中,可以避免频繁创建销毁实现重复利用。还便于线程管理

  1. public class test1 {
  2. public static void main(String[] args) {
  3. //Executors工具类,线程池的工厂类,用于创建并返回不同类型的线程池
  4. //ExecutorService真正的线程池接口
  5. //newFixedThreadPool创建一个可重用固定线程数的线程池
  6. ExecutorService service = Executors.newFixedThreadPool(10);
  7. //ThreadPoolExecutor是ExecutorService的子类,强转过来,tpe可以进行一系列的配置
  8. ThreadPoolExecutor tpe = (ThreadPoolExecutor)service;
  9. //执行线程操作,具体做什么还是要你自己创建
  10. tpe.execute(new myThread());//适用于Runnable
  11. tpe.execute(new myThread1());
  12. //service.submit();//适用于Callable
  13. //关闭线程池
  14. tpe.shutdown();
  15. }
  16. }
  17. class myThread implements Runnable{
  18. @Override
  19. public void run() {
  20. for(int i =0;i<=1000;i++){
  21. System.out.println(i);
  22. }
  23. }
  24. }
  25. class myThread1 implements Runnable{
  26. @Override
  27. public void run() {
  28. for(int i =0;i<=100;i++){
  29. System.out.println("哈哈哈哈哈"+i);
  30. }
  31. }
  32. }

线程的常用方法

  1. Thread.currentThread():静态方法,返回当前执行当前代码的线程
  2. getName():获取当前线程名字
  3. setName():设置当前线程名字
  4. yield():一旦线程执行此方法,表示释放当前cpu执行,释放了下一次操作,有可能又被这个线程抢到了,有可能被别的线程拿到了
  5. join():在当前线程调用其他线程的join方法,当前线程停下来,等调用join方法的那个线程执行完了,当前线程才开始执行
  6. stop():已过时,当执行此方法,强制结束当前线程
  7. sleep():传入毫秒,休眠线程
  8. isAlive():判断线程是否还存活

    线程优先级

    同优先级线程组成先进先出队列,先到先服务,使用时间片策略
    对高优先级,使用优先调度的抢占式策略,只是先执行的概率高

MAX_PRIORITY:10 最大优先级10 MIN_PRIORITY:1 最小优先级1 NORM_PRTORITY:5 默认优先级5

  1. public class test1 {
  2. public static void main(String[] args) {
  3. Thread t1 = new MyThread();
  4. Thread t2 = new MyThread1();
  5. t1.setName("线程1号");
  6. t2.setName("线程2号");
  7. t1.setPriority(Thread.MIN_PRIORITY);
  8. t2.setPriority(9);
  9. t1.start();
  10. t2.start();
  11. }
  12. }
  13. //使用getPriority方法获取当前线程的优先级,默认是5
  14. //setPriority设置优先级
  15. class MyThread extends Thread{
  16. public void run() {
  17. for(int i=0;i<100;i++){
  18. if(i%3==0)
  19. System.out.println(this.getName()+i+"线程优先级:"+this.getPriority());
  20. }
  21. }
  22. }
  23. class MyThread1 extends Thread{
  24. public void run() {
  25. for(int i=0;i<100;i++){
  26. if(i%3==0)
  27. System.out.println(this.getName()+i+"线程优先级:"+this.getPriority());
  28. }
  29. }
  30. }

线程的生命周期

线程同步

多线程操作同一个共享数据时,容易出差错。假如:1,2,3线程都同时进行money变量的操作,money本来有3000,1线程取钱2000,2线程取钱3000,如果1,2同时执行了,最后money变成负的。

所以需要在线程1操作共享数据时,其他线程不能参与进来,直到线程1操作完成,其他想线程才能开始操作共享数据,即使线程1被阻塞,也不能改变

在java中,通过同步机制解决线程安全问题

安全问题演示

在这段代码中,count是一个共享数据,应该是唯一的,3线程同步执行,每次都休眠20毫秒,这3个线程都在这20毫秒进入准备状态,同时执行,所以会出现重复数据,错误数据等情况

  1. public class test1 {
  2. public static void main(String[] args) {
  3. //3. 创建实现类对象
  4. InterThread interThread = new InterThread();
  5. Thread thread = new Thread(interThread);
  6. Thread thread2 = new Thread(interThread);
  7. Thread thread3 = new Thread(interThread);
  8. thread.start();
  9. thread2.start();
  10. thread3.start();
  11. }
  12. }
  13. class InterThread implements Runnable{
  14. int count = 100;
  15. public void run() {
  16. for(;count>=0;count--){
  17. try {
  18. Thread.sleep(20);
  19. } catch (InterruptedException e) {
  20. e.printStackTrace();
  21. }
  22. System.out.println(Thread.currentThread().getName()+":"+ count);
  23. }
  24. }
  25. }

image.png

1. 同步代码块

  1. class InterThread implements Runnable{
  2. int count = 100;//共享数据
  3. String a="同步锁,只要是个对象,什么对象什么内容都可以,多线程必须要用同一把锁";
  4. public void run() {
  5. while (true){
  6. //在synchronized中传入同步锁,代码块中写要同步的代码
  7. //不要包住循环,包住循环,就相当于第一个线程拿到锁,就要执行完整个循环才结束,结束循环后共享变量都用完了,其他的线程干瞪眼无作为
  8. synchronized (a){
  9. if(count>0){
  10. System.out.println(Thread.currentThread().getName()+":"+ count);
  11. count--;
  12. }
  13. else {
  14. break;
  15. }
  16. }
  17. }
  18. }
  19. }

image.png

2. 同步方法

  1. class InterThread implements Runnable{
  2. int count = 100;//共享数据
  3. String a="同步锁,只要是个对象,什么对象什么内容都可以,多线程必须要用通一把锁";
  4. public void run() {
  5. while (true){
  6. if(count>0){
  7. show();
  8. }
  9. else {
  10. break;
  11. }
  12. }
  13. }
  14. //同步方法
  15. //非静态方法,同步监视器是this
  16. //静态方法,同步监视器是当前类本身
  17. public synchronized void show(){
  18. if(count>0){
  19. System.out.println(Thread.currentThread().getName()+":"+ count);
  20. count--;
  21. }
  22. }
  23. }

3. lock

  1. class InterThread implements Runnable{
  2. int count = 1000;//共享数据
  3. //1. 实例化一个lock,可以传参数,是个bool类型,默认false,线程抢单,传入了true就让线程排队按顺序执行
  4. private ReentrantLock lock =new ReentrantLock();
  5. public void run() {
  6. while (true){
  7. try{
  8. //2. 调用lock方法,用try catch finally,try把要同步的代码包起来,lock()方法个try这代码块上同步锁
  9. lock.lock();
  10. if(count>0){
  11. System.out.println(Thread.currentThread().getName()+":"+ count);
  12. count--;
  13. }
  14. else {
  15. break;
  16. }
  17. }finally {
  18. //没有异常就不用catch,一定要finally,因为要解锁
  19. //3. 解锁,最后同步执行完成前面上锁了,后面就要把锁解开
  20. lock.unlock();
  21. }
  22. }
  23. }
  24. }

线程死锁

不同的线程分别占用对方需要的同步资源不放弃,都在等对方放弃自己需要的同步资源,就形成了死锁。
出现死锁后,不会出现异常,不会出现提示,只是所有的线程都处于阻塞状态,无法继续。

  1. public class test1 {
  2. public static void main(String[] args) {
  3. StringBuffer s1 = new StringBuffer();
  4. StringBuffer s2 = new StringBuffer();
  5. //匿名的方式创建的线程
  6. new Thread(){
  7. public void run(){
  8. //这个线程,先握住s1这把锁,然后又握住s2这把锁,两把锁在身上
  9. synchronized (s1){
  10. //线程1拿到了s1,然后被阻塞了100毫秒,计算机100毫秒可以做很多事,这100毫秒线程2可没闲着
  11. try {
  12. Thread.sleep(100);
  13. } catch (InterruptedException e) {
  14. e.printStackTrace();
  15. }
  16. s1.append("a");
  17. s2.append("1");
  18. //阻塞完毕了,线程1需要s2这把锁,但是被线程2拿了
  19. synchronized (s2){
  20. s1.append("b");
  21. s2.append("2");
  22. System.out.println(s1);
  23. System.out.println(s2);
  24. }
  25. }
  26. }
  27. }.start();
  28. //两种匿名的方式
  29. new Thread(new Runnable() {
  30. public void run() {
  31. //这个线程,先握住s2这把锁,然后又握住s1这把锁,两把锁在身上
  32. synchronized (s2){
  33. //线程并发一起执行的,线程1被阻塞的同时,线程2拿到了s2这把锁,同时也被阻塞
  34. try {
  35. Thread.sleep(100);
  36. } catch (InterruptedException e) {
  37. e.printStackTrace();
  38. }
  39. s1.append("c");
  40. s2.append("3");
  41. //线程2现在需要s1这把锁,但是s1在线程1那里。程序也不是人,懂什么互相谦让,两个线程都没有第二把锁,进不去下面的步骤
  42. //都在这里僵持着,耗着,就都阻塞了,这就形成了死锁
  43. synchronized (s1){
  44. s1.append("d");
  45. s2.append("4");
  46. System.out.println(s1);
  47. System.out.println(s2);
  48. }
  49. }
  50. }
  51. }).start();
  52. }
  53. }