Java学习--线程(二)

目录

   1.线程同步

  2.线程通信

  3.线程池

  4.生产者消费者模型(以馒头为例)

一、线程同步

  由来:多个线程同时访问一个对象,需要对这个对象进行协调

  同步 synchronized
  可以修饰方法 同步方法
  可以修饰对象 同步对象

 如果不使用线程锁会出现以下情况:

  

两次id的结果相加不等于原始id

  同步锁,一个时间点上只允许一个线程操作一个方法或对象
  一个线程访问同步代码块,其他非同步的代码还是可以被多个线程同时访问
  当前线程访问同步代码块时,就获得了锁,访问完毕后会释放锁。

使用同步锁后会出现如下情况:

 

以下是使用synchronized关键字的基本实现代码:

package com.demo.thread;

public class Demo {
    public static void main(String[] args) {
        Entity e = new Entity();
        e.setId(1000);
        Thread t = new Thread(new IdThread(e,300),"t");
        Thread t1 = new Thread(new IdThread(e, 300),"t1");
        t1.start();
        t.start();
    }
}
/**
 * Id实体类
 * @author Administrator
 *
 */
class Entity{
    private int id;
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    /**
     * 减少id
     */
    public void reduceId(int count){
        id = id - count;
    }
}
/**
 * Id线程类
 * @author Administrator
 *
 */
class IdThread implements Runnable {
    private Entity e ;
    private int count;
    /**
     * 通过构造传参的方法获取同一个entity对象
     * @param e
     * @param count
     */
    public IdThread(Entity e,int count) {
        this.e=e;
        this.count = count;
    }
    public void run() {
        int reduce = reduce();
        System.out.println(Thread.currentThread().getName()+":"+reduce);
    }
    private int reduce(){
        synchronized (e) {
            e.reduceId(count);
            return e.getId();
        }
    }
}

运行结果为:

如果不加锁,运行结果则会出现如下情况:

二、线程通信

   由于一个进程中的线程是公用的同一个资源,所以线程之间可以共享进程的内存、数据。线程通信则是对共享数据的有效操作

线程通信所用到的方法是object 类里的:

1.wait()          等待,线程处在阻塞状态

2.notify()        唤醒,通知其他另一个线程进入就绪状态

ps:使用wait()方法与notify方法时,所访问的资源必须是有synchronized字段修饰的不然会抛出一个InterruptedException

package com.demo.thread;
/**
 * 线程通信主函数
 * @author Administrator
 *
 */
public class SignalDemo {
    public static void main(String[] args) {
        myObject obj = new myObject();
        obj.setSize(1200);
        new Thread(new SignalThread1(obj),"通信一").start();
        new Thread(new SignalThread2(obj),"通信二").start();
    }
}
class SignalThread1 implements Runnable{
    private myObject obj = null;
    /**
     * 构造函数里需要传入同一个对象
     * @param obj
     */
    public SignalThread1(myObject obj) {
        this.obj = obj;
    }
    @Override
    public void run() {
        //使用线程需要将对象同步
        synchronized (obj) {
            for(int i =0 ; i<10;i++){
                //由于访问的obj对象是被锁住的(同步),所有不用调用wait方法了如果额外调用则会抛出 InterruptedException
                    obj.reSize(100);
                    System.out.println(Thread.currentThread().getName()+" : "+obj.getSize());
                    if(i==3){
                        try { 
                            //线程等待
                            obj.wait();
                        } catch (InterruptedException e) {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                        }
                    }
            }
        }
        
        
    }
    
}
/**
 * 构造函数里需要传入同一个对象
 * @author Administrator
 *
 */
class SignalThread2 implements Runnable{
    private myObject obj = null;
    public SignalThread2(myObject obj) {
        this.obj = obj;
    }
    @Override
    public void run() {
        //使用线程需要将对象同步
        synchronized (obj) {
        for(int i =0;i<10;i++){
                obj.reSize(20);
                System.out.println(Thread.currentThread().getName()+" : "+obj.getSize());
                if(i==5){
                    obj.notify();
                }
        }
        }
    }
}
/**
 * 自定义对象
 * @author Administrator
 *
 */
class myObject{
    private int size;

    /**
     * 减少size
     */
    public void reSize(int count){
        size = size - count;
    }
    public int getSize() {
        return size;
    }

    public void setSize(int size) {
        this.size = size;
    }
    
}

 三、线程池

在某些特定的条件下,由于需要重复使用线程,如果采用传统使用线程的方法,程序的效率可能会比单线程还低。

此时需要创建一个可以重复使用的线程----线程池

创建线程池的方式

  Executors 创建线程池的工具类
  ExecutorService 线程池

方式一:创建可缓存的线程池

方式二:创建固定个数的线程池

 

方式三:创建单个线程的线程池

 

方式四:创建定时任务的线程池

 

scheduledThreadPool.scheduleWithFixedDelay(worker, 5, 3, TimeUnit.SECONDS);

 四、生产者消费者模型(以馒头为例)

以下为一个简单的生产者消费者模型的基本实现:

  

package demo;
/**
 * 馒头类
 * @author Administrator
 *
 */
public class Buns {
    private int id;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }
}
馒头类
package demo;
/**
 * 生产和消费的方法
 * @author Administrator
 *
 */
public class Way {
    //定义一个竹筐数组,竹筐里可以放入10个馒头
    private Buns [] basket = new Buns[10];
    //定义一个记录竹筐里的馒头数的变量
    private int count = 0;
    
    /**
     * 生产者放馒头到竹筐中
     * @param bun  放入的馒头
     */
    public synchronized void put(Buns bun){
        //竹筐放满时线程生产者等待
        if(count==10){
            try {
                wait();
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        //生产一个就往竹筐里放一个馒头
        basket[count] = bun;
        System.out.println(Thread.currentThread().getName()+"生产馒头"+bun.getId());
        //生产了馒头,通知消费者消费
        notify();
        count++;
    }
    /**
     * 消费者消费馒头
     */
    public synchronized void eat(){
        //如果count是0的话则表示竹筐里是空,所以需要等待
        if(count==0){
            try {
                wait();
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }        
        //如果不为空的话就将数组中的当前位置置位空表示消费了这个位置的馒头
        basket[count] = null;
        //通知生产者生产馒头
        notify();
        count--;
        
        System.out.println(Thread.currentThread().getName()+"消费馒头"+basket[count].getId());
    }
}
生产消费实现类
package demo;
/**
 * 生产者线程
 * @author Administrator
 *
 */
public class ProducerThread implements Runnable{
    
    //由于生产者和消费者需要通信,所以需要使用的一个同步类里的方法,所以这个类需要构造传过来
    private Way way;
    public ProducerThread(Way way) {
        this.way = way;
    }
    @Override
    public void run() {
        int i=0;
        while(true){
            i++;
            Buns bun = new Buns();            
            bun.setId(i);
            way.put(bun);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }
    
}
生产者线程类
package demo;
/**
 * 消费者线程
 * @author Administrator
 *
 */
public class ConsumerThread implements Runnable{
    //由于生产者和消费者需要通信,所以需要使用的一个同步类里的方法,所以这个类需要构造传过来
    private Way way;
    public ConsumerThread(Way way) {
        this.way = way;
    }
    @Override
    public void run() {
        while(true){
            
            way.eat();
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }
    
}
消费者线程类
package demo;

public class Start {
    public static void main(String[] args) {
        Way way = new Way();
        new Thread(new ProducerThread(way),"生产者").start();
        new Thread(new ConsumerThread(way),"消费者").start();
    }
}
线程启动类

  

原文地址:https://www.cnblogs.com/bananafish/p/9645164.html