JAVA笔记——多线程

JAVA笔记——多线程

转载注明出处:https://blog.csdn.net/qq_40270579/article/details/81381656

多线程

线程

进程中一个负责程序执行的控制单元(执行路径)

一个进程中可以有多个执行路径,称之为多线程。

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

开启多个线程是为了同时运行多部分代码。

每一个线程都有自己运行的内容。这个内容可以成为线程要执行的任务。

多线程好处:解决了多部分同时运行的问题

多线程的弊端:线程太多回收效率的降低

JVM启动时就启动了多个线程,至少有两个线程可以分析出来

扫描二维码关注公众号,回复: 2599664 查看本文章

1、执行mani函数的线程(栈内存)

    该线程的任务代码都定义在main函数中

2、负责垃圾回收的线程(堆内存)

线程调用是随机的

可以通过Thread的getName获取线程的名称 

public static Thread currentThread()

返回对当前正在执行的线程对象的引用

主线程的名称:main

其他线程名称:Thread-编号(从0开始)

如何创建线程

方式一:继承Thread类

步骤:

1、定义一个类继承于Thread类

2、覆盖Thread类中的run方法

3、直接创建对象Thread的子类对象创建线程

4、调用start方法开启线程并调用任务的run方法执行

举例

package com.thread;

/**

 * 通过创建Thread的子类创建线程

 * @author Administrator

 *

 */

public class ThreadDemo1 extends Thread {



   private String name;

  

   ThreadDemo1(String name){

      this.name = name;

   }

  

   public void run() {

      for(int x = 0;x<10;x++) {

         System.out.println("name = "+this.name+"..."+" x = "+x+"..."+Thread.currentThread().getName());

      }

   }

  

  

   public static void main(String[] args) {

      ThreadDemo1 d1 = new ThreadDemo1("狗子");

      ThreadDemo1 d2 = new ThreadDemo1("haha");

      d1.start();

      d2.start();

      System.out.println("over"+"..."+Thread.currentThread().getName());

     

   }

}

//调用run和调用start的区别

调用run:按照对象在main主线程中定义的顺序调用

调用start:按照线程执行

方式二、实现Runable接口

步骤:

1、定义类实现Runnable接口

2、覆盖接口中的 run方法,将线程任务代码封装到run方法中

3、通过Thread类创建线程对象,并将Runnable接口的子类对象作为Thread类的构造函数的参数进行传递。

       为什么?因为线程的任务都封装在Runnable接口子类的对象的run方法中

       所以要在线程对象创建时就必须明确要运行的任务

4、调用线程对象的start方法开启线程

Thread 内部部分结构

class Thread{

       private Runnable r;

       Thread(){};

       Thread(Runnable r){

              this.r = r;

       }

       public void run(){

       if(r!=null)

              r.run();

       }

       public void start(){

              run();

       }

}

Runnabe接口的好处:

1、将线程的任务从线程的子类中分离出来,进行单独的封装,按照面向对象的思想将任务的封装成对象

2、避免了JAVA单继承的局限性

所以,创建线程第二种方式较为常用

举例:

package com.thread;



/**

 * 实现Runable接口创建线程

 * @author Administrator

 *

 */

class Demo implements Runnable{



   @Override

   public void run() {

      show();

   }

  

   public void show() {

      for(int x = 0;x<10;x++) {

         System.out.println(" x = "+x+"..."+Thread.currentThread().getName());

      }

   }

  

}



public class ThreadDemo2 {

  

   public static void main(String[] args) {

      Demo d = new Demo();

     

      Thread t1 = new Thread(d);

      Thread t2 = new Thread(d);

     

      t1.start();

      t2.start();

     

   }



}

线程的四种状态

被创建:start()

运行:具备执行资格,同时具备执行权;

冻结:sleep(time),wait()—notify()

唤醒:线程释放了执行权,同时释放执行资格;

临时阻塞状态:线程具备cpu的执行资格,没有cpu的执行权;

消亡:stop()

例子:

package com.demos;

/**

 * 卖票

 * @author Administrator

 *

 */



class Ticket implements Runnable{

   //票数100

   private int num = 100;

  

   public void sale() {

      while(true) {

         if(num>0)

         {

            System.out.println(Thread.currentThread().getName()+"..."+"num = "+num--);

         }

      }

   }

  

   public void run() {

      sale();

   }

}



public class TicketDemo {



   public static void main(String[] args) {

      Ticket t = new Ticket(); //创建线程对象

     

      Thread t1 = new Thread(t);  //窗口一>>线程1

      Thread t2 = new Thread(t);   //窗口二>>线程2

      Thread t3 = new Thread(t);  //窗口三>>线程3

     

      t1.start();

      t2.start();

      t3.start();



   }



}

存在的问题:存在线程安全问题

线程安全问题产生的原因:

1、多个线程在操作共享数据

2、操作共享数据的线程的线程代码有多条

当一个线程在执行操作共享数据的多条代码过程中,其他线程参与了运算就会导致线程安全问题的产生

解决思路:

将多条操作共享数据的线程封装起来,当右线程在执行这些代码的时候,

其他线程不可以参与运算。必须要当前线程把这些代码执行完毕,其他线程参与运行

在JAVA中,用同步代码块就可以解决这个问题

同步代码块格式:

synchronized(对象){

       需要被同步的代码;

}

对象锁:控制线程

同步

同步的好处:解决了线程安全性问题

同步的弊端:相对降低了效率,因为同步外的线程都会判断同步锁

同步的前提:同步中必须右多个线程并使用同一个锁

修改后:

package com.demos;

/**

 * 卖票

 * @author Administrator

 *

 */



class Ticket implements Runnable{

   //票数100

   private int num = 100;

   Object obj = new Object();

  

   public void sale() {

      while(true) {

         synchronized(obj) {

            if(num>0)

            {

                try {

                   Thread.sleep(10);

                } catch (InterruptedException e) {

                   e.printStackTrace();

                }

                System.out.println(Thread.currentThread().getName()+"..."+"num = "+num--);

            }

         }

        

      }

   }

  

   public void run() {

      sale();

   }

}



public class TicketDemo {



   public static void main(String[] args) {

      Ticket t = new Ticket(); //创建线程对象

     

      Thread t1 = new Thread(t);  //窗口一>>线程1

      Thread t2 = new Thread(t);   //窗口二>>线程2

      Thread t3 = new Thread(t);  //窗口三>>线程3

     

      t1.start();

      t2.start();

      t3.start();



   }



}

同步函数:public synchronized void add(int num){}

同步函数的使用的锁是this

同步函数和同步代码块的区别:同步函数的使用的锁是固定的this

建议使用同步代码块

静态的同步函数使用的锁是该函数所属的字节码文件对象。可以用getClass方法获取,也可以用当前类名.class表示

举例:

package com.demos;

/**

 * 需求:储户,两个,每个存每次存100,共存三次

 * @author Administrator

 *

 */



class Bank{

   private int sum;

   //private Object obj = new Object();



   //同步函数synchronized

   public synchronized void add(int num) {

      //synchronized(obj) {

         sum = sum + num;

         try {

            Thread.sleep(10);

         } catch (InterruptedException e) {

            // TODO Auto-generated catch block

            e.printStackTrace();

         }

         System.out.println(Thread.currentThread().getName()+"..."+"sum = "+sum);

      //}

     

   }

}



class Add implements Runnable{



   private Bank b = new Bank();

   @Override

   public void run() {

      for(int i = 0;i<3;i++)

      b.add(100);

     

   }

  

}



public class BankDemo {



   public static void main(String[] args) {

      Add a = new Add();

      Thread t1 = new Thread(a);

      Thread t2 = new Thread(a);

     

      t1.start();

      t2.start();

   }



}

多线程下的单例模式

//饿汉式

class Signle{

   private static final Signle s = new Signle();

   private Signle(){};

   public static Signle getInstance(){

      return s;

   }

}

//懒汉式

class Signle{

   private static final Signle s = null;

   private Signle(){};

   public static Signle getInstance(){

      if(s==null){

         synchronized(Signle.class){

            if(s==null)

                s = new Signle();

            return s;

         }

      }

   }

}



Class SignleDemo{

   public static void main(String []args){

     

   }

}

死锁

1、同步块里嵌套竞争锁

package com.thread;

/**

 * 死锁

 * @author Administrator

 *

 */



class Test implements Runnable{



   private boolean flag;

  

   Test(boolean flag){

      this.flag = flag;

   }

  

   @Override

   public void run() {

      if(flag) {

         synchronized(MyLock.locka) {

            System.out.println(Thread.currentThread().getName()+"..."+"获取A锁");

            System.out.println(Thread.currentThread().getName()+"..."+"等待获取B锁");

            synchronized(MyLock.lockb) {

                System.out.println(Thread.currentThread().getName()+"..."+"获取B锁");

            }

         }

      }

      else {

         synchronized(MyLock.lockb) {

            System.out.println(Thread.currentThread().getName()+"..."+"获取B锁");

            System.out.println(Thread.currentThread().getName()+"..."+"等待获取A锁");

            synchronized(MyLock.locka) {

                System.out.println(Thread.currentThread().getName()+"..."+"获取A锁");

            }

         }

      }

     

   }

  

}



class MyLock {

   public static final Object locka = new Object();

   public static final Object lockb = new Object();

}







public class DeadLockDemo {



   public static void main(String[] args) {

      //创建两个线程对象

      Test a = new Test(true);

      Test b = new Test(false);

     

      Thread t1 = new Thread(a);

      Thread t2 = new Thread(b);

     

      t1.start();

      t2.start();



   }



}

Thread-0得到A锁,进入死锁,需要等待B锁释放;

Thread-1得到B锁,进入死锁,需要等待A锁释放.

和谐得的情况:

Thread-0...获取A锁

Thread-0...等待获取B锁

Thread-0...获取B锁

Thread-1...获取B锁

Thread-1...等待获取A锁

Thread-1...获取A锁

线程通信

1:将资源封装成对象。

2:将线程执行的任务(任务其实就是run方法。)也封装成对象。

等待唤醒机制

涉及的方法:

1、wait():  让线程处于冻结状态,被wait()的线程会被存储到线程池中

2、notify():唤醒线程池中的一个线程(任意)

3、notiftAll():唤醒线程池中的所有线程

这些方法都必须定义在同步中

因为这些方法是用于操作线程状态的方法,必须要明确到底是操作的哪个锁上的线程

为什么操作线程的方法wait notify notifyAll定义在Object类中?

因为这些方法是监视器的方法,监视器其实就是锁,锁可以是任意的对象,任意的对象调用的方式一定定义在Object类中

例子:

package com.thread;

/**

 * 等待唤醒机制

 * @author Administrator

 *

 */



class Resourse {

   private String name;

   private String sex;

   private boolean flag = false;

  

   public synchronized void set(String name,String sex) {

      if(flag) {

         try {

            this.wait();

         } catch (InterruptedException e) {

            e.printStackTrace();

         }

      }

  

      this.name = name;

      this.sex = sex;

      flag = true;

      this.notify();

   }

  

   public synchronized void out() {

      if(!flag) {

         try {

            this.wait();

         } catch (InterruptedException e) {

            e.printStackTrace();

         }

      }

      System.out.println(name+"..."+sex);

      flag = false;

      this.notify();

   }

}



//输入

class Input implements Runnable{

   Resourse r = new Resourse();



   Input(Resourse r){

      this.r = r;

   }

  

   @Override

   public void run() {

      int x = 0;

      while(true) {

         if(x == 0) {

            r.set("小米", "男");

         }

         else {

            r.set("洛洛", "女");

         }

         x=(x+1)%2;

      }

     

   }

  

}



//输出

class Output implements Runnable{

   Resourse r = new Resourse();



   Output(Resourse r){

      this.r = r;

   }

  

   @Override

   public void run() {

      while(true) {

         r.out();

      } 

   }

  

}



public class ResouseDemo {



   public static void main(String[] args) {

      //创建资源

      Resourse r = new Resourse();

      //创建任务

      Input in = new Input(r);

      Output out = new Output(r);

      //创建线程

      Thread t1 = new Thread(in);

      Thread t2 = new Thread(out);

      t1.start();

      t2.start();



   }

  

}



生产者、消费者

单一生产者、消费者

package com.thread;

/**

 * 等待唤醒机制:生产者、消费者

 * @author Administrator

 *

 */



class Resource{

   private String name;

   private static int count = 1;

   private boolean flag = false;

  

   public synchronized void set(String name) {

      if(flag) {

         try {

            this.wait();

         } catch (InterruptedException e) {

            e.printStackTrace();

         }

      }

      this.name = name + count;

      count++;

      System.out.println(Thread.currentThread().getName()+"...生产者..."+this.name);

      flag = true;

      this.notify();

   }

  

   public synchronized void out() {

      if(!flag) {

         try {

            this.wait();

         } catch (InterruptedException e) {

            // TODO Auto-generated catch block

            e.printStackTrace();

         }

      }

      System.out.println(Thread.currentThread().getName()+">>>消费者>>>"+this.name);

      flag = false;

      this.notify();

   }



}



//生产者

class Producer implements Runnable{

   private Resource r;

  

   Producer(Resource r) {

      this.r = r;

   }

  



   @Override

   public void run() {

      while(true) {

         r.set("烤鸭");

      }

     

   }

  

}



//消费者

class Consumer implements Runnable{



   private Resource r;

  

   Consumer(Resource r) {

      this.r = r;

   }

  

   @Override

   public void run() {

      while(true) {

         r.out();

      }

     

   }

  

}



public class ProduerConsumerDemo {



   public static void main(String[] args) {

      //创建资源

      Resource r = new Resource();

      //创建任务

      Producer pro = new Producer(r);

      Consumer con = new Consumer(r);

      //创建线程

      Thread t1 = new Thread(pro);

      Thread t2 = new Thread(con);

     

      t1.start();

      t2.start();          



   }



}

多生产、多消费问题(死锁)

if判断flag改为while判断实现多生产、多消费导致没有唤醒的死锁

解决方法:将notify();改为notifyAll();

代码修改:

package com.thread;

/**

 * 等待唤醒机制:单一生产者、消费者

 * @author Administrator

 * 改为多生产者、消费者:

 * if(flag)改为while(flag)

 * this.notify();改为this.notifyAll();

 */



class Resource{

   private String name;

   private static int count = 1;

   private boolean flag = false;

  

   public synchronized void set(String name) {

      while(flag) {

         try {

            this.wait();

         } catch (InterruptedException e) {

            e.printStackTrace();

         }

      }

     

      this.name = name + count;

      count++;

      System.out.println(Thread.currentThread().getName()+"...生产者...生产"+this.name);

      flag = true;

      this.notifyAll();

   }

  

   public synchronized void out() {

      while(!flag) {

         try {

            this.wait();

         } catch (InterruptedException e) {

            // TODO Auto-generated catch block

            e.printStackTrace();

         }

      }

      System.out.println(Thread.currentThread().getName()+">>>消费者>>>消费"+this.name);

      flag = false;

      this.notifyAll();

   }



}



//生产者

class Producer implements Runnable{

   private Resource r;

  

   Producer(Resource r) {

      this.r = r;

   }

  

   @Override

   public void run() {

      while(true) {

         r.set("烤鸭");

      }

     

   }

  

}



//消费者

class Consumer implements Runnable{



   private Resource r;

  

   Consumer(Resource r) {

      this.r = r;

   }

  

   @Override

   public void run() {

      while(true) {

         r.out();

      }

     

   }

  

}





public class ProduerConsumerDemo {



   public static void main(String[] args) {

      //创建资源

      Resource r = new Resource();

      //创建任务

      Producer pro = new Producer(r);

      Consumer con = new Consumer(r);

      //创建线程

      Thread t0 = new Thread(pro);

      Thread t1 = new Thread(pro);

      Thread t2 = new Thread(con);

      Thread t3 = new Thread(con);

      t0.start();

      t1.start();

      t2.start(); 

      t3.start();

        



   }



}

多生产、多消费问题JDK新特征

synchronized 同步代码块对锁的操作是隐式的

JDK1.5后定义了Lock接口,存在于java.util.concurrent.locks

Lock实现提供比使用synchronized方法和语句可以获得的更广泛的锁定操作

Lock接口:替代了同步函数或同步代码块。将同步的隐式锁操作变成显示锁的操作。

同时更加灵活,可以在一个锁里加多个监视器

Condition接口:替代了Object中的wait notify notiftAll方法

将这些监视器方法单独进行封装,变成Condition监视器的对象,可以任意锁进行组合

提供了await();signal();signalAll();方法

JDK API提供的范例

class BoundedBuffer {

   final Lock lock = new ReentrantLock();

   final Condition notFull  = lock.newCondition();

   final Condition notEmpty = lock.newCondition();



   final Object[] items = new Object[100];

   int putptr, takeptr, count;



   public void put(Object x) throws InterruptedException {

     lock.lock();

        try {

       while (count == items.length)

         notFull.await();

       items[putptr] = x;

       if (++putptr == items.length) putptr = 0; //达到生产数量length后从0开始继续生产

       ++count;

       notEmpty.signal();

     } finally {

               lock.unlock();

        }

   }



   public Object take() throws InterruptedException {

     lock.lock();

        try {

       while (count == 0)

         notEmpty.await();

       Object x = items[takeptr];

       if (++takeptr == items.length) takeptr = 0;//达到生产数量大小length后从0开始继续消费

       --count;

       notFull.signal();

       return x;

     } finally {

               lock.unlock();

        }

   }

 }



生产者、消费者实例修改

package com.thread;

import java.util.concurrent.locks.*

/**

 * 等待唤醒机制:生产者、消费者JDK1.5后解决办法

 * @author Administrator



 */



class Resource{

       private String name;

       private static int count = 1;

       private boolean flag = false;

       //创建锁对象lock

       Lock lock = new ReentrantLock();

       //通过已有的锁获取该锁上的监视器对象

       //Condition con = lock.newCondition();

      

       //通过已有的锁获取两组监视器,一组监视生产者,一组监视消费者

       Condition producer_con = lock.newCondition();

       Condition consumer_con = lock.newCondition();

      

      

      

       public void set(String name) {

              lock.lock();

              try{

                     while(flag) {

                     try {

                            //this.wait();

                            //con.await();

                            producer_con.signal();

                     } catch (InterruptedException e) {

                            e.printStackTrace();

                     }

              }

             

              this.name = name + count;

              count++;

              System.out.println(Thread.currentThread().getName()+"...生产者...生产"+this.name);

              flag = true;

              //this.notifyAll();

              //con.signalAll();

              consumer_con.signal();

              }

              finally{

                     lock.unlock();

              }

             

       }

      

       public synchronized void out() {

              lock.lock();

              try{

                     while(!flag) {

                     try {

                            //this.wait();

                            //con.await();

                            consumer_con.await();

                     } catch (InterruptedException e) {

                            // TODO Auto-generated catch block

                            e.printStackTrace();

                     }

              }

              System.out.println(Thread.currentThread().getName()+">>>消费者>>>消费"+this.name);

              flag = false;

              //this.notifyAll();

              //con.signalAll();

              producer_con.signal();

              }

              finally{

                     lock.unlock();

              }

             

       }



}



//生产者

class Producer implements Runnable{

       private Resource r;

      

       Producer(Resource r) {

              this.r = r;

       }

      

       @Override

       public void run() {

              while(true) {

                     r.set("烤鸭");

              }

             

       }

      

}



//消费者

class Consumer implements Runnable{



       private Resource r;

      

       Consumer(Resource r) {

              this.r = r;

       }

      

       @Override

       public void run() {

              while(true) {

                     r.out();

              }

             

       }

      

}





public class ProduerConsumerDemo {



       public static void main(String[] args) {

              //创建资源

              Resource r = new Resource();

              //创建任务

              Producer pro = new Producer(r);

              Consumer con = new Consumer(r);

              //创建线程

              Thread t0 = new Thread(pro);

              Thread t1 = new Thread(pro);

              Thread t2 = new Thread(con);

              Thread t3 = new Thread(con);

              t0.start();

              t1.start();

              t2.start();     

              t3.start();



       }



}

wait 和 sleep(都能使线程处于冻结状态) 的区别

1、wait可以指定时间

     sleep必须指定时间

2、在同步中时,对CPU的执行权和锁的处理不同

     wait:释放执行权,释放锁

     sleep:释放执行权,不释放锁

停止线程

1、stop方法,已过时,该方法具有固有的不安全性

2、run方法结束

怎么控制线程的任务结束呢?

任务中都会有循环结构只要控制住循环就可以结束任务

控制循环通过定义标记来完成

如果线程处于冻结状态,无法读取标记,该如何结束?

可以使用interrupt()方法将线程从冻结状态强制恢复到运行状态中来,让线程具备CPU的执行资格

但是强制动作会发生InterruptedException异常,需要处理

线程中常见的方法

setDaemon();    守护线程


public final void setDaemon(boolean on)将此线程标记为daemon线程或用户线程。 当运行的唯一线程都是守护进程线程时,Java虚拟机将退出。

线程启动前必须调用此方法。

参数

on - 如果 true ,将此线程标记为守护线程

异常

IllegalThreadStateException - 如果这个线程是 alive

SecurityException - 如果 checkAccess()确定当前线程不能修改此线程



join();                 


public final void join()

                throws InterruptedException等待这个线程死亡。

调用此方法的行为方式与调用完全相同

join (0)

异常

InterruptedException - 如果任何线程中断当前线程。 当抛出此异常时,当前线程的中断状态将被清除。

t1.join(); //t1线程要申请加入进来运行。临时加入一个线程运算时可以使用join方法

toString


public String toString()返回此线程的字符串表示,包括线程的名称,优先级和线程组。

重写:

toString在 Object

结果

这个线程的字符串表示形式。

Modifier and Type          Field and Description

static int                           MAX_PRIORITY (10)

                                          线程可以拥有的最大优先级。 

static int                            MIN_PRIORITY (1)

                                          线程可以拥有的最小优先级。 

static int                           NORM_PRIORITY (5)

                                          分配给线程的默认优先级。 

setPriority(int newPriority)

更改此线程的优先级。

yield


public static void yield()对调度程序的一个暗示,即当前线程愿意产生当前使用的处理器。 调度程序可以自由地忽略这个提示。

产量是一种启发式尝试,以改善否则会过度利用CPU的线程之间的相对进度。 其使用应与详细的分析和基准相结合,以确保其具有预期的效果。

很少使用这种方法。 它可能对调试或测试有用,可能有助于根据种族条件重现错误。 在设计并发控制结构(例如java.util.concurrent.locks包中的并行控制结构)时也可能有用。

猜你喜欢

转载自blog.csdn.net/qq_40270579/article/details/81381656