计算机中的概念:

并发:进程(指两个或多个任务,在同一时间段内执行的(交替执行的))
是指一个内存中运行的应用程序,每个进程都有一个独立的内存空间,一个应用程序可以同时运行多个进程;
进程也是程序的一次执行过程,是系统运行程序的基本单位;系统运行一个程序即是一个进程从创建、运行到消亡的过程。
并行:线程(指两个或多个任务,在同一时刻执行的(同时执行的))
线程是进程中的一个执行单元,负责当前进程中程序的执行,
一个进程中至少有一个线程。一个进程中是可以有多个线程的,这个应用程序也可以称之为多线程程序。
简而言之:一个程序运行后至少有一个进程,一个进程中可以包含多个线程
一个程序运行代表一个进程,一个进程可以有多个线程

线程的分类:

用户线程(普通线程):线程的代码必须执行完毕之后才能结束
守护线程(待在一个线程的后边):可以不用等到全部执行完毕就能结束线程,,,,陪着一个特定的线程,待特定线程死亡跟着死亡
主线程(顾名思义):就是执行主方法(main)的线程,该主线程是一个单线程的

线程的创建:

继承Thread类:创建一个类继承该类。进而重写run方法
实现Runnable接口:创建一个类实现该接口,进而重写run方法

  1. public class MyThread extends Thread{
  2. @Override
  3. public void run() {
  4. //待操作代码快
  5. //getName() 获取当前线程名称。返回值为String
  6. //Thread.currentThread.getName() 获取主线程名称 返回对当前正在执行的线程对象的引用
  7. }
  8. }
  1. public class ThreadDemo {
  2. public static void main(String[] args) {
  3. MyThread mt = new MyThread(); //创建实例
  4. Thread th = new Thread(); //创建Thread类,也就是创建线程对象
  5. //Thread th = new Thread(mt); //创建一个带指定目标的线程对象
  6. //Thread th = new Thread(mt,"线程一"); //创建一个带指定目标和指定名称的线程对象
  7. //th.run() 此线程要执行的任务在此处定义代码(直接用start了,这个也就是个摆设)
  8. th.start(); // Java虚拟机调用此线程的run方法 -->进入就绪状态
  9. }

Thread&Runnable:

如果一个类继承Thread,则不适合资源共享。但是如果实现了Runable接口的话,则很容易的实现资源共享。
实现Runnable接口比继承Thread类所具有的优势
1.适合多个相同的程序代码的线程去共享同一个资源。
2.可以避免java中的单继承的局限性。
3.增加程序的健壮性,实现解耦操作,代码可以被多个线程共享,代码和线程独立。
4.线程池只能放入实现Runable或Callable类线程,不能直接放入继承Thread的类。

其两者的作用大差不差,都能创建线程,后续的操作中使用的还是Thread中的方法
两者的区别在于一个是类一个是接口,点击Thread进去(public class Thread implements Runnable {})不难看出Thread也是实现了Runnable接口
所以在使用的过程中两者区别不大,复杂些的线程用Thread,简单的线程用Runnable

线程的生命周期:

image.png

线程的状态方法:

Sleep:就是执行到这句话的时候停一会,停多久?可以控制的,单位为毫秒(Thread.sleep(毫秒数);)
使线程停止运行一段时间,将处于阻塞状态
如果调用了sleep方法之后,没有其他等待执行的线程,这个时候当前线程不会马上恢复执行!

  1. public class ThreadSleep extends Thread {
  2. @Override
  3. public void run() {
  4. for (int i = 0; i < 100; i++) {
  5. System.out.println(getName() + ":" + i);
  6. try {
  7. Thread.sleep(1000);
  8. } catch (InterruptedException e) {
  9. e.printStackTrace();
  10. }
  11. }
  12. }
  13. }
  14. -------------------------------------------------------------------------------------------------
  15. public class ThreadSleepDemo {
  16. public static void main(String[] args) {
  17. ThreadSleep ts1 = new ThreadSleep();
  18. ThreadSleep ts2 = new ThreadSleep();
  19. ThreadSleep ts3 = new ThreadSleep();
  20. ts1.setName("曹操");
  21. ts2.setName("刘备");
  22. ts3.setName("孙权");
  23. ts1.start();
  24. ts2.start();
  25. ts3.start();
  26. }
  27. }

Join:就是让其他线程停下来,让去指定的线程走完,然后再回来走其他线程(线程名.join;指定先走的线程名哦,跟在指定线程的start后边)
阻塞指定线程,等到另一个线程完成以后再继续执行。

  1. public class ThreadJoin extends Thread {
  2. @Override
  3. public void run() {
  4. for (int i = 0; i < 100; i++) {
  5. System.out.println(getName() + ":" + i);
  6. }
  7. }
  8. }
  9. -------------------------------------------------------------------------------------------------
  10. public class ThreadJoinDemo {
  11. public static void main(String[] args) {
  12. ThreadJoin tj1 = new ThreadJoin();
  13. ThreadJoin tj2 = new ThreadJoin();
  14. ThreadJoin tj3 = new ThreadJoin();
  15. tj1.setName("康熙");
  16. tj2.setName("四阿哥");
  17. tj3.setName("八阿哥");
  18. tj1.start();
  19. try {
  20. tj1.join();
  21. } catch (InterruptedException e) {
  22. e.printStackTrace();
  23. }
  24. tj2.start();
  25. tj3.start();
  26. }
  27. }

setDeamon:也就是设置拜把子兄弟,其中一个嗝屁了其他的也跟着嗝屁(线程名.setDeamon(true);)
可以将指定的线程设置成后台线程,守护线程;
创建用户线程的线程结束时,后台线程也随之消亡;
只能在线程启动之前把它设为后台线程

  1. public class ThreadDaemon extends Thread {
  2. @Override
  3. public void run() {
  4. for (int i = 0; i < 100; i++) {
  5. System.out.println(getName() + ":" + i);
  6. }
  7. }
  8. }
  9. -------------------------------------------------------------------------------------------------
  10. public class ThreadDaemonDemo {
  11. public static void main(String[] args) {
  12. ThreadDaemon td1 = new ThreadDaemon();
  13. ThreadDaemon td2 = new ThreadDaemon();
  14. td1.setName("关羽");
  15. td2.setName("张飞");
  16. //设置主线程为刘备
  17. Thread.currentThread().setName("刘备");
  18. //设置守护线程
  19. td1.setDaemon(true);
  20. td2.setDaemon(true);
  21. td1.start();
  22. td2.start();
  23. for(int i=0; i<10; i++) {
  24. System.out.println(Thread.currentThread().getName()+":"+i);
  25. }
  26. }
  27. }

setPriority:设置该线程去走程序更积极一些,并不是完全就是这个先走(线程名.setPriority(5);)
线程的优先级代表的是概率
范围从1到10,默认为5

  1. public class ThreadPriority extends Thread {
  2. @Override
  3. public void run() {
  4. for (int i = 0; i < 100; i++) {
  5. System.out.println(getName() + ":" + i);
  6. }
  7. }
  8. }
  9. -----------------------------------------------------------------------------------------------
  10. public class ThreadPriorityDemo {
  11. public static void main(String[] args) {
  12. ThreadPriority tp1 = new ThreadPriority();
  13. ThreadPriority tp2 = new ThreadPriority();
  14. ThreadPriority tp3 = new ThreadPriority();
  15. tp1.setName("高铁");
  16. tp2.setName("飞机");
  17. tp3.setName("汽车");
  18. //public final int getPriority():返回此线程的优先级
  19. // System.out.println(tp1.getPriority()); //5
  20. // System.out.println(tp2.getPriority()); //5
  21. // System.out.println(tp3.getPriority()); //5
  22. //public final void setPriority(int newPriority):更改此线程的优先级
  23. // tp1.setPriority(10000); //IllegalArgumentException
  24. // System.out.println(Thread.MAX_PRIORITY); //10
  25. // System.out.println(Thread.MIN_PRIORITY); //1
  26. // System.out.println(Thread.NORM_PRIORITY); //5
  27. //设置正确的优先级
  28. tp1.setPriority(5);
  29. tp2.setPriority(10);
  30. tp3.setPriority(1);
  31. tp1.start();
  32. tp2.start();
  33. tp3.start();
  34. }
  35. }

yield:也就是让行,我不先走这个程序的全部,我运行一部分然后让其他线程运行,然后我再运行
让当前正在执行线程暂停,不是阻塞线程,而是将线程转入就绪状态;
调用了yield方法之后,如果没有其他等待执行的线程,此时当前线程就会马上恢复执行!

  1. public class ThreadYield extends Thread{
  2. @Override
  3. public void run() {
  4. System.out.println(getName()+"-->start");
  5. Thread.yield(); //礼让
  6. System.out.println(getName()+"-->end");
  7. }
  8. }
  9. -------------------------------------------------------------------------------------------------
  10. public class YieldDemo {
  11. public static void main(String[] args) {
  12. ThreadYield ty1 =new ThreadYield();
  13. ThreadYield ty2 =new ThreadYield();
  14. ty1.setName("a");
  15. ty2.setName("b");
  16. ty1.start();
  17. ty2.start();
  18. }
  19. }

线程同步:

线程安全:当多个线程操作同一份数据时,数据的结果有可能跟我们想像的不一样,这就是线程安全问题
同步代码块:关键字 ( synchronized )
synchronized(同步锁){ //对象的同步锁只是一个概念,可以想象为在对象上标记了一个锁
需要同步操作的代码
}
锁对象 可以是任意类型。<br />多个线程对象 要使用同一把锁。

  1. public class RunnableImpl implements Runnable{
  2. //定义一个多个线程共享的票源
  3. private int ticket = 100;
  4. //创建一个锁对象
  5. Object obj = new Object();
  6. //设置线程任务:卖票
  7. @Override
  8. public void run() {
  9. //使用死循环,让卖票操作重复执行
  10. while(true){
  11. //同步代码块
  12. synchronized (obj){
  13. //先判断票是否存在
  14. if(ticket>0){
  15. //提高安全问题出现的概率,让程序睡眠
  16. try {
  17. Thread.sleep(10);
  18. } catch (InterruptedException e) {
  19. e.printStackTrace();
  20. }
  21. //票存在,卖票 ticket--
  22. System.out.println(Thread.currentThread().getName()+"-->正在卖第"+ticket+"张票");
  23. ticket--;
  24. }
  25. }
  26. }
  27. }
  28. }
  29. ----------------------------------------------------------------------
  30. public class Demo01Ticket {
  31. public static void main(String[] args) {
  32. //创建Runnable接口的实现类对象
  33. RunnableImpl run = new RunnableImpl();
  34. //创建Thread类对象,构造方法中传递Runnable接口的实现类对象
  35. Thread t0 = new Thread(run,"窗口1");
  36. Thread t1 = new Thread(run,"窗口2");
  37. Thread t2 = new Thread(run,"窗口3");
  38. //调用start方法开启多线程
  39. t0.start();
  40. t1.start();
  41. t2.start();
  42. }
  43. }

同步方法:
public synchronized void method(锁){
可能会产生线程安全问题的代码
}
对于非static方法,同步锁就是this。
对于static方法,我们使用当前方法所在类的字节码对象(类名.class)。

锁:

锁的进阶==》先考虑对象锁,其次是this锁,如果前两个仍不能保证数据的完整性,考虑使用字节码对象锁(类名.class)

  1. public class RunnableImpl implements Runnable{
  2. //定义一个多个线程共享的票源
  3. private static int ticket = 100;
  4. //设置线程任务:卖票
  5. @Override
  6. public void run() {
  7. System.out.println("this:"+this);//this:com.itfxp.Synchronized.RunnableImpl@58ceff1
  8. //使用死循环,让卖票操作重复执行
  9. while(true){
  10. payTicketStatic();
  11. }
  12. }
  13. /*
  14. 静态的同步方法
  15. 锁对象是谁?
  16. 不能是this
  17. this是创建对象之后产生的,静态方法优先于对象
  18. 静态方法的锁对象是本类的class属性-->class文件对象(反射)
  19. */
  20. public static /*synchronized*/ void payTicketStatic(){
  21. synchronized (RunnableImpl.class){
  22. //先判断票是否存在
  23. if(ticket>0){
  24. //提高安全问题出现的概率,让程序睡眠
  25. try {
  26. Thread.sleep(10);
  27. } catch (InterruptedException e) {
  28. e.printStackTrace();
  29. }
  30. //票存在,卖票 ticket--
  31. System.out.println(Thread.currentThread().getName()+"-->正在卖第"+ticket+"张票");
  32. ticket--;
  33. }
  34. }
  35. }
  36. /*
  37. 定义一个同步方法
  38. 同步方法也会把方法内部的代码锁住
  39. 只让一个线程执行
  40. 同步方法的锁对象是谁?
  41. 就是实现类对象 new RunnableImpl()
  42. 也是就是this
  43. */
  44. public /*synchronized*/ void payTicket(){
  45. synchronized (this){
  46. //先判断票是否存在
  47. if(ticket>0){
  48. //提高安全问题出现的概率,让程序睡眠
  49. try {
  50. Thread.sleep(10);
  51. } catch (InterruptedException e) {
  52. e.printStackTrace();
  53. }
  54. //票存在,卖票 ticket--
  55. System.out.println(Thread.currentThread().getName()+"-->正在卖第"+ticket+"张票");
  56. ticket--;
  57. }
  58. }
  59. }
  60. }

Lock锁:同步代码块/同步方法具有的功能Lock都有,除此之外更强大,更体现面向对象。

  1. package com.itheima.demo09.Lock;
  2. import java.util.concurrent.locks.Lock;
  3. import java.util.concurrent.locks.ReentrantLock;
  4. /*
  5. 卖票案例出现了线程安全问题
  6. 卖出了不存在的票和重复的票
  7. 解决线程安全问题的三种方案:使用Lock锁
  8. java.util.concurrent.locks.Lock接口
  9. Lock 实现提供了比使用 synchronized 方法和语句可获得的更广泛的锁定操作。
  10. Lock接口中的方法:
  11. void lock()获取锁。
  12. void unlock() 释放锁。
  13. java.util.concurrent.locks.ReentrantLock implements Lock接口
  14. 使用步骤:
  15. 1.在成员位置创建一个ReentrantLock对象
  16. 2.在可能会出现安全问题的代码前调用Lock接口中的方法lock获取锁
  17. 3.在可能会出现安全问题的代码后调用Lock接口中的方法unlock释放锁
  18. */
  19. public class RunnableImpl implements Runnable{
  20. //定义一个多个线程共享的票源
  21. private int ticket = 100;
  22. //1.在成员位置创建一个ReentrantLock对象
  23. Lock l = new ReentrantLock();
  24. //设置线程任务:卖票
  25. @Override
  26. public void run() {
  27. //使用死循环,让卖票操作重复执行
  28. while(true){
  29. //2.在可能会出现安全问题的代码前调用Lock接口中的方法lock获取锁
  30. l.lock();
  31. //先判断票是否存在
  32. if(ticket>0){
  33. //提高安全问题出现的概率,让程序睡眠
  34. try {
  35. Thread.sleep(10);
  36. //票存在,卖票 ticket--
  37. System.out.println(Thread.currentThread().getName()+"-->正在卖第"+ticket+"张票");
  38. ticket--;
  39. } catch (InterruptedException e) {
  40. e.printStackTrace();
  41. }finally {
  42. //3.在可能会出现安全问题的代码后调用Lock接口中的方法unlock释放锁
  43. l.unlock();//无论程序是否异常,都会把锁释放
  44. }
  45. }
  46. }
  47. }
  48. }