黑马程序员--多线程总结1--java

------- <a href="http://www.itheima.com" target="blank">android培训</a>、<a href="http://www.itheima.com" target="blank">java培训</a>、期待与您交流! ----------

多线程总结1

01、线程

1、概述

     宏观来讲

     进程:就是正在运行的程序,是系统进行资源分配和调用的独立单位。每一个进程都有它自己的内存空间和系统资源。

     线程:就是进程的执行路径,执行单元

一个进程如果只有一条执行路径,则称为单线程程序。

一个进程如果有多条执行路径,则称为多线程程序。

     举例:

   jvm虚拟机的启动是多线程的。原因是垃圾回收线程也要先启动,否则很容易会出现内存溢出。现在的垃圾回收线程加上前面的主线程,最低启动了两个线程,所以,jvm的启动其实是多线程的。

    并行和并发。

前者是逻辑上同时发生,指在某一个时间内同时运行多个程序。

后者是物理上同时发生,指在某一个时间点同时运行多个程序。

2、创建一个线程的两种方式

     1、定义一个类继承Thread类

<pre name="code" class="java">       publicclass A extends Thread{          
       }
       newA().start();
 

     2、定义一个类实现Runnable接口,并且重写run()方法

          publicclass A implements Runnable{
              @Override
              public void run(){
                   
              }
          }
          newThread(new A()).start();
 

3、线程的随机性原理

     多个程序实际是通过CPU在做高效切换实现的

 

4、线程的声明周期

     新建 --> 就绪 --> 运行 -->阻塞--> 死亡

     这里要注意,线程阻塞后就无法执行,回到就绪状态

02、案例

问题代码:

public class TicketRunnable implements Runnable{
       private int tickets = 100;
       @Override
       public void run() {
              while(true){
                     if(tickets > 0){
                            try {
                                   Thread.sleep(1000);//必须加这个,否则不一定出现负数-----语句1
                            } catch (InterruptedException e) {
                                   e.printStackTrace();
                            }
                            System.out.println(Thread.currentThread().getName()+"正在卖第"+tickets--+"张的票");---语句2
                     }else{
                            break;//必须加这个,否则无法跳出循环,造成死机
                     }
              }
       }
}<strong>
</strong>
</pre><pre name="code" class="java">测试代码:

public class TicketDemo {
       public static void main(String[] args) {
              TicketRunnable runnable = new TicketRunnable();
             
             Thread t1 = new Thread(runnable, "窗口1");
             Thread t2 = new Thread(runnable, "窗口2");
             Thread t3 = new Thread(runnable, "窗口3");
             
             t1.start();
             t2.start();
             t3.start();
       }
}

2、产生问题的原因

              比如现在只剩一张票了tickets=1,现在有两个线程,线程1和线程2

              线程1先执行,判断tickets >0,执行线程1的语句1,然后被休眠1s

              在这个时候线程2抢到了执行权,首先判断tickets>0,继续往下走,执行线程2的语句1,然后被休眠1秒

              在线程2休眠的时候,线程1醒了,执行语句1,然后线程1停止,这时候tickets=0

              线程2醒了,执行语句2,这时候tickets=-1

 

 

3、如何查找问题

     1、看有没有共享数据

     2、看操作共享数据的语句是不是多条语句

     3、看是不是在多线程的环境中

 

     最后,把操作共享数据的多条语句用 锁起来

4、改良后的代码

修改后的代码
public class TicketRunnable implements Runnable {
       private int tickets = 50;
       private Object lock = new Object();
       @Override
       public void run() {
              while (true) {
 
                     synchronized (lock) {
                            if (tickets > 0) {
                                   try {
                                          Thread.sleep(100);
                                   } catch (InterruptedException e) {
                                          e.printStackTrace();
                                   }
                                   System.out.println(Thread.currentThread().getName()+ "正在卖第" + tickets-- + "张的票");
                            } else {
                                   break;
                            }
                     }
              }
       }
}

不加static修饰完全可以,但是在使用的时候需要注意创建多个线程时,传入的必须是同一个Runnable对象的实例,因为每个TicketThread对象中的obj的值是不一样的。这样锁就不一样,所以代码只能这么写:

TicketThread runnable = new TicketThread();
Thread t1 = new Thread(runnable);
Thread t2 = new Thread(runnable);
       
t1.start();
t2.start();

要是加static的话,即使创建多个TicketThread对象,每个对象共享obj这个变量,锁就是一样的,代码还可以这么写

TicketThread runnable1 = new TicketThread();
TicketThread runnable2 = new TicketThread();
         
Thread t1 = new Thread(runnable1);
Thread t2 = new Thread(runnable2);
       
t1.start();
t2.start();

03、锁,同步代码块,同步方法

1、同步代码块

     synchronized(锁对象){
          需要被锁的代码//线程只有拿到了锁对象,才能执行这里的代码!!!换言之,这里的代码如果执行了,说明该线程拿到了锁对象,其他线程不能拿到该锁对象
     }

     注意

          多个线程必须使用同一个锁对象,要不然锁无效

 

2、同步方法

     public synchronized void show(){}              //普通方法的锁是this
     public staticsynchronized void show(){}    //静态方法的锁是当前类的字节码文件对象 类名.class


3、注意问题

     多个线程必须使用同一个锁对象,要不然锁无效

     同步代码块锁可以是任意对象

     同步方法的锁是this

     静态方法的锁是当前类的字节码文件对象 类名.class 

 

4、什么时候用同步代码块,什么时候用同步方法

     尽可能用同步代码块

     如果一个方法内的所有代码都被同步代码块包住了,那就用同步方法就可以了


04、死锁

死锁原因总结

     线程1自身拿着一个锁:A锁,线程2自身拿着一个锁:B锁

     当线程1要用B锁,线程B要用A锁的时候就会发生死锁

线程一:

public class Thread1 extends Thread {
       @Override
       public void run() {
              synchronized (Lock.LOCK_A) {
                     System.out.println("我是线程1,已经拿到A锁,将要去哪B锁");
                     synchronized (Lock.LOCK_B) {
                            System.out.println("我是线程1,成功拿到B锁");
                     }
              }
       }
}
</pre><pre name="code" class="java">线程二:
public class Thread2 extends Thread {
       @Override
       public void run() {
              synchronized (Lock.LOCK_B) {
                     System.out.println("我是线程2,已经拿到B锁,将要去哪A锁");
                     synchronized (Lock.LOCK_A) {
                            System.out.println("我是线程2,成功拿到A锁");
                     }
              }
       }
}


锁对象:
public class Lock {
       public static final  Object LOCK_A = new Object();
       public static final  Object LOCK_B = new Object();
}

测试代码:
public class Test {
       public static void main(String[] args) {
              Thread1 t1= new Thread1();
              Thread2 t2= new Thread2();
              t1.start();
              t2.start();
       }
}


05、多个线程操作同一数据的问题(线程键通讯问题)

线程键通讯:

     其实就是多个线程同时操作同一个对象

     卖票案例就是线程间通讯问题

共享数据(学生类):
public class Student {
       public String name;
       public int age;
 
       @Override
       public String toString() {
              return "Student [name=" + name + ", age=" + age + "]";
       }
}
线程1:负责修改共享数据
package tongxin;
 
public class SetThread  extends Thread{
       private Student stu;
       public SetThread(Student stu){
              this.stu = stu;
       }
      
      
       @Override
       public void run() {
              int i=0;
              while(true){
                     if(i%2 ==0){//执行%2操作,是为了写入不同的数据,测试在写入过程中,是否影响另一个线程的读取操作
                            stu.name = "张三";
                            stu.age = 13;
                     }else{
                            stu.name = "李四";
                            stu.age = 14;
                     }
                     i++;
              }
       }
}
 
线程2:负责获取共享数据信息
package tongxin;
 
public class GetThread extends Thread {
       private Student stu;
       public GetThread(Student stu){
              this.stu = stu;
       }
       @Override
       public void run() {
              while(true){
                     System.out.println(stu);
              }
       }
}
 
测试代码

 
public class Test {
       public static void main(String[] args) {
              //创建共享数据
              Student stu = new Student();
              //创建两个线程,并且让这两个线程同时操作这个共享数据
              GetThread get = new GetThread(stu);
              SetThread set = new SetThread(stu);
             
              get.start();
              set.start();
       }
}
 

1、问题产生原理分析

     注意:每个线程有个普通的成员变量Student,创建线程的时候需要先初始化该变量

 

2、解决方案

     1、看有没有共享数据

     2、看操作共享数据的语句是不是多条语句

     3、看是不是在多线程的环境中

 

     最后,把操作共享数据的多条语句用 锁起来

 

所以给两个线程while(true){}中间的内容用加锁

线程1:负责修改共享数据
public class SetThread extends Thread {
       private Student stu;
 
       public SetThread(Student stu) {
              this.stu = stu;
       }
 
       @Override
       public void run() {
              int i = 0;
 
              while (true) {
                     synchronized (stu) {
                            if (i % 2 == 0) {// 执行%2操作,是为了写入不同的数据,测试在写入过程中,是否影响另一个线程的读取操作
                                   stu.name = "张三";
                                   stu.age = 13;
                            } else {
                                   stu.name = "李四";
                                   stu.age = 14;
                            }
                            i++;
                     }
              }
 
       }
}
线程2:负责获取共享数据信息
public class GetThread extends Thread {
       private Student stu;
       public GetThread(Student stu){
              this.stu = stu;
       }
       @Override
       public void run() {
              while(true){
                     synchronized (stu) {
                            System.out.println(stu);
                     }
              }
       }
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值