Java多线程
多线程的缘起:进程的创建开销太大,进程内部程序还可以再并发,需要在此基础上进一步划分以提高并发。
1、多线程基础
①Thread多线程的实现:继承Thread类后改写run方法。

class Student extends Thread{ String name; Student(String name){ this.name = name; } public void run(){ for(int i = 0; i < 5; i++) System.out.println(String.format("%-10s%d", name, i)); } } public class test { public static void main(String[] args) throws Exception { new Student("hi").start(); new Student("hey").start(); new Student("hello").start(); } }
问题:为什么不是new MyThread().run()执行?
-
- 因为多线程程序执行涉及到cpu资源分配,这是一个系统调用函数;
- 不同操作系统的系统调用不同,需要JVM的额外处理。所以需要再start函数中做一些额外处理,而不能把run当成普通函数直接调用执行。
②Runnable方式:MyThread继承Runnable接口实现run方法,以MyThread初始化Thread类new Thread(MyThread)。使用接口可以避免单继承的局限,且方便数据共享!

class MyTicket implements Runnable{ private int ticket = 10; public void run(){ for (int i = 0; i < 20; i++) { if(ticket > 0) System.out.println("买票 =" + this.ticket--); } } } public class test { public static void main(String[] args) { MyTicket mt = new MyTicket(); new Thread(mt).start(); new Thread(mt).start(); new Thread(mt).start(); } }
常用方法:
Thread.sleep(1000); ms
Thread.yield(); 临时暂停,为使其它线程抢占cpu资源
p.start();p.join(); join表示等p运行完后main主线程才开始运行
Thread.currentThread().getName(); 当前线程名称
setPriority(Thread.MAX_PRIORITY); MIN_PRIORITY 1,NORM_PRIORITY 5,MAX_PRIORITY 10
2、线程同步-临界资源互斥访问
概念:应对多线程同时修改一个数据时可能出现的问题。
注意:run函数中访问的全局变量是final类型,输出10001或9999等。
问题:9999,当前10000+1add运算还未结束时,sub运算来了 在10000上减一,于是数值由10000 -> 10001 -> 9999。

class Student{ int number; Student(int number){ this.number = number; } public void sub(){ this.number --; } public void add(){ this.number ++; } } public class test { public static void main(String[] args) throws Exception { final Student s = new Student(10000); int n = 9000; Thread[] addThreads = new Thread[n]; Thread[] subThreads = new Thread[n]; for (int i = 0; i < n; i++) { Thread t = new Thread( ()->{s.sub(); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } ); t.start(); addThreads[i] = t; } for (int i = 0; i < n; i++) { Thread t = new Thread( ()->{s.add(); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } ); t.start(); subThreads[i] = t; } for(Thread t : addThreads) t.join(); for(Thread t : subThreads) t.join(); System.out.printf("Student's number is %d!\n" , s.number); } }
解决①:synchronized(object){...object...} 表示当前进程独占改对象,其它进程访问时则等待。
1)Thread t = new Thread( ()->{ synchronized (s){ s.sub(); } } ); 2)public void add(){ synchronized (this) { this.number++; } }
3)public synchronized void add(){
...
}
解决②:Lock lock = new ReentrantLock(); lock.lock(); lock.unlock();
Lock和synchronized都是一直请求占用对象,lock.tryLock(1, TimeUnit.SECONDS)只坚持请求1秒。
比较Lock synchronized
接口 关键字
有tryLock()方法 循环等待占有
手动释放锁 同步结束和异常时自动释放锁

class Student{ int number; Student(int number){ this.number = number; } public void sub(){ this.number --; System.out.println("sub, this number is :\t" + this.number); } public void add(){ this.number ++; System.out.println("add, this.number is :\t" + this.number); } } public class test { public static void main(String[] args) throws Exception { Lock lock = new ReentrantLock(); Condition condition = lock.newCondition(); final Student s = new Student(1); int n = 6; Thread[] addThreads = new Thread[n]; Thread[] subThreads = new Thread[n]; for (int i = 0; i < n; i++) { Thread t = new Thread( ()->{ lock.lock(); try { if(s.number <= 1) condition.await(); } catch (InterruptedException e) { e.printStackTrace(); } s.sub(); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } lock.unlock(); } ); t.start(); addThreads[i] = t; } for (int i = 0; i < n; i++) { Thread t = new Thread( ()->{ lock.lock(); s.add(); if(s.number > 1) condition.signal(); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } lock.unlock(); } ); t.start(); subThreads[i] = t; } for(Thread t : addThreads) t.join(); for(Thread t : subThreads) t.join(); // System.out.printf("Student's number is %d!\n" , s.number); } }
解决③:原子访问/原子操作 是不可中断的操作,如i++由3个原子操作组成 不是线程安全的。
AtomicInteger atomicI =new AtomicInteger(10); int i = atomicI.decrementAndGet(); // 9 int j = atomicI.incrementAndGet(); // 10 int k = atomicI.addAndGet(3); // 13

public class test { static int value1 = 0; static AtomicInteger value2 =new AtomicInteger(0); public static void main(String[] args) throws Exception { int n = 100000; Thread[] ts1 = new Thread[n], ts2 = new Thread[n]; for(int i = 0; i < n; i++){ Thread t = new Thread( ()->{ value1++; } ); t.start(); ts1[i] = t; } for(int i = 0; i < n; i++) ts1[i].join(); for(int i = 0; i < n; i++){ Thread t = new Thread( ()->{ value2.incrementAndGet(); } ); t.start(); ts2[i] = t; } for(int i = 0; i < n; i++) ts2[i].join(); System.out.printf("after increase %d, the value1 and value2 are :%d\t%d", n, value1, value2.intValue()); } }
3、线程安全类(所有方法都用synchronized修饰)
HashTable HashMap
线程安全 线程不安全(但速度快很多)
不允许null的k-v 允许k或v为null
hash=key.hashCode() (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
扩容: 原容量2倍 + 1 原容量2倍(于hash计算方式有关)
hash冲突:链表 链表长度大于8时转红黑树
StringBuffer StringBuilder
Vector ArrayList
非安全转安全集合:借助Collections.synchronizedList、-Map、-Set将ArrayList、HashMap、HashSet、LinkedList安全化。
List<Integer> list1 = new ArrayList<>(); List<Integer> list2 = Collections.synchronizedList(list1);
死锁:不可剥夺、请求保持、循环等待、互斥访问

class Student{ String name; Student(String name) { this.name = name; } } public class test { public static void main(String[] args) throws Exception { final Student s1 = new Student("hi"), s2 = new Student("hello"); new Thread( ()->{ synchronized (s1){ System.out.println("A\t已经占有s1"); try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("A\t正在等待占有s2..."); synchronized (s2){System.out.println("A\t已经占有s2");}; }; }).start(); new Thread( ()->{ synchronized (s2){ System.out.println("B\t已经占有s2"); try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("B\t正在等待占有s1..."); synchronized (s1){System.out.println("B\t已经占有s1");}; }; }).start(); } }
4、线程交互
synchronized + this.wait() + this.notify() 和 Lock、await()、signal()
this.wait() 让占有该this的线程等待 并释放synchronized的临时占有。
this.notify() 通知那些等待在this的线程 可以苏醒了。。。this.notify()只通知一个,notifyAll()通知所有。

class Student{ int number; Student(int number){ this.number = number; } public synchronized void sub() throws InterruptedException { if(this.number <= 1) this.wait(); this.number --; System.out.println("sub: the number is " + this.number); } public synchronized void add(){ this.number ++; this.notify(); System.out.println("add: the number is " + this.number); } } public class test { public static void main(String[] args) throws Exception { final Student s = new Student(2); int n = 5; Thread[] addThreads = new Thread[n]; Thread[] subThreads = new Thread[n]; for (int i = 0; i < n; i++) { Thread t = new Thread( ()->{ synchronized (s){ try { s.sub(); } catch (InterruptedException e) { e.printStackTrace(); } try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } } } ); t.start(); addThreads[i] = t; } for (int i = 0; i < n; i++) { Thread t = new Thread( ()->{ synchronized (s){s.add(); try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } } } ); t.start(); subThreads[i] = t; } for(Thread t : addThreads) t.join(); for(Thread t : subThreads) t.join(); } }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix