Java学习--线程(二)
目录
一、线程同步
由来:多个线程同时访问一个对象,需要对这个对象进行协调
同步
可以修饰方法 同步方法
可以修饰对象 同步对象
两次
同步锁,一个时间点上只允许一个线程操作一个方法或对象
一个线程访问同步代码块,其他非同步的代码还是可以被多个线程同时访问
当前线程访问同步代码块时,就获得了锁,访问完毕后会释放锁。
使用同步锁后会出现如下情况:
以下是使用
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(); } } }
运行结果为:
如果不加锁,运行结果则会出现如下情况:或
二、线程通信
由于一个进程中的线程是公用的同一个资源,所以线程之间可以共享进程的内存、数据。线程通信则是对共享数据的有效操作
线程通信所用到的方法是
ps:使用
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; } }
三、线程池
在某些特定的条件下,由于需要重复使用线程,如果采用传统使用线程的方法,程序的效率可能会比单线程还低。
此时需要创建一个可以重复使用的线程
创建线程池的方式
方式一:创建可缓存的线程池
方式二:创建固定个数的线程池
方式三:创建单个线程的线程池
方式四:创建定时任务的线程池
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(); } }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 零经验选手,Compose 一天开发一款小游戏!
· 通过 API 将Deepseek响应流式内容输出到前端
· 因为Apifox不支持离线,我果断选择了Apipost!