一.简介
在一般情况下,创建一个线程是不能提高程序的执行效率的,所以要创建多个线程。但是多个线程同时运行的时候可能调用线程函数,在多个线程同时对同一个内存地址进行写入,由于CPU时间调度上的问题,写入数据会被多次的覆盖,所以就要使线程同步。
线程同步:即当有一个线程在对内存进行操作时,其他线程都不可以对这个内存地址进行操作,直到该线程完成操作, 其他线程才能对该内存地址进行操作,而其他线程又处于等待状态,目前实现线程同步的方法有很多,临界区对象就是其中一种。
二.线程同步的方式和机制


public class TestSync implements Runnable{ Timer timer = new Timer(); public static void main(String[] args) { TestSync test = new TestSync(); Thread t1 = new Thread(test); Thread t2 = new Thread(test); t1.setName("线程1"); t2.setName("线程2"); t1.start(); t2.start(); } public void run() { timer.add(Thread.currentThread().getName()); } } class Timer { private static int num = 0; public void add(String name){ //public synchronized void add(String name){ //①表示执行当前方法过程中,锁定当前对象,是当前对象被锁定了 //synchronized(this){ //②表示锁定某一块东西 num++; try{ Thread.sleep(1); }catch(InterruptedException e){ } System.out.println(name+",你是第"+num+"个使用Timer的线程"); //} } }
此时结果是:
线程1,你是第2个使用Timer的线程
线程2,你是第2个使用Timer的线程
此时,可以用关键字synchronized通过线程的互斥来实现同步,
(提供了两种方法:①方法前加上synchronized;②synchronized(this){}):
放开注释的其中之一,此时结果是:
线程1,你是第1个使用Timer的线程
线程2,你是第2个使用Timer的线程
四.死锁

public class TestDeadLock implements Runnable{ public int flag = 0; static Object obj1 = new Object(); static Object obj2 = new Object(); public void setFlag(int flag){ this.flag = flag; } public static void main(String[] args) { TestDeadLock tdl1 = new TestDeadLock(); TestDeadLock tdl2 = new TestDeadLock(); tdl1.setFlag(0); tdl1.setFlag(1); Thread thread1 = new Thread(tdl1); Thread thread2 = new Thread(tdl2); thread1.start(); thread2.start(); } public void run() { System.out.println("flag="+flag); if(flag == 1){ synchronized(obj1){ //先申请资源1 try{ Thread.sleep(500); }catch(InterruptedException e){ e.printStackTrace(); } synchronized(obj2){ //后申请资源2 System.out.println("情况"+flag+"的资源申请完毕"); } } }else if(flag ==0){ synchronized(obj2){ //先申请资源2 try{ Thread.sleep(500); }catch(InterruptedException e){ e.printStackTrace(); } synchronized(obj1){ //后申请资源1 System.out.println("情况"+flag+"的资源申请完毕"); } } } } }
五.synchronized作用域
synchronized只会对使用了synchronized关键字的对象加锁,这样当被加锁对象正在访问时,另外的线程要对其进行访问才会互斥,未被加锁的对象不会产生互斥,即使都会用到同样的资源。因此要实现对某资源的同步就必须要把用到此资源的对象都加上synchronized关键字。如下例:

public class TestLockConcept implements Runnable{ int b = 100; //比如这是多处会用到的资源 public synchronized void m1() throws InterruptedException { b = 1000; Thread.sleep(2000); System.out.println("m1:b = " + b); } // public void m2() { // System.out.println("m2:b = " + b); //此时m2未加锁,m1加了锁,因此可以直接访问m2 // //结果: m2:b = 1000 m1:b = 1000 final:b = 1000 // } // public void m2() { // b = 2000; //此时m2未加锁,m1加了锁,也可以直接修改b和访问m2 // System.out.println("m2:b = " + b); // //结果: m2:b = 2000 m1:b = 2000 final:b = 2000 // } public synchronized void m2() throws InterruptedException { b = 2000; //此时m2加锁,m1也加了锁,不可以直接修改b和访问m2 System.out.println("m2:b = " + b); //结果: m1:b = 1000 m2:b = 2000 final:b = 2000 } public void run() { try{ m1(); }catch(InterruptedException e){ e.printStackTrace(); } } public static void main(String[] args) throws InterruptedException { TestLockConcept tl = new TestLockConcept(); Thread t = new Thread(tl); t.start(); Thread.sleep(1000); tl.m2(); Thread.sleep(2000); System.out.println("final:b = " + tl.b); } }
六.生产者和消费者
实例:

public class ProductConsumer { public static void main(String[] args) { SyncStack ss = new SyncStack(); Product p = new Product(ss); Consumer c = new Consumer(ss); //一个人每天生产40个馒头供四个人吃,每人吃十个。 new Thread(p).start(); new Thread(c).start(); new Thread(c).start(); new Thread(c).start(); new Thread(c).start(); } } class WoTou { //窝头即是资源,有人生产窝头,有人需要窝头。 int id; WoTou(int id){ this.id = id; } public String toString(){ return "wotou:" + id; } } class SyncStack { //装窝头的篮子,最多只能装6个,提供了往里放窝头以及从里面拿窝头的方法。 int index = 0; WoTou[] arrWT = new WoTou[6]; public synchronized void push(WoTou wt){ //往篮子里放窝头 while(index == arrWT.length){ try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } this.notifyAll(); arrWT[index] = wt; index++; System.out.println("生产了:" + wt); } public synchronized WoTou pop(){ //从篮子里面拿窝头 while(index == 0){ try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } this.notifyAll(); index--; WoTou wt = arrWT[index]; System.out.println("消费了:" + wt); return wt; } } class Product implements Runnable { SyncStack syncStack = null; Product(SyncStack syncStack) { this.syncStack = syncStack; } public void run() { for(int i=0;i<40;i++){ WoTou wt = new WoTou(i); syncStack.push(wt); try { Thread.sleep(200); //隔一阵子生产一个窝头 } catch (InterruptedException e) { e.printStackTrace(); } } } } class Consumer implements Runnable { SyncStack ss = null; Consumer(SyncStack ss) { this.ss = ss; } public void run() { for(int i=0;i<10;i++){ ss.pop(); try{ Thread.sleep(1000); //隔一阵子消费一个窝头 }catch(InterruptedException e){ e.printStackTrace(); } } } }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构