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; } }