• 黑马程序员——java学习6(127-151)——多线程


    1、多线程

    进程:是一个正在执行中的程序。

      每一个进程执行都有一个执行顺序,该顺序是一个执行路径,或者叫控制单元。

    线程:就是进程中的一个独立的控制单元

      线程在控制着进程的执行

    一个进程中至少有一个线程。

    Java VM 启动的时候回有一个进程java.exe

    该进程中至少一个线程负责java程序的执行

    而且这个线程运行的代码存在于main方法中

    该线程称之为主线程。

    扩展:其实更细节说明jvm,启动的还有垃圾回收机制的线程

     创建线程的方式

    1.1继承Thread

    (1)定义类,继承Thread

    (2)覆写Thread类中的run方法

      目的:将自定义的代码存储在run方法中,让线程运行。

    (3)调用线程的start方法,

       作用:启动线程,调用run方法.

    多线程的随机性:每次运行结果不同,单核CPU在某一个时刻只能执行一个程序,cpu在快速切换,以达到看上去同时运行的效果。

    1.1.1为何覆盖run方法?

    Thread用于描述线程,该类就定义了一个功能,用于存储线程要运行的代码,该存储功能就是run方法。

    即run用于存储线程要运行的代码。

     1 class Demo extends Thread{
     2     public void run(){
     3         for(int x=0;x<60;x++)
     4         {
     5             System.out.println("demo run---"+x);
     6         }
     7     };
     8 }
     9 public class ThreadDemo {
    10     public static void main(String[] args) {
    11         Demo d=new Demo();
    12         //d.start();//开启线程并执行该线程的run方法,demorun与helloworld相互执行
    13         d.run();//仅仅是对象调用方法,而线程创建了,并没有运行,demorun全部执行完才执行helloworld
    14         for(int x=0;x<60;x++)
    15         {
    16             System.out.println("Helloworld---"+x);
    17         }
    18 }
    19 }

    1.1.2、线程方法

    static Thread currentThread();获取当前线程对象,静态的,调用时为Thread.currentThread();

    getName();获取线程名称

    设置线程名称:setName();或者构造函数

    1.2、创建线程的第二种方法(常用)

    (1)定义类实现Runnable接口

    (2)覆盖Runnable接口中的run方法

      将线程要运行的代码存放在该run方法中

    (3)通过Thread类建立线程对象

    (4)将Runnable接口的子类对象作为实际参数传递给Thread类的构造函数

      为何将Runnable接口的子类对象传递

      因为自定义的run方法所述的对象是Runnable接口的子类对象

      所以要让线程去执行指定对象的run方法,就必须明确该run所属对象

    (5)调用Thread类的start方法开启线程并调用Runnable接口子类的run方法

    好处:避免单继承的局限性

    区别:

    继承Runnable:线程代码存放在Thread子类的run方法中,无父类时用

    实现Runnable:线程代码存在接口的子类的run方法中,避免局限

     1 /*
     2  * 需求:
     3  * 银行有一个金库
     4  * 有两个储户分别存300元,每次存100,分3次
     5  * 
     6  * 目的:该程序是否有安全问题,如果有,如何解决?
     7  * 
     8  * 如何找问题
     9  * 1、明确哪些代码是多线程运行代码
    10  * 2、明确共享数据
    11  * 3、明确多线程运行代码中哪些语句是操作共享数据的
    12  * */
    13 
    14 package learn;
    15 class Bank
    16 {
    17     private int sum;
    18     public void add(int n)
    19     {
    20         sum=sum+n;
    21         try{Thread.sleep(10);}catch(Exception e){}
    22         System.out.println("sum"+sum);
    23     }
    24 }
    25 class Cus implements Runnable
    26 {
    27     private Bank b=new Bank();
    28     public void run()
    29     {
    30         for(int x=0;x<3;x++)
    31         {
    32             b.add(100);
    33         }
    34     }
    35 }
    36 public class BankDemo {
    37     public static void main(String[] args) {
    38         Cus c=new Cus();
    39         Thread t1=new Thread(c);
    40         Thread t2=new Thread(c);
    41         t1.start();
    42         t2.start();
    43     }
    44 }

    1.3、安全问题

    当多条语句操作同一个线程共享数据时,一个线程对多条语句只执行了一部分,还没有执行完,

    另一个线程参与执行,导致共享数据的错误

    解决办法:

      对多条操作共享数据的语句,只能让一个线程都执行完。在执行过程中其他线程不可参与执行

    同步代码块。

    synchronized(对象)

    {

      需要被同步的代码  

    }

    例:火车上的厕所

    同步的前提:

    (1)必须有两个或以上的线程

    (2)必须多个线程使用同一个锁

    PS:必须保证同步中只有一个线程在执行

    好处:解决了多线程的安全问题

    弊端:判断锁,消耗资源

    同步的方式:

    (1)同步代码块,使用的锁是obj

    (2)同步函数(作修饰符加入),使用的锁是this

    (3)静态方法用过的锁是class,即该方法所在类的字节码文件对象,函数列表中写作(类名.class)

    1.4、单例

     1 package learn;
     2 /*
     3  * class Single
     4  * {
     5  *     private static final Single s=new Single();
     6  *  private Single(){};
     7  *  public static Single getInstance()
     8  *  {
     9  *      return s;
    10  *  }
    11  * }
    12  * */延迟加载的单例模式示例
    13 class Single
    14 {
    15     private static Single s=null;
    16     private Single(){}
    17     public static Single getInstance(){
    18         if(s==null)
    19             synchronized(Single.class){
    20                 if(s==null)
    21                     s=new Single();
    22             }
    23         return s;
    24     }
    25 }
    26 public class SingleDemo {
    27 
    28 }

    1.5、死锁

    同步中嵌套同步

     1 package learn;
     2 class Test implements Runnable
     3 {
     4     private boolean flag;
     5     Test(boolean flag)
     6     {
     7         this.flag=flag;
     8     }
     9     public void run(){
    10         if(flag)
    11         {
    12             synchronized(MyLock.locka)
    13             {
    14                 System.out.println("if locka ");
    15                 synchronized(MyLock.lockb)
    16                 {
    17                     System.out.println("if lockb");
    18                 }
    19             }
    20         }
    21         else
    22         {
    23             synchronized(MyLock.lockb)
    24             {
    25                 System.out.println("else lockb");
    26                 synchronized(MyLock.locka)
    27                 {
    28                     System.out.println("else locka");
    29                 }
    30             }
    31         }
    32     }
    33 }
    34 
    35 class MyLock{
    36     static Object locka=new Object();
    37     static Object lockb=new Object();
    38 }
    39 public class DeadLockTicket {
    40     public static void main(String[] args) {
    41         Thread t1=new Thread(new Test(true));
    42         Thread t2=new Thread(new Test(false));
    43         t1.start();
    44         t2.start();
    45     }
    46 }

    1.6、线程间通信

    1.6.1等待唤醒机制

    wait都存放在线程池,notify通常唤醒第一个被等待的,notifyall唤醒全部,三者都必须用在同步中(因须有锁),也需加前缀obj.wait

    只有同一个锁上的被等待线程,可以被同一个锁上的notify唤醒

    而锁是任意对象,所以可以被任意对象调用的方法定义在object类中

     1 package learn;
     2 class Res{
     3     private String name;
     4     private String sex;
     5     private boolean flag=false;
     6     public synchronized void set(String name,String sex)
     7     {
     8         if(flag)
     9             try{this.wait();}catch(Exception e){}
    10         this.name=name;
    11         this.sex=sex;
    12         flag=true;
    13         this.notify();
    14     }
    15     public synchronized void out()
    16     {
    17         if(!flag)
    18             try{this.wait();}catch(Exception e){}
    19         System.out.println(name+"..."+sex);
    20         flag=false;
    21         this.notify();
    22     }
    23 }
    24 
    25 class Input implements Runnable{
    26     private Res r;
    27     Input(Res r)
    28     {
    29         this.r=r;
    30     }
    31     public void run()
    32     {
    33         int x=0;
    34         while(true)
    35         {
    36             if(x==0)        
    37                 r.set("mike", "nan");            
    38             else            
    39                 r.set("lili", "nv");
    40             x=(x+1)%2; 
    41         }
    42     }
    43 
    44 }
    45 
    46 class Output implements Runnable{
    47     private Res r;
    48     Output(Res r)
    49     {
    50         this.r=r;
    51     } 
    52     public void run()
    53     {
    54         int x=0;
    55         while(true)
    56         {
    57             r.out();
    58         }
    59     }
    60 }
    61 public class InputOutputDemo {
    62     public static void main(String[] args) {
    63         Res r=new Res();
    64         //匿名对象
    65         new Thread(new Input(r)).start();
    66         new Thread(new Output(r)).start();
    67         
    68 //        Input in=new Input(r);
    69 //        Output out =new Output(r);
    70 //        Thread t1=new Thread(in);
    71 //        Thread t2=new Thread(out);
    72 //        t1.start();
    73 //        t2.start();
    74     }
    75 }

    1.6.2、生产者消费者例子

    多个生产者,多个消费者,while循环判断标记,notifyAll

     1 package pack;
     2 class Resource{
     3     private String name;
     4     private int count=1;
     5     private boolean flag=false;
     6     
     7     public  synchronized void set(String name)
     8     {
     9         while(flag)
    10             try{wait();}catch(Exception e){}
    11         this.name=name+"..."+count++;
    12         System.out.println(Thread.currentThread().getName()+"生产者"+this.name);
    13         flag=true;
    14         this.notifyAll();
    15     }
    16     public synchronized void out(){
    17         while(!flag)
    18             try{wait();}catch(Exception e){}
    19         System.out.println(Thread.currentThread().getName()+"消费者............"+this.name);
    20         flag=false;
    21         this.notifyAll();
    22     }
    23 }
    24 
    25 class Producer implements Runnable{
    26     private Resource res;
    27     Producer(Resource res){
    28         this.res=res;
    29     }
    30     public void run(){
    31         while(true)
    32         {
    33             res.set("+商品+");
    34         }
    35     }
    36 }
    37 
    38 class Consumer implements Runnable{
    39     private Resource res;
    40     Consumer(Resource res){
    41         this.res=res;
    42     }
    43     public void run(){
    44         while(true)
    45         {
    46             res.out();
    47         }
    48     }
    49 }
    50 public class ProduceConsumerDemo {
    51     public static void main(String[] args) {
    52         Resource r=new Resource();
    53         Producer pro=new Producer(r);
    54         Consumer con=new Consumer(r);
    55         
    56         Thread t1=new Thread(pro);
    57         Thread t2=new Thread(pro);
    58         Thread t3=new Thread(con);
    59         Thread t4=new Thread(con);
    60         t1.start();
    61         t2.start();
    62         t3.start();
    63         t4.start();
    64     }
    65 }

    1.6.3、多线程升级锁Lock

    JDK1.5中提供了多线程升级解决方案

    将同步syn替换成现实Lock操作

    将Object中的wait,notify,notifyAll,替换了Condition对象

    该对象可以Lock锁,进行获取

    实现了本方只唤醒对方操作

     1 package learn;
     2 
     3 import java.util.concurrent.locks.Condition;
     4 import java.util.concurrent.locks.Lock;
     5 import java.util.concurrent.locks.ReentrantLock;
     6 
     7 class Resource{
     8     private String name;
     9     private int count=1;
    10     private boolean flag=false;
    11     private Lock lock=new ReentrantLock();
    12     private Condition condition_pro=lock.newCondition();
    13     private Condition condition_con=lock.newCondition();
    14     public  void set(String name)throws InterruptedException
    15     {
    16         lock.lock();
    17         try{    
    18             while(flag)
    19             condition_pro.await();
    20             this.name=name+"..."+count++;
    21             System.out.println(Thread.currentThread().getName()+"生产者"+this.name);
    22             flag=true;    
    23             condition_con.signal();
    24         }
    25         finally{
    26             lock.unlock();
    27         }
    28         
    29     }
    30     public void out()throws InterruptedException{
    31         lock.lock();
    32         try{
    33         while(!flag)
    34             condition_con.await();
    35         System.out.println(Thread.currentThread().getName()+"消费者............"+this.name);
    36         flag=false;
    37         condition_pro.signal();
    38         }
    39         
    40         finally
    41         {
    42             lock.unlock();
    43         }
    44     }
    45 }
    46 
    47 class Producer implements Runnable{
    48     private Resource res;
    49     Producer(Resource res){
    50         this.res=res;
    51     }
    52     public void run(){
    53         try{
    54         while(true)
    55         {
    56             res.set("+商品+");
    57         }
    58         }
    59         catch(Exception e)
    60         {}
    61     }
    62 }
    63 
    64 class Consumer implements Runnable{
    65     private Resource res;
    66     Consumer(Resource res){
    67         this.res=res;
    68     }
    69     public void run(){
    70         while(true)
    71         {
    72             try{
    73             res.out();
    74             }
    75             catch(Exception e)
    76             {}
    77         }
    78     }
    79 }
    80 public class ProducerConsumerDemo2 {
    81     public static void main(String[] args) {
    82         Resource r=new Resource();
    83         Producer pro=new Producer(r);
    84         Consumer con=new Consumer(r);
    85         
    86         Thread t1=new Thread(pro);
    87         Thread t2=new Thread(pro);
    88         Thread t3=new Thread(con);
    89         Thread t4=new Thread(con);
    90         t1.start();
    91         t2.start();
    92         t3.start();
    93         t4.start();
    94     }
    95 }

    1.7、如何停止线程?

    stop方法已过时,只能让run方法结束,即控制run中的循环结束

    特殊情况:

    当线程处于冻结状态

    就不会读取到标记,那么线程就不会结束

    当没有指定的方式让冻结的线程回复到运行状态时,这时需要对冻结进行清除

    强制让线程回复到运行状态中来,这样就可以操作标记让线程结束

    interrput();方法

    1.8、守护线程

    1.9、join

    当A线程执行到B线程的join方法时,A就会等到B执行完再执行

     1 package pack;
     2 class Demo implements Runnable
     3 {
     4     public void run(){
     5         for(int x=0;x<70;x++)
     6             System.out.println(Thread.currentThread().getName()+"..."+x);
     7     }
     8 }
     9 public class JoinDemo {
    10  public static void main(String[] args) throws InterruptedException {
    11     Demo d=new Demo();
    12     Thread t1=new Thread(d);
    13     Thread t2=new Thread(d);
    14     t1.start();
    15     //t1抢夺CPU执行权
    16     t1.join();
    17     t2.start();
    18     
    19     for(int x=0;x<80;x++)
    20     {
    21         System.out.println("main..."+x);
    22     }
    23     System.out.println("over");
    24 }
    25 }

    1.9.1、线程优先级

    所有线程默认优先级5,一共1-10级

    1.9.2、yield();

    尽量轮流执行

    1.9.3、开发举例

     1 package pack;
     2 
     3 public class ThreadTest {
     4     public static void main(String[] args) {
     5         //匿名内部类更方便
     6         new Thread()
     7         {
     8             public void run()
     9             {
    10                 for(int x=0;x<100;x++)
    11                 {
    12                     System.out.println(Thread.currentThread().getName()+"..."+x);
    13                 }
    14             }
    15         }.start();
    16         
    17         for(int x=0;x<100;x++)
    18         {
    19             System.out.println(Thread.currentThread().getName()+"..."+x);
    20         }
    21             
    22         
    23         Runnable r=new Runnable(){
    24             public void run(){
    25                 for(int x=0;x<100;x++)
    26                 {
    27                     System.out.println(Thread.currentThread().getName()+"..."+x);
    28                 }
    29                 
    30             }    
    31         };
    32         new Thread(r).start();
    33     }
    34 }
    35 
    36 //麻烦
    37 //class Test1 extends Thread
    38 //{
    39 //    public void run()
    40 //    {
    41 //        for(int x=0;x<100;x++)
    42 //            System.out.println(Thread.currentThread().getName());
    43 //    }
    44 //}
  • 相关阅读:
    响应式页面@media介绍和移动端单位介绍
    jQuery的位置信息和事件
    jQuery的文档操作和操作input的value值,ajax
    jQuery的选择器和属性操作
    虚拟机linux桥接联网问题
    Linux基础知识与命令1(su passwd)
    操作系统基础知识笔记1
    Algs4-2.3.10快排100万元素对比次数超1000亿次的概率
    Algs4-2.3.9请说明Quick.sort()在处理只有两种、三种主键值时的行为
    Algs4-2.3.8Quick.sort()在处理N个全部重复的元素时比较次数
  • 原文地址:https://www.cnblogs.com/sunxlfree1206/p/4679111.html
Copyright © 2020-2023  润新知