线程入门

1.创建线程的三种方式

1.1 继承Thread类,重写run()

  1. public class ExtendThread extends Thread { //继承Thread类
  2. @Override
  3. public void run() { //重写run()方法, run()方法中包含线程的执行体
  4. for (int i = 0; i < 1000; i++) {
  5. System.out.println("画圆");
  6. }
  7. }
  8. public static void main(String[] args) {
  9. //创建实例,start()使线程进入就绪状态
  10. ExtendThread extendThread = new ExtendThread();
  11. extendThread.start();
  12. //main线程,用于与创建的线程对比
  13. for (int i = 0; i < 1000; i++) {
  14. System.out.println("画方");
  15. }
  16. }
  17. }

1.2 实现Runnable接口,重写run()

  1. public class ImplementsRunnable implements Runnable {
  2. //重写run(), run()方法中包含线程的执行体
  3. @Override
  4. public void run() {
  5. //Thread.sleep()使线程休眠,模拟延时
  6. try {
  7. Thread.sleep(500);
  8. } catch (InterruptedException e) {
  9. e.printStackTrace();
  10. }
  11. for (int i = 0; i < 20; i++) {
  12. System.out.println("画圆");
  13. }
  14. }
  15. public static void main(String[] args) {
  16. //创建实现接口的实例
  17. ImplementsRunnable implementsRunnable = new ImplementsRunnable();
  18. //创建线程的语法
  19. //Thread thread = new Thread(runnable);
  20. //start()使线程进入就绪状态
  21. //thread.start();
  22. //简化为下
  23. new Thread((implementsRunnable)).start();
  24. //main线程,用于与创建的线程对比
  25. for (int i = 0; i < 20; i++) {
  26. System.out.println("画方块");
  27. }
  28. }
  29. }

应用: 龟兔赛跑

  1. //龟兔赛跑
  2. public class Race implements Runnable {
  3. static String Winner = null;
  4. @Override
  5. public void run() {
  6. for (int i = 0; i <= 100; i++) {
  7. System.out.println(Thread.currentThread().getName() + " 跑了 " + i + " 步");
  8. //跑到终点,产生胜者,结束循环
  9. if (gameOver(i)) {
  10. Winner = Thread.currentThread().getName();
  11. System.out.println(Winner+" 赢了");
  12. break;
  13. }
  14. //已存在胜者,结束循环
  15. if (Winner != null) {
  16. break;
  17. }
  18. }
  19. }
  20. private boolean gameOver(int step) {
  21. if (step >= 100) {
  22. return true;
  23. } else {
  24. return false;
  25. }
  26. }
  27. public static void main(String[] args) {
  28. Race race = new Race();
  29. new Thread(race,"兔子").start();
  30. new Thread(race,"乌龟").start();
  31. }
  32. }

1.3 实现Callable接口,重写call()

  1. public class ImplementsCallable implements Callable<Integer> { //Callable后加上泛型
  2. //该 call() 方法将作为线程执行体,并且有返回值。
  3. @Override
  4. public Integer call() throws Exception {
  5. int i;
  6. for (i = 0; i < 10; i++) {
  7. System.out.println(Thread.currentThread().getName()+" "+i);
  8. }
  9. return i;
  10. }
  11. public static void main(String[] args) {
  12. //创建 Callable接口实现类的实例
  13. ImplementsCallable ctt = new ImplementsCallable();
  14. //使用 FutureTask 类来包装 Callable 对象,该 FutureTask 对象封装了该 Callable 对象的 call() 方法的返回值
  15. //类似C++中容器
  16. FutureTask<Integer> ft = new FutureTask<>(ctt);
  17. for (int i = 0; i < 100; i++) {
  18. System.out.println(Thread.currentThread().getName() + " 的循环变量i的值" + i);
  19. if (i == 20) {
  20. //使用 FutureTask类的对象作为 Thread 对象的 target 创建并启动新线程
  21. new Thread(ft, "有返回值的线程").start();
  22. }
  23. }
  24. //调用 FutureTask 对象的 get() 方法来获得子线程执行结束后的返回值
  25. try {
  26. System.out.println("子线程的返回值:" + ft.get());
  27. } catch (Exception e) {
  28. e.printStackTrace();
  29. }
  30. }
  31. }

2.操作线程

sleep()

  1. Thread.sleep(millis);

用sleep()模拟网络延时,放大问题的发生可能性

每个对象都有一把锁,sleep()不会释放锁,类似于抱着锁睡觉

  1. //应用: 模拟倒计时
  2. public class CountDown {
  3. public static void main(String[] args) {
  4. count(10);
  5. }
  6. static void count(int time) {
  7. for (; time >= 0; time--) {
  8. try {
  9. Thread.sleep(1000); // 模拟延时, 1000ms
  10. } catch (InterruptedException e) {
  11. e.printStackTrace();
  12. }
  13. System.out.println(time + " second left...");
  14. }
  15. }
  16. }

stop()

在Runnable的实现类中定义标志位, 并定义一个用于转换标志位停止线程的stop()方法, 在线程体run()中通过判断标志位状态来控制线程

  1. public class ThreadStop implements Runnable {
  2. //定义标志位
  3. boolean flag = true;
  4. //线程体
  5. @Override
  6. public void run() {
  7. //线程体使用标志位控制
  8. while (flag) {
  9. //循环输出,直至主线程中for循环到第80次
  10. System.out.println("Thread is running...");
  11. }
  12. }
  13. //定义一个公开的stop()方法,用于转换标志位,停止线程
  14. public void stop() {
  15. this.flag = false;
  16. System.out.println("分线程停止了");
  17. }
  18. public static void main(String[] args) {
  19. ThreadStop runnable = new ThreadStop();
  20. Thread thread = new Thread(runnable);
  21. thread.start();
  22. //主线程
  23. for (int i = 0; i < 100; i++) {
  24. System.out.println("main is running..." +i);
  25. if (i == 80) {
  26. //stop方法,转换标志位
  27. runnable.stop();
  28. }
  29. }
  30. }
  31. }

getState()

线程的六种基本状态:

新建状态(New) 即用new关键字新建一个线程,这个线程就处于新建状态

就绪状态(Runnable) 操作系统中的就绪和运行两种状态,在Java中统称为RUNNABLE。

等待状态(WAITING) 进入该状态表示当前线程需要等待其他线程做出一些的特定的动作(通知或中断)。

超时等待状态(TIMED_WAITING) 区别于WAITING,它可以在指定的时间自行返回。

阻塞状态(Blocked) 阻塞状态表示线程正等待监视器锁,而陷入的状态。

消亡状态(Dead)线程的终止,表示线程已经执行完毕。

使用getState()方法能够获得线程当前所处的状态

  1. public class ThreadState {
  2. public static void main(String[] args) throws InterruptedException {
  3. //使用lambda表达式,创建Thread实例,重写run()方法
  4. Thread thread = new Thread(() -> {
  5. for (int i = 0; i < 50; i++) {
  6. if (i % 5 == 0) {
  7. try {
  8. Thread.sleep(100); //3.TIMED_WAITING
  9. } catch (InterruptedException e) {
  10. e.printStackTrace();
  11. }
  12. }
  13. }
  14. System.out.println("---------------------");
  15. //4.TERMINATED
  16. });
  17. System.out.println("thread.getState() = " + thread.getState()); //1.NEW
  18. thread.start(); //2.RUNNABLE
  19. while (thread.getState() != Thread.State.TERMINATED) {
  20. System.out.println("thread.getState() = " + thread.getState());
  21. Thread.sleep(100);
  22. }
  23. }
  24. }

setPriority()

使用setPriority()方法来设置线程的优先级

setPriority()方法的参数为可以为Thread的枚举类型属性, 或是1~10的整数, 数字越大优先级越高

  1. public class ThreadPriority {
  2. public static void main(String[] args) {
  3. //主线程默认优先级
  4. System.out.println(Thread.currentThread().getName()+" --> "+ Thread.currentThread().getPriority());
  5. MyPriority myPriority = new MyPriority();
  6. Thread t1 = new Thread(myPriority, "t1");
  7. Thread t2 = new Thread(myPriority, "t2");
  8. Thread t3 = new Thread(myPriority, "t3");
  9. Thread t4 = new Thread(myPriority, "t4");
  10. //先设置优先级再启动
  11. t1.start();
  12. t2.setPriority(1);
  13. t2.start();
  14. t3.setPriority(4);
  15. t3.start();
  16. t4.setPriority(Thread.MAX_PRIORITY);
  17. t4.start();
  18. }
  19. }
  20. class MyPriority implements Runnable {
  21. @Override
  22. public void run() {
  23. //执行体打印当前线程名和优先级
  24. System.out.println(Thread.currentThread().getName()+" --> "+ Thread.currentThread().getPriority());
  25. }
  26. }

join()

join()调用合并线程,其他线程阻塞, 待调用join的线程执行完毕后,再执行其他线程,类似插队

  1. public class ThreadJoin implements Runnable {
  2. public static void main(String[] args) throws InterruptedException {
  3. ThreadJoin threadJoin = new ThreadJoin();
  4. Thread thread = new Thread(threadJoin, "分线程");
  5. thread.start();
  6. for (int i = 0; i < 200; i++) {
  7. //main线程的i=100时,分线程插入,main线程阻塞
  8. if (i == 100) thread.join();
  9. //分线程完全结束后,main线程继续执行
  10. System.out.println("main is running..." + i);
  11. }
  12. }
  13. @Override
  14. public void run() {
  15. for (int i = 0; i < 50; i++) {
  16. System.out.println(Thread.currentThread().getName() + " is running..." + i);
  17. }
  18. }
  19. }

yield()

yield()使得线程礼让,让运行状态的线程重新进入就绪状态, 等待CPU的调度

  1. public class ThreadYield {
  2. public static void main(String[] args) {
  3. MyYield myYield = new MyYield();
  4. new Thread(myYield,"Apple").start();
  5. new Thread(myYield,"Google").start();
  6. }
  7. }
  8. class MyYield implements Runnable {
  9. @Override
  10. //首次运行时从头开始执行run()
  11. public void run() {
  12. System.out.println(Thread.currentThread().getName()+"线程开始执行。。。");
  13. //线程礼让, 暂停线程, 重新进入就绪状态等待调度
  14. Thread.yield();
  15. //礼让后继续执行yield()语句后的代码
  16. System.out.println(Thread.currentThread().getName()+"线程停止执行。。。");
  17. }
  18. }

setDaemon()

线程分为用户线程(如main…)和守护线程(如垃圾回收机制gc…)

虚拟机不必等待守护线程结束才结束进程

  1. public class ThreadDaemon {
  2. public static void main(String[] args) {
  3. God god = new God();
  4. People people = new People();
  5. Thread thread_god = new Thread(god);
  6. thread_god.setDaemon(true);//通过setDaemon()方法将thread_god线程设为守护线程
  7. Thread thread_people = new Thread(people);
  8. thread_god.start();
  9. thread_people.start();
  10. }
  11. }
  12. class God implements Runnable {
  13. @Override
  14. public void run() {
  15. //发现死循环并不会一直执行,到用户线程结束即结束
  16. while (true) {
  17. try {
  18. Thread.sleep(100);
  19. } catch (InterruptedException e) {
  20. e.printStackTrace();
  21. }
  22. System.out.println("god bless you!");
  23. }
  24. }
  25. }
  26. class People implements Runnable {
  27. @Override
  28. public void run() {
  29. for (int i = 0; i < 100; i++) {
  30. try {
  31. Thread.sleep(100);
  32. } catch (InterruptedException e) {
  33. e.printStackTrace();
  34. }
  35. System.out.println("I am happy!");
  36. }
  37. System.out.println("Goodbye World!");
  38. }
  39. }

3.线程池

创建线程池使用Executors.newFixedThreadPool(nThread) 静态工厂方法,参数为池子大小

  1. public class ThreadPool {
  2. public static void main(String[] args) {
  3. //1.创建服务,创建线程池
  4. // Executors.newFixedThreadPool() 静态工厂方法,参数为池子大小
  5. ExecutorService service = Executors.newFixedThreadPool(10);
  6. //2.将线程放入线程池
  7. service.execute(new MyRunnable());
  8. service.execute(new MyRunnable());
  9. service.execute(new MyRunnable());
  10. service.execute(new MyRunnable());
  11. //3.关闭线程池
  12. service.shutdown();
  13. }
  14. }
  15. class MyRunnable implements Runnable {
  16. @Override
  17. public void run() {
  18. System.out.println(Thread.currentThread().getName() + " is running...");
  19. }
  20. }

4.锁

在并发编程中存在线程安全问题,主要原因有:1.存在共享数据 2.多线程共同操作共享数据。

当多线程同时操作同一个对象, 会导致数据紊乱, 线程不安全, 故引入

为了解决这些问题, 引入了SynchronizedReentrantLock两种方式

Synchronized

语法为: synchronized (需要上锁的对象) { 代码块 }

  1. public class BuyTicket implements Runnable {
  2. //票数
  3. private Integer ticketNum = 20;
  4. @Override
  5. public void run() {
  6. //循环抢票
  7. while (true) {
  8. //引入锁
  9. //使用synchronized锁上ticketNum,使得多个线程不能同时对ticketNum进行操作
  10. //执行完一次代码块中的内容就释放锁
  11. synchronized (ticketNum) {
  12. if (ticketNum <= 0) {
  13. break;
  14. }
  15. System.out.println(Thread.currentThread().getName() + "拿到了第" + (ticketNum--) + "张票");
  16. }
  17. }
  18. }
  19. public static void main(String[] args) {
  20. //创建Runnable对象
  21. BuyTicket buyTicket = new BuyTicket();
  22. //三个线程操作一个Runnable对象
  23. new Thread(buyTicket, "小红").start();
  24. new Thread(buyTicket, "小黄").start();
  25. new Thread(buyTicket, "小蓝").start();
  26. }
  27. }

ReentrantLock

  1. public class LockDemo {
  2. public static void main(String[] args) {
  3. //创建Runnable对象
  4. Ticket ticket = new Ticket();
  5. //三个线程操作一个Runnable对象
  6. new Thread(ticket, "小红").start();
  7. new Thread(ticket, "小黄").start();
  8. new Thread(ticket, "小蓝").start();
  9. }
  10. }
  11. class Ticket implements Runnable {
  12. private int number = 1000;
  13. //定义ReentrantLock类的Lock锁
  14. private ReentrantLock lock = new ReentrantLock();
  15. @Override
  16. public void run() {
  17. while (true) {
  18. try {
  19. //lock()加锁
  20. lock.lock();
  21. //加锁后判断,不能让多个线程同时判断
  22. if (number <= 0) {
  23. System.out.println(Thread.currentThread().getName() + "--> 没票了");
  24. break;
  25. }
  26. System.out.println(Thread.currentThread().getName() + " 拿到了 " + (number--) + " 号票");
  27. //unlock()解锁
  28. lock.unlock();
  29. } catch (Exception e) {
  30. e.printStackTrace();
  31. }
  32. }
  33. }
  34. }

5.死锁

多个线程互相持有对方所需要的资源,相互等待,形成僵持

  1. public class DeadLock {
  2. public static void main(String[] args) {
  3. Thread lea = new Thread(new Girl("Lea", 1));
  4. Thread anna = new Thread(new Girl("Anna", 0));
  5. lea.start();
  6. anna.start();
  7. }
  8. }
  9. //镜子类
  10. class Mirror {
  11. }
  12. //口红类
  13. class LipStick {
  14. }
  15. class Girl implements Runnable {
  16. private String name;
  17. private int choice;
  18. //mirror,lipStick设为static,表示只有一个镜子和口红
  19. static Mirror mirror = new Mirror();
  20. static LipStick lipStick = new LipStick();
  21. //构造器 choice=0或1
  22. public Girl(String name, int choice) {
  23. this.name = name;
  24. this.choice = choice;
  25. }
  26. @Override
  27. public void run() {
  28. makeup();
  29. }
  30. void makeup() {
  31. //choice为0,先获得镜子的锁,再等待拿口红的锁
  32. if (choice == 0) {
  33. synchronized (mirror) {//获得镜子锁
  34. System.out.println(this.name + " 获得了镜子的锁");
  35. try {
  36. Thread.sleep(100);//模拟延时,保证另一个人拿到了另一件物品的锁
  37. } catch (InterruptedException e) {
  38. e.printStackTrace();
  39. }
  40. //锁中拿锁,造成死锁
  41. synchronized (lipStick) {//获得口红锁
  42. System.out.println(this.name + " 获得了口红的锁");
  43. }
  44. }
  45. } else {
  46. synchronized (lipStick) {//获得口红锁
  47. System.out.println(this.name + " 获得了口红的锁");
  48. try {
  49. Thread.sleep(200);//模拟延时,保证另一个人拿到了另一件物品的锁
  50. } catch (InterruptedException e) {
  51. e.printStackTrace();
  52. }
  53. //锁中拿锁,造成死锁
  54. synchronized (mirror) {//获得镜子锁
  55. System.out.println(this.name + " 获得了镜子的锁");
  56. }
  57. }
  58. }
  59. }
  60. }