java—线程的使用

** 概念

进程:简单说就是一个正在运行的程序。进程负责分配程序的内存空间等资源。

线程:一个进程的某个执行流程。一个进度可以有多个线程。进程中的多个线程共享进程的内存。

多线程就是一个进程中有多个线程同时进行。

对于电脑,多个程序同时运行,其实是CPU的分时机制在起作用,不过现在都是多核的电脑了。

多线程意味这可以在一个进程里同时执行多个任务。而且可以提高资源的利用率。

# 如何使线程

方式一、继承自Thread

                  >> 继承自Thread

                  >> 重写run()方法

                  >> 创建继承自Thread类的实例

                  >> 调用start()方法开启线程

publicclass Tm05 extends Thread{

   /**

    * 一个程序就是一个进程,进程负责分配内存

    * 一个进程有多个线程[多线程]可以执行不同的任务,线程负责CPU资源的抢夺

    * 多线程提高了资源的利用率

    * 对于单核的电脑对于微观来说是单进程的

    * 任何一个java程序,jvm在运行的时候创建一个主线程,来执行main中的代码

    * 任何一个java程序,至少有两个线程[1.main | 2.垃圾回收器[GC]]

    */ 

   @Override

   public void run() {

    

     // 自定义线程的任务代码定义在run()方法中

     for(int i=0;i<100;i++){

        System.out.println("run()+"+(i+1));

     }

   }

  

   public static void main(String[] args){

     System.out.println("多线程使用");

     System.out.println("#继承Thread  | #重写run()方法  | #创建Thread实例,并调用start()方法");

     System.out.println("注意不要调用run()方法");

     Tm05 t=new Tm05();

     t.start();;

     for(int i=0;i<100;i++){

        System.out.println("main()+"+(i+1));

    

   }

}

 

方式二、实现Runnable接口[tips:推荐使用第二种,因为java是单继承多实现的]

                   >>实现Runnable接口

                   >>重写run()方法

                   >>创建Runnable接口子类的实例

                   >>创建Thread类实例,并将Runnable接口子类的实例作为实参传入到构造方法

                   >>Thread类的实例调用start()方法开启线程[start()方法在Thread类中]

package hs;

/**

 * 推荐使用第二种[这种]

 * 因为java是单继承多实现的,如果用了继承Thread类,其他类就不能继承了

*/

publicclass Tm10 implements Runnable{

   static Object lockState=new Object();

   // 方式二中Tm10只是共享了一份代码而已,所以不用加static

   int num=100;

  

   @Override

   public void run() {

     while(true){

        synchronized (lockState) {

          if(num>0){

             num--;

             System.out.println(Thread.currentThread().getName()+"卖出1,还有"+num+"");

          }else{

             System.out.println("已经买完了......");

             break;

          }

        }

     }

   }

  

   public static void main(String[] args) {

     Tm10 t=new Tm10();

     Thread t1=new Thread(t, "A");

     Thread t2=new Thread(t, "B");

     Thread t3=new Thread(t, "C");

     t1.start();

     t2.start();

     t3.start();

   }

}

 

** 线程的状态和常见的方法

就绪状态:状态的线程位于可运行线程池中,等待获取cpu的执行权

临时阻塞状态:线程因为某种原因放弃CPU使用权,暂时停止运行

运行状态:就绪状态的线程获取了CPU执行权,执行程序代码

 

常见的线程方法

publicclass Tm12 implements Runnable{

   @Override

   public void run() {

     int i=0;

     while(true){

        i++;

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

        if(i>=10){

          break;

        }

     }

   }

   /**

    * 常用的线程方法

    * @throws InterruptedException

   */

   public static void main(String[] args) throws InterruptedException {

     Tm12 t=new Tm12();

      Thread th=new Thread(t, "my thread");

     // # setName() 设置线程的名字

     th.setName("set thread name");

     // # setPriority() 设置当前线程的优先级

     th.setPriority(10);

     // # start() 开启线程

     th.start();

     // # sleep() 让线程睡眠XXX毫秒对于sleep是对当前运行的线程有效果

     Thread.sleep(1000);

     // # setDaemon() 设置是否为守护线程

     th.setDaemon(true);

     // # 还有notify()/notifyAll()wait()等方法后面讲

     // # currentThread() 获取当前线程对象

     Thread thMain=Thread.currentThread();

     for(int i=1;i<=10;i++){

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

      }

   }

}

 

** 同步代码块和同步函数

同步代码块[同步代码块比较容易控制锁对象,所以推荐使用同步代码块]

   @Override

   public void run() {

     while (true) {

        synchronized (lockState) {

          if (num > 0) {

             num--;

             System.out.println(Thread.currentThread().getName()+"成功卖出1张票,还有:" + this.num + "");

          } else {

             System.out.println("已经买完了票......");

             break;

          }

        }

     }

   }

同步函数

// 静态同步函数 --> 锁对象是唯一的

   // **推荐使用同步代码块**

   public static synchronizedvoid getMoney(){

     while(true){

        if(money-1000>0){

          money=money-1000;

           System.out.println(Thread.currentThread().getName()+"取了1000,还有"+money);

        }else{

          System.out.println("对不起余额不足......");

          break;

        }

     }

   }

** 锁对象

Java中的每个对象都有一个内置锁,只有当对象具有同步方法代码时,内置锁才会起作用,当进入一个同步的非静态方法时,就会自动获得与类的当前实例(this)相关的锁,该类的代码就是正在执行的代码。获得一个对象的锁也成为获取锁、锁定对象也可以称之为监视器来指我们正在获取的锁对象。

因为一个对象只有一个锁,所有如果一个线程获得了这个锁,其他线程就不能获得了,直到这个线程释放(或者返回)锁。也就是说在锁释放之前,任何其他线程都不能进入同步代码(不可以进入该对象的任何同步方法)。释放锁指的是持有该锁的线程退出同步方法,此时,其他线程可以进入该对象上的同步方法。

1:只能同步方法(代码块),不能同步变量或者类

2:每个对象只有一个锁

3:不必同步类中的所有方法,类可以同时具有同步方法和非同步方法

4:如果两个线程要执行一个类中的一个同步方法,并且他们使用的是了类的同一个实例(对象)来调用方法,那么一次只有一个线程能够执行该方法,另一个线程需要等待,直到第一个线程完成方法调用,总结就是:一个线程获得了对象的锁,其他线程不可以进入该对象的同步方法。

5:如果类同时具有同步方法和非同步方法,那么多个线程仍然可以访问该类的非同步方法。

同步会影响性能(甚至死锁),优先考虑同步代码块。

6:如果线程进入sleep()睡眠状态,该线程会继续持有锁,不会释放。

** 生产者和消费者

[百度百科] 生产者消费者问题(英语:Producer-consumerproblem),也称有限缓冲问题(英语:Bounded-buffer problem),是一个多线程同步问题的经典案例。该问题描述了两个共享固定大小缓冲区线程——即所谓的生产者消费者”——在实际运行时会发生的问题。生产者的主要作用是生成一定量的数据放到缓冲区中,然后重复此过程。与此同时,消费者也在缓冲区消耗这些数据。该问题的关键就是要保证生产者不会在缓冲区满时加入数据,消费者也不会在缓冲区中空时消耗数据。

wait:告诉当前线程放弃执行权,并放弃监视器(锁)并进入阻塞状态,直到其他线程持有获得执行权,并持有了相同的监视器(锁)并调用notify为止。

notify:唤醒持有同一个监视器(锁)中调用wait的第一个线程,例如,餐馆有空位置后,等候就餐最久的顾客最先入座。注意:被唤醒的线程是进入了可运行状态。等待cpu执行权。

notifyAll:唤醒持有同一监视器中调用wait的所有的线程。

如何解决生产者和消费者的问题?

可以通过设置一个标记,表示数据的(存储空间的状态)例如,当消费者读取了(消费了一次)一次数据之后可以将标记改为false,当生产者生产了一个数据,将标记改为true

,也就是只有标记为true的时候,消费者才能取走数据,标记为false时候生产者才生产数据。

package hs;

 

import java.util.ArrayList;

import java.util.List;

 

/**

 * 产品类

*/

class Product{

   String name;

   double price;

  

   public Product(String name, doubleprice) {

     this.name=name;

     this.price=price;

   }

}

 

/**

 * 生产者

 */

class Producer implements Runnable{

   static int i=0;

   List<Product> p;

  

   public Producer(List<Product> p) {

     this.p=p;

   }

  

   @Override

   public void run() {

     while(true){

        synchronized (p) {

          if(p.size()<10){

             Product pro;

             if(i%2==0){

               pro=new Product("西瓜",2.99);

               p.add(pro);

               i++;

             }else{

               pro=new Product("桃子", 4.88);

               p.add(pro);

               i++;

             }

             System.out.println(Thread.currentThread().getName()+":生产了一个"

                  +pro.name+",价格是"+pro.price+"");

             // 通知唤醒的线程

             p.notify();

          }else{

             System.out.println(Thread.currentThread().getName()+":存储区域已经满了......");

             try {

               p.wait();

             } catch (InterruptedException e) {

               e.printStackTrace();

             }

          }

        }

     }

   }

}

 

/**

 * 消费者

 */

class Consumer implements Runnable{

   List<Product> p;

  

   public Consumer(List<Product> p) {

     this.p=p;

   }

  

   @Override

   public void run() {

     while(true){

        synchronized (p) {

          if(p.size()>0){

             System.out.println(Thread.currentThread().getName()+

                  ":取出了一个"+p.get(0).name+",价格"+p.get(0).price+"");

             p.remove(0);

             p.notify();

          }else{

             System.out.println(Thread.currentThread().getName()+":物品已经被取完......");

             try {

               p.wait();

             } catch (InterruptedException e) {

               e.printStackTrace();

             }

          }

        }

     }

   }

}

 

publicclass Tm11 {

  

   public static void main(String[] args) {

     List<Product> p=new ArrayList<Product>();

     Producer producer=new Producer(p);

     Consumer consumer=new Consumer(p);

    

     Thread th1=new Thread(producer, "生产者");

     Thread th2=new Thread(consumer,"消费者");

    

     th1.start();

     th2.start();

    

   }

}

 

** 其他

后台线程:就是隐藏起来一直在默默运行的线程,直到进程结束。

setDaemon(booleanon)

当所有的非后台线程结束时,程序也就终止了同时还会杀死进程中的所有后台线程,也就是说,只要有非后台线程还在运行,程序就不会终止,执行main方法的主线程就是一个非后台线程。

必须在启动线程之前(调用start方法之前)调用setDaemontrue)方法,才可以把该线程设置为后台线程。

 

join方法

A线程执行到了B线程Join方法时A就会等待,等B线程都执行完A才会执行,Join可以用来临时加入线程执行

 

猜你喜欢

转载自blog.csdn.net/u013468915/article/details/51941275
今日推荐