Java 线程
这玩意绝对很重要,因为经常听到多线程这个词,而且也听说很难
创建线程
创建线程有两种方法,一种是创建继承Thread的子类,一种是创建实现Runnable接口的实现类
方法一:
步骤:
- 创建一个 Thread 子类
- 在Thread类的子类中重写Thread的run方法,设置线程任务
- 创建Thread子类对象
- 调用Thread类中的start方法,开启新的线程执行run方法
实例:
1 2 3 4 5 6 7 8
| public class myThread extends Thread{ @Override public void run() { for (int i = 0; i < 10; i++) { System.out.println("run:" + i); } } }
|
启动线程:
1 2 3 4 5 6 7
| myThread mt = new myThread(); mt.start(); myThread mt1 = new myThread(); mt1.start(); for (int i = 0; i < 10; i++) { System.out.println("润:" + i); }
|
使用对象的start方法就是在调用对象的run方法,于是执行了重写的run方法
方法二:
步骤:
- 创建Runnable接口的实现类
- 重写接口的run方法
- 创建实现类对象
- 创建Thread类对象,构造方法中传入Runnable接口的实现类对象
- 调用Thread类中的start方法,开启新的线程执行run方法
实例:
1 2 3 4 5 6 7 8
| public class RunnableImpl implements Runnable{ @Override public void run() { for (int i = 0; i < 20; i++) { System.out.println(Thread.currentThread().getName()); } } }
|
启动线程:
1 2 3
| RunnableImpl run = new RunnableImpl(); Thread th = new Thread(run); th.start();
|
- 使用Runnable接口的优点就是一个类只能继承自一个父类,但能继承多个接口,如果使用继承Thread的方法,则无法继承其他的类了
获取线程名称的方法:
currentThread()方法返回当前正在执行的进程对象的引用,获取主线程的线程名可以通过 Thread.currentThread().getName() 来获取
线程获取线程名的方法可以直接调用 getName() 方法,返回字符串
1 2 3 4 5 6 7 8 9
| public class myThread extends Thread{ @Override public void run() { String name = getName(); System.out.println(name); Thread name1 = currentThread(); System.out.println(name1); } }
|
启动线程:
1 2 3 4 5
| myThread mt = new myThread(); mt.start(); myThread mt1 = new myThread(); mt1.start(); System.out.println(Thread.currentThread().getName());
|
通过currentThread()方法可以看出,调用这个线程的是主线程
线程安全
多个线程访问共享数据的时候,会出现线程安全问题,所以需要让线程进行某些操作的时候,同一时间只能有一个进程在工作
有两种方法实现:第一种使用synchronized同步代码块,第二种使用Lock锁
方法一:
格式:
原理: 抢夺到了同步块的执行权,会把锁对象拿走,然后执行同步代码块,执行完了就释放锁对象,其他线程执行到此处发现没有锁对象,没有执行权,会一直等待锁对象归还
锁对象我认为只要 Object 的对象都可以,此处锁对象可以使用一个有意义的对象来控制进程的调用
synchronized可以实现同步代码块,也可以实现同步方法
同步代码块实例:
1 2 3 4
| Object obj = new Object(); synchronized (obj){ System.out.println(Thread.currentThread()); }
|
同步方法实例:
1 2 3 4 5 6 7 8 9 10 11
| public class RunnableImpl implements Runnable{ @Override public void run() { while (ticket>0){ payTicket(); } } public synchronized void payTicket(){ System.out.println(Thread.currentThread()); } }
|
方法二:
Lock锁解决线程安全,比synchronized更好用
Lock接口的方法:
void lock()获取锁
void unlock()释放锁
步骤:
步骤
- 在类的成员位置创建一个ReentrantLock对象
- 在可能出现安全问题的代码前调用Lock接口的lock方法
- 在可能出现安全问题的代码后调用Lock接口的unlock方法
1 2 3 4 5 6 7 8 9 10 11 12
| public class RunnableImpl implements Runnable{ Lock l = new ReentrantLock();
@Override public void run() { l.lock(); while (true){ System.out.println(Thread.currentThread().getName()); } l.unlock(); } }
|
同步和锁都是让这部分代码一个时间只能有一个线程去执行,这样就不会出现线程问题了
等待(wait)和唤醒(notify)
这里就可以用到同步锁对象来做到让线程等待和唤醒其他线程了
调用wait方法会使该线程进入等待状态,并且会释放被同步对象的锁
notify操作可以唤醒一个因执行wait而处于阻塞状态的线程,使其进入就绪状态,被唤醒的线程等待锁对象被释放后,可以尝试获得锁对象,如果获得了,则执行wait之后的代码
多线程实例,包子铺和吃货
1 2 3 4 5
| public class BaoZi { String pi; String xian; boolean flag = false; }
|
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
| public class BaoZiPu extends Thread{ private final BaoZi baozi; private int count = 0; public BaoZiPu(BaoZi bz){ this.baozi = bz; }
@Override public void run() { while (true){ synchronized (baozi){ if (baozi.flag){ try { baozi.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } if (count % 2==0){ baozi.pi="薄皮"; baozi.xian="三鲜"; } else { baozi.pi="冰皮"; baozi.xian="牛肉"; } count++; System.out.println("包子铺正在生产:" + baozi.pi + baozi.xian + "包子"); System.out.println("生产包子需要3s"); try { sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } baozi.flag=true; baozi.notify(); System.out.println("包子铺已经生产好了" + baozi.pi + baozi.xian + "包子,可以吃了"); } } } }
|
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
| public class ChiHuo extends Thread{ private BaoZi baozi; public ChiHuo(BaoZi bz){ this.baozi=bz; } @Override public void run() { while (true){ synchronized (baozi){ if (!baozi.flag){ try { baozi.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("吃货正在吃" + baozi.pi + baozi.xian + "的包子"); System.out.println("吃包子需要3s"); try { sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } baozi.flag=false; baozi.notify(); System.out.println("吃货已经吃掉了" + baozi.pi + baozi.xian + "的包子"); System.out.println("=============================="); } } } }
|
1 2 3 4 5 6 7
| public class demo { public static void main(String[] args) { BaoZi baozi = new BaoZi(); new BaoZiPu(baozi).start(); new ChiHuo(baozi).start(); } }
|
多线程真的让人头秃,看了课之后又多看了几遍代码,然后就加上了注释,也就是自己的理解,途中还翻了许多大佬的博客来验证自己的理解是不是正确的,现在来说,理解算是接近正确的了吧 ٩(๑❛ᴗ❛๑)۶