本文最后更新于:2022年3月6日 晚上
概览:Java多线程0
多线程创建的三种方式
创建方式 |
使用场景 |
继承Thread |
单继承 |
实现Runnable |
无返回值任务 |
实现Callable |
有返回值任务 |
1 继承Thread
1 2 3 4 5 6 7 8 9 10 11
| public class MyThread extends Thread{
@Override public void run() { System.out.println(Thread.currentThread().getName() + " 线程启动运行"); }
public static void main(String[] args) { new MyThread().start(); } }
|
- 由于Java只支持单继承,所以一个类一旦继承了Thread类就不能继承其他类了,对于功能扩展有很大的限制,不推荐使用。
2 实现Runnable
1 2 3 4 5 6 7 8 9 10 11 12
| public class TestRunnable implements Runnable{ @Override public void run() { System.out.println(Thread.currentThread().getName() + " 线程启动运行"); }
public static void main(String[] args) { TestRunnable testRunnable = new TestRunnable(); Thread thread = new Thread(testRunnable); thread.start(); } }
|
- Runnable仅仅只是一个接口,没有启动线程的能力,需要搭配之Thread类来使用。
lambda写法
1 2 3
| new Thread(()->{ System.out.println(Thread.currentThread().getName() + " 线程启动运行"); }).start();
|
- 因为Runnable接口是一个函数式接口,可以简写为lambda表达式。
匿名内部类形式
1 2 3 4 5 6
| new Thread(new Runnable(){ @Override public void run() { System.out.println(Thread.currentThread().getName() + " 线程启动运行"); } }).start();
|
3 实现Callable接口
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
| public class TestCallable implements Callable<String> { @Override public String call() throws Exception { return Thread.currentThread().getName() + " 线程启动运行"; }
public static void main(String[] args) { TestCallable testCallable = new TestCallable(); FutureTask<String> futureTask = new FutureTask<>(testCallable); Thread thread = new Thread(futureTask); thread.start(); try { String result = futureTask.get(); System.out.println(result); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } } }
|
- 需要指明类型,即继承时的泛型类型要指明。
- Callable仅仅只是一个接口,没有启动线程的能力,需要搭配之Thread类来使用。
线程的基础
currentThread() 获取当前正在执行的线程
1 2 3
| Thread.currentThread();
System.out.println(Thread.currentThread());
|
- mian表示线程名称:
getName()
- 5表示线程的优先级:
getPriority()
- main表示线程所属的线程组名称:
group.getName()
run() 与 start() 区别
位置
- 均位于Thread类,但run()是重写了Runnable接口
类型
- run()是非同步方法
- start()是同步方法,多个方法同时执行时不会存在线程安全问题
作用
- run()存放任务代码
- start()启动线程,它会自动调用run()方法
线程数量
- run()不会产生新线程
- start()会产生一个新线程
调用次数
- run()可以被调用无数次
- start()被重复调用多次就会出问题,因为线程不能被重复启动。
一个线程对象只能调用一次start方法.从new到等待运行是单行道,所以如果你对一个已经启动的线程对象再调用一次start方法的话,会产生:IllegalThreadStateException异常. 可以被重复调用的是run()方法。
线程优先级
getPriority()
获取到优先级。
setPriority(int)
设置优先级
- 有限级等级从1到10,最低的是1级。有三个常量优先级
MIN_PRIORITY
=1
NORM_PRIORITY
= 5
MAX_PRIORITY
= 10最大优先级
线程休眠 sleep()
- static方法
- 是的当前线程休眠一段时间,放弃CPU的执行权。
线程停止 interrupt()
- interrupt仅仅是将线程标记为中断状态,并没有实际去停止线程。
- 如果想要线程停下来,那么要求我们手动检查线程状态,然后对此做出反应。
isInterrupted()
非静态方法,判断线程是否被中断
interrupted()
,静态方法,判断线程是否被中断,并清除中断标记。
- 中断休眠中的线程将会导致异常的发生。
使正在执行的线程放弃执行权——yield()
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
| class ValueTask implements Runnable{ public static int value = 0; @Override public void run() { try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } value = 100; } }
class PrintTask implements Runnable{
@Override public void run() { while(ValueTask.value == 0){ System.out.println("000"); Thread.yield(); } System.out.println("Value:"+ValueTask.value); } }
public class threadlearn { public static void main(String[] args) { Thread thread1 = new Thread(new ValueTask()); Thread thread2 = new Thread(new PrintTask()); thread1.start(); thread2.start(); } }
|
1 2 3 4 5 6 7 8 9
| while(ValueTask.value == 0){ System.out.println("000"); Thread.yield(); }
while(ValueTask.value == 0){ continue; }
|
代码1和代码2的区别是,代码1会放弃执行权,而代码2则是一直空转,直到条件成立。
等待线程死亡 join()
- join()可以等待线程顺利执行完毕还是中途被中断。
让线程A随着主线程结束而结束
同步
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 31 32 33 34 35
| public class TicketTask implements Runnable {
private int num = 10;
@Override public void run() { while (num > 0) { synchronized (this) { if (num > 0) { System.out.println(num + "号票"); num--; } }
} } }
public class Main {
public static void main(String[] args) { TicketTask task1 = new TicketTask(); Thread t1 = new Thread(task1); Thread t2 = new Thread(task1); Thread t3 = new Thread(task1);
t1.start(); t2.start(); t3.start(); } }
|
同步代码块封装成同步方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| public class TicketTask implements Runnable {
private int num = 10;
@Override public void run() { while (num > 0) { sellTicket(); } }
public synchronized void sellTicket(){ if (num > 0) { System.out.println(num + "号票"); num--; } } }
|
- 一般不要把while循环放入到同步代码块,这意味这可能某个线程就执行完了全部的while循环的代码
- 同步代码块内外双判断!
同步锁:同步锁是为了保证每一个线程都能正常执行原子不可更改操作,同步监听对象、同步锁、同步监听器、互斥锁的一个标记锁。
能够成为锁的类型:
- 对象类型:即new出来的对象
- 类类型:Object.class
同步类型 |
锁类型 |
同步代码块 |
对象类型,this,类类型 |
同步方法 |
this |
静态同步方法 |
类类型 |
线程等待唤醒机制
wait & sleep
- wait是锁方法,位于Object内,sleep方法是线程方法
- wait需要当前线程持有锁,没有拥有锁而调用会使得当前程序发生异常。sleep方法不需要当前线程拥有锁,可以直接调用
- wait支持手动唤醒,notify()和notifyAll()可以唤醒,而sleep方法不能够被手动唤醒
- 两个方法都支持自动唤醒,可以设置超时时间
- 两者都可以支持中断操作,但是调用interrupt()方法之后都会发生InterruptedException线程异常
- wait方法被调用时会立即释放锁,而sleep方法不会释放锁。
- wait有两种状态:WAITING和TIMED_WAITING,而sleep方法调用后只有TIMED_WAITING。
- wait有多个方法,支持手动唤醒(WAITING)和超时唤醒(TIMED_WAITING),
线程间通讯——wait¬ify应用
生产者消费者示例:
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
| public class Produce implements Runnable {
private static final Logger log = LoggerFactory.getLogger(Produce.class);
private Data data;
public Produce(Data data) { this.data = data; }
@Override public void run() { int i = 0; while (true) { synchronized (data){ if (data.getMsg() == null) { log.info("produce# produce a msg:{}", i); data.setMsg("produce a msg " + i++); data.notify(); try { data.wait(); } catch (Exception e) { log.error("err: ", e); } } } } } }
|
消费者示例:
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
| public class Consumer implements Runnable {
private static final Logger log = LoggerFactory.getLogger(Consumer.class);
private Data data;
public Consumer(Data data) { this.data = data; }
@Override public void run() { while (true) { synchronized (data) { if (data.getMsg() != null) { log.info("consumer# consumer a data:{}", data.getMsg()); data.setMsg(null); data.notify(); try { data.wait(); } catch (Exception e) { log.error("err:", e); } } } } } }
|
mian:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| public class PCMain {
public static void main(String[] args) {
Data d1 = new Data(); Produce produce = new Produce(d1); Consumer consumer = new Consumer(d1); Thread t1= new Thread(produce); Thread t2 = new Thread(consumer); t1.start(); t2.start(); } }
|
- 使用同一个对象作为多个不同线程的属性,以此作为他们的共同资源,也可以来充当锁
- wait以及notify都需要锁对象来去调用
- notify之后再调用wait,这种时序上的原理是什么?
锁
Lock显式锁
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| public class TestLock implements Runnable { private Lock lock = new ReentrantLock(); @Override public void run() { lock.lock(); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "lock"); lock.unlock(); } }
public static void main(String[] args) { TestLock task1 = new TestLock();
new Thread(task1).start(); new Thread(task1).start(); }
|
- tryLock()是非阻塞方式获取锁,即尝试获取锁,如果锁可用,则返回true,否则返回false。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| public class TestLockTry implements Runnable { private Lock lock = new ReentrantLock(); @Override public void run() { if(lock.tryLock()){ try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); }finally { lock.unlock(); } System.out.println(Thread.currentThread().getName() + "lock"); }else{ System.out.println(Thread.currentThread().getName() + "未获取到锁"); } } }
|
lock常用子类
- ReentrantLock可重入锁
- ReentrantReadWriteLock.ReadLock:读锁
- ReentrantReadWriteLock.WriteLock:写锁
中断正在等待锁的线程
- synchronized,不能中断等待锁的线程
- Lock.lock(),不能中断等待锁的线程
- Lock.lockInterruptibly(),可以中断
1 2 3 4 5 6 7 8
| @Override public void run() { try { lock.lockInterruptibly(); } catch (InterruptedException e) { System.out.println(Thread.currentThread().getName() + "当前线程被中断!"); } }
|
Lock的等待唤醒机制 Condition
await() 使当前线程等待,直到唤醒或被中断为止
awaitUninterruptibly() 使当前线程等待,直到唤醒为止。
awaitNanos(Long nanosTimeout) 使当前线程等待,直到唤醒或被中断或经过指定的等待时间为止。
await(Long time, TimeUnit unit) 使当前线程等待,直到唤醒或被中断或经过指定的等待时间为止。
awaitUntil(Date deadLine) 使当前线程等待,直到唤醒或被中断或经过指定的等待时间为止。
signal() 唤醒个等 待线程。
signaLAlL() 唤醒所有等待的线程。
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
| public class TestCond implements Runnable{
private Lock lock; private Condition cond;
public TestCond(Lock lock,Condition cond){ this.lock = lock; this.cond = cond; }
@Override public void run() { lock.lock(); try { cond.await(); System.out.println(Thread.currentThread().getName()); } catch (InterruptedException e) { System.out.println("err!"); }finally { lock.unlock(); } } }
public static void main(String[] args) { Lock lock = new ReentrantLock(); Condition cond = lock.newCondition();
TestCond tc = new TestCond(lock,cond); Thread t = new Thread(tc); t.start();
try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } lock.lock(); cond.signal(); lock.unlock(); }
|