java多线程基础(一)

悠悠 2022-07-14 13:14 312阅读 0赞

java基础之多线程

1、线程生命周期

Center

2、线程的创建有两种方式

(1)继承Thread类:

  1. /*
  2. * 创建一个子线程,完成1-100之间自然数的输出。同样的,主线程执行同样的操作。
  3. * 创建多线程的第一种方式:继承Thread类。
  4. */
  5. public class TestThread {
  6. public static void main(String[] args) {
  7. //3\创建一个子类对象
  8. SubThread subThread1 = new SubThread();
  9. SubThread subThread2 = new SubThread();
  10. //4\调用线程的start()方法:启动此线程,调用线程的run()方法。
  11. //一个线程只能执行一次start()
  12. subThread1.start();
  13. subThread2.start();
  14. for(int i =1;i<10000;i++){
  15. System.out.println(Thread.currentThread().getName()+":"+i);
  16. }
  17. }
  18. }
  19. //1、继承Thread类
  20. class SubThread extends Thread{
  21. //2、重写run方法,方法类内实现此子线程压迫完成的功能
  22. public void run(){
  23. for(int i =1;i<10000;i++){
  24. System.out.println(Thread.currentThread().getName()+":"+i);
  25. }
  26. }
  27. }
  28. /*
  29. * Thread的chnagyong fangfa
  30. * 1、start():启动线程并执行相应的run方法
  31. * 2、run():子线程要执行的方法,放入run()方法中
  32. * 3、currentThread():静态的,调取当前的线程
  33. * 4、getName():获取此线程的名字
  34. * 5、setName():设置此线程的名字
  35. * 6、yield():调用此方法的线程释放当钱CPU的执行权。
  36. * 7、join():在A线程中调用B线程的join()方法,表示,当执行到此方法,A线程停止执行,直至B线程执行完毕,A线程再接着执行。
  37. * 8、isAlive():判断当前线程是否还存活。
  38. * 9、sleep(long l):显示的让此线程睡眠l毫秒。
  39. * 10、线程间通信:wait()、notify()、notifyAll()
  40. */
  41. public class TestThread1 {
  42. public static void main(String[] args) throws InterruptedException {
  43. //3\创建一个子类对象
  44. SubThread1 subThread1 = new SubThread1();
  45. SubThread1 subThread2 = new SubThread1();
  46. //4\调用线程的start()方法:启动此线程,调用线程的run()方法。
  47. //一个线程只能执行一次start()
  48. subThread1.setName("嘿嘿");
  49. subThread1.start();
  50. subThread1.setPriority(Thread.MAX_PRIORITY);
  51. subThread2.start();
  52. Thread.currentThread().setName("主线程");
  53. for(int i =1;i<10000;i++){
  54. //Thread.currentThread().sleep(1000);
  55. System.out.println(Thread.currentThread().getName()+":"+Thread.currentThread().getPriority()+"---"+i);
  56. /*if(i%10==0){
  57. Thread.currentThread().yield();
  58. }*/
  59. if(i==20){
  60. subThread1.join();
  61. }
  62. }
  63. }
  64. }
  65. //1、继承Thread类
  66. class SubThread1 extends Thread{
  67. //2、重写run方法,方法类内实现此子线程要完成的功能
  68. public void run(){
  69. for(int i =1;i<10000;i++){
  70. System.out.println(Thread.currentThread().getName()+":"+Thread.currentThread().getPriority()+"---"+i);
  71. }
  72. }
  73. }

(2)实现Runnable接口

  1. /*
  2. * 创建线程的第二种方法:通过实现Runnable 接口来实现
  3. * 1、创建一个实现了Runable接口的类
  4. * 2、实现接口的抽象方法
  5. * 3、创建一个Runnable接口实现类的对象
  6. * 4、对象作为形参传递给Thread类的构造器中,创建Thread对象,此对象即为一个线程
  7. * 5、调用Thread类对象的start()方法。
  8. *
  9. * 对比一下继承和实现的方式
  10. * 1、联系:都实现了Runnable接口。(继承中的THread类,底层也是实现了Runnable接口)
  11. * 2、哪种方式比较好?实现的方式优于继承的方式》
  12. * (1)实现的方式避免了java中单继承的局限性(因为一个类继承了Thread之后,就不能再继承其他类了)。
  13. * (2)比如在售票例子中,多线程操作同一份数据,更适合使用实现的方式。
  14. */
  15. class PrintNum implements Runnable{
  16. @Override
  17. public void run() {
  18. for(int i = 0 ; i<=100 ; i++){
  19. if(i%2 == 0){
  20. System.out.println(Thread.currentThread().getName() +"---"+ i);
  21. }
  22. }
  23. }
  24. }
  25. public class TestRunnable{
  26. public static void main(String[] args) {
  27. PrintNum p = new PrintNum();
  28. //要想启动一个多线程,必须调用start()方法。
  29. Thread t1 = new Thread(p);
  30. t1.start();//启动线程执行Thread对象生成时,构造器形参的对象的run方法。
  31. //再创建一个线程
  32. Thread t2 = new Thread(p);
  33. t2.start();
  34. }
  35. }

3、线程间同步问题

  1. /*
  2. * 此程序存在线程的安全问题,打印车票时,会出现重票和错票的问题。
  3. */
  4. public class TestWindow1 {
  5. public static void main(String[] args) {
  6. Window1 window = new Window1();
  7. Thread t1 = new Thread(window);
  8. Thread t2 = new Thread(window);
  9. Thread t3 = new Thread(window);
  10. t1.setName("窗口1");
  11. t2.setName("窗口2");
  12. t3.setName("窗口3");
  13. t1.start();
  14. t2.start();
  15. t3.start();
  16. }
  17. }
  18. class Window1 implements Runnable {
  19. int sum = 100;
  20. @Override
  21. public void run() {
  22. while(true){
  23. if(sum> 0){
  24. try {
  25. Thread.currentThread().sleep(10);
  26. } catch (InterruptedException e) {
  27. // TODO Auto-generated catch block
  28. e.printStackTrace();
  29. }
  30. System.out.println(Thread.currentThread().getName()+"----"+sum);
  31. sum--;
  32. }else if(sum == 0){
  33. System.out.println("这是最后一张票");
  34. sum--;
  35. }else {
  36. break;
  37. }
  38. }
  39. }
  40. }

分别解决多线程继承和实现方式出现的问题:

(1)同步代码块

  1. public class TestWindow3 {
  2. public static void main(String[] args) {
  3. Window3 w1 = new Window3();
  4. Window3 w2 = new Window3();
  5. Window3 w3 = new Window3();
  6. w1.setName("窗口1");
  7. w2.setName("窗口2");
  8. w3.setName("窗口3");
  9. w1.start();
  10. w2.start();
  11. w3.start();
  12. }
  13. }
  14. class Window3 extends Thread{
  15. public static int sum = 100;
  16. static Object obj = new Object();
  17. public void run(){
  18. while(true){
  19. synchronized (obj) {//在本题中,this表示w1、w2、w3。
  20. if (sum > 0) {
  21. try {
  22. Thread.currentThread().sleep(10);
  23. } catch (InterruptedException e) {
  24. // TODO Auto-generated catch block
  25. e.printStackTrace();
  26. }
  27. System.out.println(Thread.currentThread().getName()
  28. + "----" + sum);
  29. sum--;
  30. }else {
  31. break;
  32. }
  33. }
  34. }
  35. }
  36. }
  37. /*
  38. * 此程序存在线程的安全问题,打印车票时,会出现重票和错票的问题。
  39. * 1、存在的原因是什么?
  40. * 由于一个线程在操作共享数据未执行完的情况下,另外的数据参与进来,导致共享数据存在安全问题。
  41. *
  42. * 2、如何解决线程的安全问题?
  43. * 必须让一个操作共享数据完毕以后,其他线程才有机会参与共享数据的操作。
  44. *
  45. * 3、java如何实现线程的安全:线程的同步机制
  46. * 方式一:同步代码块
  47. * synchronized(对象(同步监视器)){
  48. * //需要被同步的代码块(即为操作共享数据的代码)
  49. * }
  50. * (1)共享数据:多个线程共同操作的同一个数据或者变量
  51. * (2)同步监视器(锁):由一个对象来充当。哪个线程获取此监视器,谁就能执行大括号里被同步的代码。
  52. * 注:要求所有的线程必须使用同一把安全锁,才能实现同步。
  53. * 在实现的方式中可以使用this作为同步锁,但是在继承的方式要慎用。
  54. * 方式二:同步方法
  55. * synchronized 还可以放在方法的声明中,表示整个方法为同步方法
  56. * 例如:public synchronized void show(String name){
  57. * ....
  58. * }
  59. * 注: 将操作共享数据的方法声明成synchronized 。即此方法为同步方法,能够保证当其中一个线程执行此方法时,其他线程在外等待,
  60. * 直至此线程执行完此方法。
  61. * 》同步方法的锁:就是当前对象this。
  62. */
  63. public class TestWindow2 {
  64. public static void main(String[] args) {
  65. Window2 window = new Window2();
  66. Thread t1 = new Thread(window);
  67. Thread t2 = new Thread(window);
  68. Thread t3 = new Thread(window);
  69. t1.setName("窗口1");
  70. t2.setName("窗口2");
  71. t3.setName("窗口3");
  72. t1.start();
  73. t2.start();
  74. t3.start();
  75. }
  76. }
  77. class Window2 implements Runnable {
  78. int sum = 100; //共享数据
  79. //Animal obj = new Animal();
  80. @Override
  81. public void run() {
  82. while(true){
  83. synchronized (this) {//在本问题中,this表示当前对象,本题中表示window,只有一个,所以此处可以使用this
  84. if(sum> 0){
  85. try {
  86. Thread.currentThread().sleep(10);
  87. } catch (InterruptedException e) {
  88. // TODO Auto-generated catch block
  89. e.printStackTrace();
  90. }
  91. System.out.println(Thread.currentThread().getName()+"----"+sum);
  92. sum--;
  93. }else if(sum == 0){
  94. System.out.println("这是最后一张票");
  95. sum--;
  96. }else {
  97. break;
  98. }
  99. }
  100. }
  101. }
  102. }

注:如果synchronized (this) {}包含住了while(true)这个代码块,那么控制台就会出现,只有一个线程在执行售票业务的情况。

(2)同步代码方法

注意:在继承Thread类方法中,不能使用synchronized方法,因为锁对象不是唯一的,所以依旧会出现同步问题。

  1. /*
  2. * 此程序存在线程的安全问题,打印车票时,会出现重票和错票的问题。
  3. */
  4. public class TestWindow4 {
  5. public static void main(String[] args) {
  6. Window4 window = new Window4();
  7. Thread t1 = new Thread(window);
  8. Thread t2 = new Thread(window);
  9. Thread t3 = new Thread(window);
  10. t1.setName("窗口1");
  11. t2.setName("窗口2");
  12. t3.setName("窗口3");
  13. t1.start();
  14. t2.start();
  15. t3.start();
  16. }
  17. }
  18. class Window4 implements Runnable {
  19. int sum = 100;
  20. @Override
  21. public void run() {
  22. while(true){
  23. show();
  24. }
  25. }
  26. public synchronized void show(){
  27. if (sum > 0) {
  28. try {
  29. Thread.currentThread().sleep(10);
  30. } catch (InterruptedException e) {
  31. // TODO Auto-generated catch block
  32. e.printStackTrace();
  33. }
  34. System.out.println(Thread.currentThread().getName()
  35. + "----" + sum);
  36. sum--;
  37. }
  38. }
  39. }

4、单例模式(懒汉式)中关于多线程同步的解决问题

  1. /*
  2. * 关于懒汉式的线程安全问题,使用同步机制,对于一般的方法内,使用同步代码块,可以考虑使用this作为同步锁。
  3. * 对于静态方法,使用当前类本身充当锁
  4. */
  5. public class TestSingleTon {
  6. public static void main(String[] args) {
  7. SingleTon s1 = SingleTon.getInstance();
  8. SingleTon s2 = SingleTon.getInstance();
  9. System.out.println(s1 == s2);
  10. }
  11. }
  12. class SingleTon{
  13. private SingleTon(){}
  14. private static SingleTon instance = null;
  15. public static SingleTon getInstance(){
  16. //加上下边这个if判断语句,程序的效率会提高不少
  17. if(instance == null){
  18. synchronized (SingleTon.class) {
  19. if(instance == null)
  20. instance = new SingleTon();
  21. }
  22. }
  23. return instance;
  24. }
  25. }

5、多线程实例:实现两个人往银行账户中存钱

  1. /*
  2. * 实现两个人往银行账户中存钱:方式一:采用继承的方法
  3. */
  4. public class TestAccount {
  5. public static void main(String[] args) {
  6. Account2 account2 = new Account2();
  7. Customer2 a1 = new Customer2(account2);
  8. Customer2 a2 = new Customer2(account2);
  9. a1.setName("账户1");
  10. a2.setName("账户2");
  11. a1.start();
  12. a2.start();
  13. }
  14. }
  15. class Account2{
  16. private double balance;
  17. public Account2(){
  18. }
  19. public synchronized void deposit(double amt){
  20. this.balance += amt;
  21. try {
  22. Thread.currentThread().sleep(100);
  23. } catch (InterruptedException e) {
  24. // TODO Auto-generated catch block
  25. e.printStackTrace();
  26. }
  27. System.out.println(Thread.currentThread().getName()+"操作后,"+this.balance);
  28. }
  29. }
  30. class Customer2 extends Thread{
  31. Account2 account;
  32. public Customer2(Account2 account){
  33. this.account = account;
  34. }
  35. public void run(){
  36. for(int i = 0 ;i<3;i++){
  37. account.deposit(1000);
  38. }
  39. }
  40. }
  41. /*
  42. * 实现两个人往银行账户中存钱:方式二:采用实现的方法
  43. */
  44. public class TestAccount1 {
  45. public static void main(String[] args) {
  46. Account account = new Account();
  47. Customer c = new Customer(account);
  48. Thread t1 = new Thread(c);
  49. Thread t2 = new Thread(c);
  50. t1.start();
  51. t2.start();
  52. }
  53. }
  54. class Customer implements Runnable {
  55. private Account account = null;
  56. public Account getAccount() {
  57. return account;
  58. }
  59. public void setAccount(Account account) {
  60. this.account = account;
  61. }
  62. public Customer() {
  63. super();
  64. }
  65. public Customer(Account account) {
  66. super();
  67. this.account = account;
  68. }
  69. @Override
  70. public void run() {
  71. synchronized (this) {
  72. for (int i = 0; i < 3; i++) {
  73. account.setMoney(1000);
  74. }
  75. }
  76. }
  77. }
  78. class Account {
  79. private int money = 0;
  80. public Account() {
  81. System.out.println("账户现有:" + money);
  82. }
  83. public Account(int money) {
  84. this.money = money;
  85. System.out.println("账户现有:" + this.money);
  86. }
  87. public int getMoney() {
  88. return money;
  89. }
  90. public void setMoney(int money) {
  91. this.money += money;
  92. System.out.println(Thread.currentThread().getName() + "-----" + money);
  93. System.out.println("账户余额是:" + this.money);
  94. }
  95. }

6、实现线程之间的通信(使用wait()、notify()、notifyAll())

  1. /*
  2. * 线程通信:如下的三个关键字使用的话,都得在同步代码块或者同步方法中
  3. * wait():一旦一个线程执行到wait方法,就释放当前的锁。
  4. * notify():唤醒wait()的一个线程
  5. * notifyAll():唤醒wait()的所有线程
  6. */
  7. class PrintNum implements Runnable{
  8. int num = 0;
  9. @Override
  10. public void run() {
  11. while(true){
  12. synchronized (this) {
  13. notify(); //在此处加notify,意思是,第一个线程执行结束,释放锁,第二个线程进来之后,
  14. //首先已经获取锁了,然后再唤醒另一个线程(虽然此时唤醒,但是也只能等到第二个线程释放锁之后,第一个线程才能再次开始执行)。
  15. if(num <= 100){
  16. try {
  17. Thread.currentThread().sleep(10);
  18. } catch (InterruptedException e) {
  19. // TODO Auto-generated catch block
  20. e.printStackTrace();
  21. }
  22. System.out.println(Thread.currentThread().getName() + "-----"
  23. + num);
  24. num++;
  25. }
  26. try {
  27. wait();
  28. } catch (InterruptedException e) {
  29. // TODO Auto-generated catch block
  30. e.printStackTrace();
  31. }
  32. }
  33. }
  34. }
  35. }
  36. public class TestCommunication {
  37. public static void main(String[] args) {
  38. PrintNum printNum = new PrintNum();
  39. Thread t1 = new Thread(printNum);
  40. Thread t2 = new Thread(printNum);
  41. t1.start();
  42. t2.start();
  43. }
  44. }
  45. /*
  46. * 实现两个人往银行账户中存钱:方式一:采用继承的方法
  47. */
  48. public class TestAccount {
  49. public static void main(String[] args) {
  50. Account2 account2 = new Account2();
  51. Customer2 a1 = new Customer2(account2);
  52. Customer2 a2 = new Customer2(account2);
  53. a1.setName("账户1");
  54. a2.setName("账户2");
  55. a1.start();
  56. a2.start();
  57. }
  58. }
  59. class Account2{
  60. private double balance;
  61. public Account2(){
  62. }
  63. public synchronized void deposit(double amt){
  64. notify();
  65. this.balance += amt;
  66. try {
  67. Thread.currentThread().sleep(100);
  68. } catch (InterruptedException e) {
  69. // TODO Auto-generated catch block
  70. e.printStackTrace();
  71. }
  72. System.out.println(Thread.currentThread().getName()+"操作后,"+this.balance);
  73. try {
  74. wait();
  75. } catch (InterruptedException e) {
  76. // TODO Auto-generated catch block
  77. e.printStackTrace();
  78. }
  79. }
  80. }
  81. class Customer2 extends Thread{
  82. Account2 account;
  83. public Customer2(Account2 account){
  84. this.account = account;
  85. }
  86. public void run(){
  87. for(int i = 0 ;i<3;i++){
  88. account.deposit(1000);
  89. }
  90. }
  91. }

7、死锁

  1. /*
  2. * 死锁问题,处理线程同步时,容易出现这个问题
  3. * 不同的线程分别占用对方的同步资源不放弃,都在等待对方放弃自己需要的同步资源,就形成了线程的死锁。
  4. * 如下例:第一个线程在执行的时候,首先握住sb1这个锁,要想释放sb1锁,必须先要执行完里面握住sb2锁的操作执行完毕才可以。但是在这个过程中,另一个线程可能在第一个线程拿到sb2锁,并且第二个线程也需要调用sb1锁才能释放sb2锁,这样就会导致双方都握住对方的锁,但是都不能执行,从而造成死锁。
  5. */
  6. public class TestDeadLock {
  7. static StringBuffer sb1 = new StringBuffer();
  8. static StringBuffer sb2 = new StringBuffer();
  9. public static void main(String[] args) {
  10. new Thread(){
  11. public void run(){
  12. synchronized (sb1) {
  13. try {
  14. Thread.currentThread().sleep(100);
  15. } catch (InterruptedException e) {
  16. // TODO Auto-generated catch block
  17. e.printStackTrace();
  18. }
  19. sb1.append("A");
  20. synchronized (sb2) {
  21. sb2.append("B");
  22. }
  23. }
  24. System.out.println(sb1);
  25. System.out.println(sb2);
  26. }
  27. }.start();
  28. new Thread(){
  29. public void run(){
  30. synchronized (sb2) {
  31. try {
  32. Thread.currentThread().sleep(100);
  33. } catch (InterruptedException e) {
  34. // TODO Auto-generated catch block
  35. e.printStackTrace();
  36. }
  37. sb2.append("C");
  38. synchronized (sb1) {
  39. sb1.append("D");
  40. }
  41. }
  42. System.out.println(sb1);
  43. System.out.println(sb2);
  44. }
  45. }.start();
  46. }
  47. }

8、生产者消费者例子

  1. /*
  2. * 生产者消费者模式
  3. * 1、是否涉及多线程;生产者、消费者
  4. * 2、是否涉及到共享数据?有!
  5. * 3、此共享数据是谁?即为产品数量
  6. * 4、是否涉及到线程的通信?存在着生产者与消费者的通信
  7. */
  8. class Clerk{
  9. int produtor;
  10. public synchronized void addProdutor(){
  11. if(produtor>=20){
  12. try {
  13. wait();
  14. } catch (InterruptedException e) {
  15. // TODO Auto-generated catch block
  16. e.printStackTrace();
  17. }
  18. }else {
  19. produtor++;
  20. System.out.println(Thread.currentThread().getName()+"生产了第:"+produtor+"个产品");
  21. notifyAll();
  22. }
  23. }
  24. public synchronized void consumeProductor(){
  25. if(produtor<=0){
  26. try {
  27. wait();
  28. } catch (InterruptedException e) {
  29. // TODO Auto-generated catch block
  30. e.printStackTrace();
  31. }
  32. }else {
  33. produtor--;
  34. System.out.println(Thread.currentThread().getName()+"消费了第:"+produtor+"个产品");
  35. notifyAll();
  36. }
  37. }
  38. }
  39. class Producer implements Runnable{
  40. Clerk clerk;
  41. public Producer(Clerk clerk) {
  42. this.clerk = clerk;
  43. }
  44. @Override
  45. public void run() {
  46. System.out.println("生产者开始生产产品");
  47. while(true){
  48. try {
  49. Thread.currentThread().sleep(100);
  50. } catch (InterruptedException e) {
  51. // TODO Auto-generated catch block
  52. e.printStackTrace();
  53. }
  54. clerk.addProdutor();
  55. }
  56. }
  57. }
  58. class Consumer implements Runnable{
  59. Clerk clerk;
  60. public Consumer(Clerk clerk) {
  61. this.clerk = clerk;
  62. }
  63. @Override
  64. public void run() {
  65. while(true){
  66. try {
  67. Thread.currentThread().sleep(100);
  68. } catch (InterruptedException e) {
  69. // TODO Auto-generated catch block
  70. e.printStackTrace();
  71. }
  72. clerk.consumeProductor();
  73. }
  74. }
  75. }
  76. public class TestProduceAndCutomer {
  77. public static void main(String[] args) {
  78. Clerk clerk = new Clerk();
  79. Producer p = new Producer(clerk);
  80. Consumer c = new Consumer(clerk);
  81. Thread t1 = new Thread(p);
  82. Thread t2 = new Thread(c);
  83. Thread t3 = new Thread(p);
  84. t1.start();
  85. t2.start();
  86. t3.start();
  87. }
  88. }

发表评论

表情:
评论列表 (有 0 条评论,312人围观)

还没有评论,来说两句吧...

相关阅读

    相关 线 —— 基础概念

    > 一、进程、线程与协程 进程 程序由指令和数据组成,但这些指令要运行,数据要读写,就必须将指令加载至 CPU,数据加载至内存。在指令运行过程中还需要用到磁盘、网

    相关 () 线基础

    1.进程和线程区别 多进程:在操作系统中能同时运行多个任务(程序)。如电脑: 微信和网易云音乐同时运行 多线程:在同一应用程序中有多个顺序流在执行。如网易云音乐:音乐

    相关 java线基础

    线程和进程: 进程:资源(cpu,内存,磁盘)分配的最小单位 线程:归属于某个进程,cpu调度的最小单元(栈、程序计数器),堆 cpu轮转机制(RR调度):主要用于分