Java🎯多线程
|字数总计:4.1k|阅读时长:18分钟|阅读量:
线程
相关概念
- 程序
Program
是为实现特定目标或解决特定问题而用计算机语言编写的命令序列的集合
- 进程
Process
- 是程序关于某个数据集合上的一次运行活动(对应一个
exe
),是独立运行的程序(正在运行的程序);对应了从代码加载、执行至执行完毕的一个完整过程;
- 目前操作系统都是支持多进程,可以同时执行多个进行,通过进程
PID
区分
- 是系统进行资源分配和调度的一个独立单位

- 线程
Thread
- 是进程的一个实体,CPU调度和分派的基本单位,是比进程更小的、能独立运行的基本单位
- 一个应用程序中有多条并发执行的线索,每条线索都被称为一个线程。各线程交替执行,彼此间可以通信

- 线程与进程的关系
- 一个线程只能属于一个进程,而一个进程可以有多个线程,但至少有一个线程
- 操作系统把资源分配给进程,而同一进程的所有线程共享该进程的所有资源
- 进程是拥有资源的基本单位,而线程是作为CPU调度和分配的基本单位
创建线程
- 创建线程的三种方式
- 继承
Thread
类,重写run
方法
- 实现
Runnable
接口
- 实现
Callable
接口
继承Thread

1 2 3 4 5 6 7 8 9
| public class MyThread extends Thread{ @Override public void run() { for(int i=0;i<100;i++){ System.out.println("子线程....."+i); } } }
|
1 2 3 4 5 6 7 8 9 10 11 12
| public class Demo01{ public static void main(String[] args) { MyThread myThread = new MyThread(); myThread.start(); for(int i=0;i<50;i++){ System.out.println("主线程===="+i); } } }
|
由于线程运行是抢占式的,两个线程执行结果顺序是不同的
获取线程名称
- 第一种方法:在Thread的子类中调用
this.getId()
或this.getName()
1 2 3 4 5 6 7 8 9 10 11
| public class MyThread extends Thread{ @Override public void run() { for(int i=0;i<100;i++){ System.out.println( "线程id:"+this.getId()+ "线程名称"+this.getName()+ "子线程....."+i); } } }
|
- 第二种方法【推荐】:使用
Thread.currentThread().getId()
和Thread.currentThread().getName()
1 2 3 4 5 6 7 8 9 10 11 12
| public class MyThread extends Thread{ @Override public void run() { for(int i=0;i<100;i++){ System.out.println( "线程id:"+Thread.currentThread().getId()+ "线程名称"+Thread.currentThread().getName()+ "子线程....."+i); } } }
|
修改线程名称
1 2 3 4
| MyThread myThread1 = new MyThread();
myThread1.setName("子线程1"); myThread1.start();
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| public class MyThread extends Thread{ public MyThread() { } public MyThread(String name) { super(name); } @Override public void run() { for(int i=0;i<100;i++){ System.out.println( "线程id:"+Thread.currentThread().getId()+ "线程名称"+Thread.currentThread().getName()+ "子线程....."+i); } } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13
| public class Demo01{ public static void main(String[] args) { MyThread myThread1 = new MyThread("子线程1"); myThread1.start(); MyThread myThread2 = new MyThread("子线程2"); myThread2.start(); for(int i=0;i<50;i++){ System.out.println("主线程===="+i); } } }
|
卖票案例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| public class TicketWin extends Thread{ private int ticket = 100; public TicketWin(){
} public TicketWin(String name){ super(name); } @Override public void run() { while(true){ if(ticket<=0){ break; } System.out.println(Thread.currentThread().getName()+"卖了第"+ticket+"张票"); ticket--; } } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| public class Demo01{ public static void main(String[] args) { TicketWin w1 = new TicketWin("窗口1"); TicketWin w2 = new TicketWin("窗口2"); TicketWin w3 = new TicketWin("窗口3"); TicketWin w4 = new TicketWin("窗口4"); w1.start(); w2.start(); w3.start(); w4.start(); } }
|
实现Runnable接口

1 2 3 4 5 6 7 8
| public class MyRunnable implements Runnable{ @Override public void run() { for(int i=0;i<100;i++){ System.out.println(Thread.currentThread().getName()+"====="+i); } } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13
| public class Demo01{ public static void main(String[] args) { MyRunnable myRunnable = new MyRunnable(); Thread thread = new Thread(myRunnable,"线程1"); thread.start(); for(int i=0;i<50;i++){ System.out.println("主线程......."+i); } } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| public class Demo01{ public static void main(String[] args) { Runnable runnable = new Runnable() { @Override public void run() { for(int i=0;i<100;i++){ System.out.println(Thread.currentThread().getName()+"====="+i); } } }; Thread thread = new Thread(runnable,"线程1"); thread.start(); } }
|
线程状态
基本状态

线程休眠
public static void sleep(long millis)
当前线程主动休眠,单位为毫秒数。静态方法可以通过类名调用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| public class MyThread extends Thread{ public MyThread() { } public MyThread(String name) { super(name); } @Override public void run() { for(int i=0;i<100;i++){ System.out.println( "线程id:"+Thread.currentThread().getId()+ "线程名称"+Thread.currentThread().getName()+ "子线程....."+i); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } }
|
1 2 3 4 5 6 7 8
| public class Demo01{ public static void main(String[] args) { MyThread myThread = new MyThread(); myThread.start(); MyThread myThread1 = new MyThread(); myThread1.start(); } }
|
线程放弃
public static void yield()
当前线程主动放弃时间片,回到就绪状态,竞争下一次时间片
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| public class MyThread extends Thread{ public MyThread() { } public MyThread(String name) { super(name); } @Override public void run() { for(int i=0;i<100;i++){ System.out.println( "线程id:"+Thread.currentThread().getId()+ "线程名称"+Thread.currentThread().getName()+ "子线程....."+i); Thread.yield(); } } }
|
1 2 3 4 5 6 7 8
| public class Demo01{ public static void main(String[] args) { MyThread myThread = new MyThread(); MyThread myThread1 = new MyThread(); myThread.start(); myThread1.start(); } }
|
线程加入
public final void join()
允许其他线程加入到当前线程中,不是静态方法,需要创建实例调用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| public class MyThread extends Thread{ public MyThread() { } public MyThread(String name) { super(name); } @Override public void run() { for(int i=0;i<100;i++){ System.out.println( "线程id:"+Thread.currentThread().getId()+ "线程名称"+Thread.currentThread().getName()+ "子线程....."+i); try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } } } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| public class Demo01{ public static void main(String[] args) { MyThread myThread = new MyThread(); myThread.start(); for(int i=0;i<20;i++){ System.out.println(Thread.currentThread().getName()+"====="+i); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } } }
|
未使用join
方法时,两个线程是差不多交替执行的,并没有出现阻塞的情况
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| public class Demo01{ public static void main(String[] args) { MyThread myThread = new MyThread(); myThread.start(); try { myThread.join(); } catch (InterruptedException e) { e.printStackTrace(); } for(int i=0;i<20;i++){ System.out.println(Thread.currentThread().getName()+"====="+i); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } } }
|
使用join
方法后,主线程被阻塞了,必须使子线程运行完后才能运行主线程
线程优先级
- 设置线程优先级
- 使用线程对象调用
setPriority()
方法即可
- 线程优先级为1-10,默认为5,优先级越高,表示获取CPU机会越多
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| public class MyThread extends Thread{ public MyThread() { } public MyThread(String name) { super(name); } @Override public void run() { for(int i=0;i<100;i++){ System.out.println( "线程id:"+Thread.currentThread().getId()+ "线程名称"+Thread.currentThread().getName()+ "子线程....."+i); } } }
|
1 2 3 4 5 6 7 8 9 10 11 12
| public class Demo01{ public static void main(String[] args) { MyThread myThread = new MyThread("线程1"); MyThread myThread1 = new MyThread("线程2"); MyThread myThread2 = new MyThread("线程3"); myThread.setPriority(1); myThread2.setPriority(10); myThread.start(); myThread1.start(); myThread2.start(); } }
|
运行结果发现,线程3的执行顺序要靠前,也就是其优先级高,优先抢占CPU
守护线程
- 设置守护线程
- 使用线程对象调用
setDaemon(true)
设置为守护线程
- 线程有两类:用户线程(前台线程)、守护线程(后台线程)
- 如果程序中所有前台线程都执行完毕了,后台线程会自动结束。垃圾回收器线程属于守护线程
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| public class MyThread extends Thread{ public MyThread() { } public MyThread(String name) { super(name); } @Override public void run() { for(int i=0;i<100;i++){ System.out.println( "线程id:"+Thread.currentThread().getId()+ "线程名称"+Thread.currentThread().getName()+ "子线程....."+i); try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } } } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| public class Demo01{ public static void main(String[] args) { MyThread myThread = new MyThread("线程1"); myThread.setDaemon(true); myThread.start(); for(int i=0;i<10;i++){ System.out.println("主线程:-----"+i); try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } } } }
|
未设置为守护线程时,线程直到循环完才结束,设置为守护线程后,主线程运行完毕,守护线程就不再执行了
线程等待

线程同步
同步代码块
- 多线程安全问题
- 当多线程并发访问临界资源时,如果破坏原子操作,可能会造成数据不一致
- 临界资源:共享资源(同一对象),一次仅允许一个线程使用,才可保证其正确性
- 原子操作:不可分割的多步操作,被视作一个整体,其顺序和步骤不可打乱或缺省
- 同步代码块
- 每个对象都有一个互斥锁标记,用来分配给线程的
- 只有拥有对象互斥锁标记的线程,才能进入对该对象加锁的同步代码块
- 线程退出同步代码块时,会释放相应的互斥锁标记
1 2 3 4 5 6 7 8 9 10 11
| public class BankCard { private double money;
public double getMoney() { return money; }
public void setMoney(double money) { this.money = money; } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| public class testDemo { public static void main(String[] args) { final BankCard card = new BankCard(); Runnable add = new Runnable() { @Override public void run() { for(int i=0;i<10;i++){ synchronized (this) { card.setMoney(card.getMoney() + 1000); System.out.println(Thread.currentThread().getName() + "存了1000,余额是:" + card.getMoney()); } } } }; Runnable sub = new Runnable() { @Override public void run() { for(int i=0;i<10;i++){ synchronized (this) { if (card.getMoney() >= 1000) { card.setMoney(card.getMoney() - 1000); System.out.println(Thread.currentThread().getName() + "取了1000,余额是:" + card.getMoney()); } else { System.out.println("余额不足,请存钱"); i--; } } } } }; Thread xiaoli = new Thread(add,"小李"); Thread xiaoming = new Thread(sub,"小明"); xiaoli.start(); xiaoming.start(); } }
|
同步方法
1 2 3
| synchronized 返回值类型 方法名称(形参列表){ }
|
- 只有拥有对象互斥锁标记的线程,才能进入该对象加锁的同步方法中
- 线程退出同步方法时,会释放相应的互斥锁标记
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| public class TicketWin extends Thread{ private int ticket = 100; private Object obj = new Object(); public TicketWin(){
} public TicketWin(String name){ super(name); } @Override public void run() { while(true){ if(!sale()){ break; } } } public synchronized boolean sale(){ if(ticket<=0){ return false; } System.out.println(Thread.currentThread().getName()+"卖了第"+ticket+"张票"); ticket--; return true; } }
|
线程通信
- 等待:
public final void wait()
public final void wait (long timeout)
- 必须在对obj加锁的同步代码块中。在一个线程中,调用
obj.wait()
时,此线程会释放其拥有的所有锁标记。同时此线程阻塞在obj的等待队列中。释放锁,进入等待队列。
- 通知:
public final void notify()
public final void notifyAll()
注意:一定是锁.wait()
或者锁.notify()
存钱取钱
1 2 3 4 5 6 7 8 9 10 11 12
| public class AddMoney implements Runnable{ private BankCard card; public AddMoney(BankCard card){ this.card=card; } @Override public void run() { for(int i=0;i<10;i++){ card.save(1000); } } }
|
1 2 3 4 5 6 7 8 9 10 11 12
| public class SubMoney implements Runnable{ private BankCard card; public SubMoney(BankCard card){ this.card=card; } @Override public void run() { for(int i=0;i<10;i++){ card.take(1000); } } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| public class BankCard { private double money; private boolean flag; public double getMoney() { return money; }
public void setMoney(double money) { this.money = money; } public synchronized void save(double m){ while(flag){ try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } money=money+m; System.out.println(Thread.currentThread().getName()+"存了"+m+"余额是"+getMoney()); flag=true; this.notify(); } public synchronized void take(double m){ while(!flag){ try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } money=money-m; System.out.println(Thread.currentThread().getName()+"取了"+m+"余额是"+getMoney()); flag=false; this.notify(); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| public class testDemo { public static void main(String[] args) { BankCard card = new BankCard(); AddMoney add = new AddMoney(card); SubMoney sub = new SubMoney(card); Runnable target; Thread xiaoli = new Thread(add,"小李"); Thread xiaowang = new Thread(sub,"小王"); xiaoli.start(); xiaowang.start(); } }
|
执行结果是存一次然后取一次,线程之间有明显的执行顺序
生产者消费者问题
- 若干个生产者在生产产品,这些产品将提供给若干个消费者去消费,为了使生产者和消费者能并发执行,在两者之间设置一个能存储多个产品的缓冲区,生产者将生产的产品放入缓冲区中,消费者从缓冲区中取走产品进行消费,显然生产者和消费者之间必须保持同步,即不允许消费者到一个空的缓冲区中取产品,也不允许生产者向一个满的缓冲区中放入产品
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| public class Bread { private int id; private String productName;
public Bread() { }
public Bread(int id, String productName) { this.id = id; this.productName = productName; }
public int getId() { return id; }
public void setId(int id) { this.id = id; }
public String getProductName() { return productName; }
public void setProductName(String productName) { this.productName = productName; } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| public class BreadCon { private Bread[] cons = new Bread[6]; private int index = 0; public synchronized void input(Bread bread){ while(index>=6){ try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } cons[index]=bread; System.out.println(Thread.currentThread().getName()+"生产了"+ bread.getId()); index++; this.notifyAll(); } public synchronized void output(){ while(index<=0){ try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } index--; Bread bread = cons[index]; System.out.println(Thread.currentThread().getName()+"消费了"+ bread.getId()+"生产者:"+bread.getProductName()); cons[index] = null; this.notifyAll(); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| public class Product implements Runnable{ private BreadCon con;
public Product(BreadCon con) { this.con = con; }
@Override public void run() { for(int i=0;i<30;i++){ con.input((new Bread(i,Thread.currentThread().getName()))); } } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| public class Consume implements Runnable { private BreadCon con;
public Consume(BreadCon con) { this.con = con; }
@Override public void run() { for(int i=0;i<30;i++){ con.output(); } } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| public class testDemo { public static void main(String[] args) { BreadCon con = new BreadCon(); Product product = new Product(con); Consume consume = new Consume(con); Runnable target; Thread chenchen = new Thread(product,"晨晨"); Thread bingbing = new Thread(consume,"冰冰"); chenchen.start(); bingbing.start(); } }
|