概述
进程和线程是什么
进程是一个应用程序,可启动多个线程
线程是进程中的执行单元
DOS输入 java HelloWorld 后发生了什么
- 先启动JVM,JVM就是一个进程
- JVM再启动一个主线程调用main方法
- 同时启动一个垃圾回收线程负责看护,回收垃圾
- 此时有两个线程并发
进程和线程是什么关系
- 两进程之间内存独立不共享
- 两线程之间堆内存和方法区内存共享,栈内存独立不共享,多个线程启动多个栈,就是多线程并发,可以提高效率
- main方法结束后只是主线程结束了,其他线程可能还在压栈弹栈
单核CPU不能真正做到多线程并发
- 什么是真正的多线程并发?
t1线程执行t1的
t2线程执行t2的
t1不会影响t2,t2也不会影响t1。
- 单核的CPU表示只有一个大脑:不能够做到真正的多线程并发,但是可以做到给人一种”多线程并发”的感觉,因为人类反应速度较慢
实现线程的方式
继承java.lang.Thread,重写run()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| public class _603_实现线程的第一种方式 { public static void main(String[] args) { MyThread myThread = new MyThread(); myThread.start(); for (int i = 0; i < 1000; i++) { System.out.println("主线程 -- > " + i); } } }
class MyThread extends Thread{ @Override public void run() { System.out.println("MyThread run begin~"); for (int i = 0; i < 1000; i++) { System.out.println("分支线程 -- > " + i); } } }
|
实现java.lang.Runnable接口,实现run()
面向接口编程,使用较多
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| public class _605_实现线程的第二种方法 { public static void main(String[] args) { Thread thread = new Thread(new MyRunnable()); thread.start(); for (int i = 0; i < 1000; i++) { System.out.println("主线程 -- > " + i); } } }
class MyRunnable implements Runnable{ @Override public void run() { System.out.println("MyThread run begin~"); for (int i = 0; i < 1000; i++) { System.out.println("分支线程 -- > " + i); } } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| public class _606_采用匿名内部类方式 { public static void main(String[] args) { Thread thread = new Thread(new Runnable() { @Override public void run() { for (int i = 0; i < 1000; i++) { System.out.println("分支线程 -- > " + i); } } }); thread.start(); for (int i = 0; i < 1000; i++) { System.out.println("主线程 -- > " + i); } } }
|
线程生命周期
这儿因该有张图…
线程相关方法
public final synchronized void setName(String name)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| public class _609_获取线程名字 { public static void main(String[] args) { MyThread2 myThread2 = new MyThread2(); myThread2.setName("线程"); System.out.println(myThread2.getName()); myThread2.start(); } }
class MyThread2 extends Thread{ @Override public void run() { for (int i = 0; i < 100; i++) { System.out.println("分支线程 -> " + i); } } }
|
public final String getName()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| public class _609_获取线程名字 { public static void main(String[] args) { MyThread2 myThread2 = new MyThread2(); myThread2.setName("线程"); System.out.println(myThread2.getName()); myThread2.start(); } }
class MyThread2 extends Thread{ @Override public void run() { for (int i = 0; i < 100; i++) { System.out.println("分支线程 -> " + i); } } }
|
public static native Thread currentThread();
出现在哪就是获取当前线程对象
1 2 3 4 5 6 7 8 9
| public class _610_获取当前线程对象 { public static void main(String[] args) { Thread currentThread = Thread.currentThread(); System.out.println(currentThread.getName()); } }
|
public static native void sleep(long millis) throws InterruptedException
1 2 3 4 5 6 7 8 9 10 11 12 13
| public class _611_线程sleep方法 { public static void main(String[] args) { try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); }
System.out.println("五秒之后的输出~"); } }
|
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 _612_sleep方法的面试题 { public static void main(String[] args) { MyThread3 myThread3 = new MyThread3(); myThread3.start();
try { myThread3.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("HELLO WORLD~"); } }
class MyThread3 extends Thread{ @Override public void run() { for (int i = 0; i < 1000; i++) { System.out.println(Thread.currentThread().getName() + "->" + i); } } }
|
public void interrupt()
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 _613_终止线程的睡眠 { public static void main(String[] args) { Thread thread = new Thread(new MyRunnable2()); thread.start();
try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); }
thread.interrupt();
} }
class MyRunnable2 implements Runnable{ @Override public void run() { System.out.println(Thread.currentThread().getName() + "-> begin"); try { Thread.sleep(100000000); Thread.sleep(1000000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "-> end"); } }
|
public final void stop()
终止一个线程的执行
相当于突然断点,内存中的东西没有保存,不建议使用
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
| public class _614_强行终止线程的执行 { public static void main(String[] args) { Thread thread = new Thread(new MyRunnable3()); try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } Thread.stop(); System.out.println("=========="); } }
class MyRunnable3 implements Runnable{ @Override public void run() { for (int i = 0; i < 10; i++) { System.out.println(Thread.currentThread().getName() + "-> " + i); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } }
|
合理的终止一个线程的执行
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 _615_合理的终止一个线程的执行 { public static void main(String[] args) { MyRunnable4 myRunnable4 = new MyRunnable4(); Thread thread = new Thread(myRunnable4); thread.start(); try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } myRunnable4.run = false;
} }
class MyRunnable4 implements Runnable{ boolean run = true; @Override public void run() { for (int i = 0; i < 10; i++) { if(run){ System.out.println(Thread.currentThread().getName() + "->" + i); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } else{
return; } } } }
|
线程调度
线程调度模型
- 抢占式调度模型:按照线程的优先级来看,优先级越高,强盗CPU时间片的概率就高一些,java采用的就是这种
- 均分式调度模型:平均分配CPU时间片,每个线程占用时间片时间长度相同
线程调度方法
public final int getPriority()
线程最低优先级是1,最高是10,默认是5
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| public class _618_线程优先级 { public static void main(String[] args) { System.out.println("线程最高优先级" + Thread.MAX_PRIORITY); System.out.println("线程最低优先级" + Thread.MIN_PRIORITY); System.out.println("线程默认优先级" + Thread.NORM_PRIORITY);
Thread thread = Thread.currentThread(); System.out.println(thread.getPriority());
} }
|
public final void setPriority(int newPriority)
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 _618_线程优先级 { public static void main(String[] args) { System.out.println("线程最高优先级" + Thread.MAX_PRIORITY); System.out.println("线程最低优先级" + Thread.MIN_PRIORITY); System.out.println("线程默认优先级" + Thread.NORM_PRIORITY);
Thread thread = Thread.currentThread(); System.out.println(thread.getPriority()); thread.setPriority(1);
Thread t = new Thread(new MyRunnable5()); t.start();
for (int i = 0; i < 100; i++) { System.out.println(Thread.currentThread().getName() + "->" + i); } } }
class MyRunnable5 implements Runnable{ @Override public void run() { for (int i = 0; i < 100; i++) { System.out.println(Thread.currentThread().getName() + "->" + i); } } }
|
public static native void yield()
让位,当前线程暂停,回到就绪状态,让给其它线程
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| public class _619_线程让位 { public static void main(String[] args) { Thread t = new Thread(new MyRunnable6()); t.start();
for (int i = 0; i < 1000; i++) { System.out.println(Thread.currentThread().getName() + "->" + i); } } }
class MyRunnable6 implements Runnable{ @Override public void run() { for (int i = 0; i < 1000; i++) { System.out.println(Thread.currentThread().getName() + "->" + i); if(i%10 == 0){ Thread.yield(); } } } }
|
public final void join() throws InterruptedException
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
| public class _620_线程合并 { public static void main(String[] args) { System.out.println("main begin~"); Thread t = new Thread(new MyRunnable7()); t.start();
try { t.join(); } catch (InterruptedException e) { e.printStackTrace(); }
System.out.println("main over~"); } }
class MyRunnable7 implements Runnable{ @Override public void run() { for (int i = 0; i < 100; i++) { System.out.println(Thread.currentThread().getName() + "->" + i); } } }
|
线程安全
什么情况出现线程安全问题
- 多线程并发
- 有共享数据
- 共享数据有修改的行为
(这儿应该有张图…)
如何解决线程安全问题
线程排队执行,这种机制被称为:线程同步机制。也就是线程不能并发,必须排队执行,为了安全牺牲效率。
同步和异步的理解
- 异步编程模型:线程t1和t2各自执行各自的,就是多线程并发
- 同步编程模型:线程t1和t2在执行的过程中发生等待关系,线程排队执行
定义账户类模拟两线程取钱 synchronized
使用 synchronized 实现线程同步
在 synchronized 代码部分中:
- 假设t1和t2线程并发,开始执行以下代码的时候,肯定有一个先一个后。
- 假设t1先执行了,遇到了synchronized,这个时候自动找后面共享对象”的对象锁找到之后,并占有这把锁,然后执行同步代码块中的程序,在程序执行过程中一疸都是占有这把锁的。直到同步代码块代码结束,这把锁才会释故。
- 假设t1已经占有这把锁,此时t2也遇到synchronized关键字,也会去占有后面共享对象的这把锁,结果这把锁被t1占有,t2只能在同步代码块外面等待t1的结束,查到t1把同步代码块执行结束了,,t1会归还这把锁,此时t2终于等到这把锁,然后t2占有这把锁之后,进入同步代码块执行程序。
- 对 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 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110
| public class _625_账户类的定义 { public static void main(String[] args) { Account act = new Account("act-001", 10000);
AccountThread t1 = new AccountThread(act); AccountThread t2 = new AccountThread(act);
t1.setName("t1"); t2.setName("t2");
t1.start(); t2.start();
try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("账户最终余额:" + act.getBalance()); } }
class AccountThread extends Thread{
private Account act;
public AccountThread(Account o){ this.act = o; }
@Override public void run() {
double money = 5000; act.withdraw(money); }
public Account getAct() { return act; }
public void setAct(Account act) { this.act = act; } }
class Account{ private String actno; private double balance;
public Account(String actno, double balance) { this.actno = actno; this.balance = balance; }
public void withdraw(double money){
synchronized (this){
double before = this.getBalance(); double after = before - money;
this.setBalance(after);
System.out.println(Thread.currentThread().getName() + "操作" + "余额:" + this.getBalance()); } }
public String getActno() { return actno; }
public void setActno(String actno) { this.actno = actno; }
public double getBalance() { return balance; }
public void setBalance(double balance) { this.balance = balance; } }
|
synchronized 可以出现在实例方法上,只能锁this,不灵活,但是省代码
表示整个方法体都需要同步
1
| public synchronized void withdraw(double money)
|
哪些变量有线程安全问题
- 实例变量:在堆中
- 静态变量:在方法区中
- 局部变量:在栈中
以上三大变量中,局部变量不会出现线程安全问题
不满足线程安全中共享数据的条件
栈可以有很多个,局部变量在栈中,而堆和方法区只有一个
synchronized 出现在某类中静态方法上会锁类锁
synchronized 的三种写法
- 同步代码块
1 2 3
| synchronized(线程同步对象){ }
|
- 在实例方法上使用synchronized
表示共享对象一定是this,并且同步代码块是整个方法体
- 在静态方法上使用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 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
| public class _638_死锁概述 { public static void main(String[] args) { Object o1 = new Object(); Object o2 = new Object();
MyThread1 t1 = new MyThread1(o1, o2); MyThread2 t2 = new MyThread2(o1, o2);
t1.start(); t2.start(); } }
class MyThread1 extends Thread{ Object o1; Object o2;
public MyThread1(Object o1, Object o2) { this.o1 = o1; this.o2 = o2; }
@Override public void run() { synchronized (o1){ try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (o2){
} } System.out.println(Thread.currentThread().getName() + "run()执行结束"); } }
class MyThread2 extends Thread{ Object o1; Object o2;
public MyThread2(Object o1, Object o2) { this.o1 = o1; this.o2 = o2; }
@Override public void run() { synchronized (o2){ try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (o1){
} } System.out.println(Thread.currentThread().getName() + "run()执行结束"); } }
|
开发中怎么解决线程安全问题
- 尽量使用局部变量代替“实例变量和静态变量”
- 如果必须是实例变量,可以考虑创建多个对象,这样实例变量的内存就不共享了
- 不能用局部变量,也不能创建多个对象,那就只能 synchronized 了
守护线程
- 线程分为两大类:用户线程、守护线程(后台线程)
- 守护线程一般是个死循环,所有的用户线程只要结束,守护线程自动结束
- 主线程main方法是一个用户线程
实现守护线程
main用户线程结束后,守护线程就结束了
1
| public final void setDaemon(boolean on)
|
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
| public class _642_实现守护线程 { public static void main(String[] args) { BakDataThread t = new BakDataThread(); t.setName("数据备份线程"); t.setDaemon(true);
t.start();
for (int i = 0; i < 10; i++) { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "->" + i); } } }
class BakDataThread extends Thread{ @Override public void run() { int i = 0; while (true) { System.out.println(Thread.currentThread().getName() + "->" + (++i)); try { sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } }
|
定时器
- 间隔的定时间,执行特定程序
- 可以使用sleep(),比较low
- 在java.util.Timer实现了定时器,可以直接拿来用
- 在实际开发中,使用较多的是Spring框架中提供的SpringTask框架
实现定时器
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
| import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Timer; import java.util.TimerTask;
public class _644_实现定时器 { public static void main(String[] args) throws ParseException { Timer timer = new Timer();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); Date date = sdf.parse("2022-02-07 17:42:00");
Date date1 = new Date(System.currentTimeMillis() + 1000 * 2);
timer.schedule(new LogTimerTask(), date1, 1000 * 10);
} }
class LogTimerTask extends TimerTask { @Override public void run() { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String s = sdf.format(new Date()); System.out.println(s + "完成了一次数据备份"); } }
|
实现线程的第三种方式
实现Callable接口。(JDK8新特性)
可以获得线程的返回值
缺点是获取执行结果的时候效率较低
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
| import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.FutureTask;
public class _645_实现线程的第三种方式 { public static void main(String[] args) { FutureTask<Object> task = new FutureTask(new Callable() { @Override public Object call() throws Exception { System.out.println("call method begin~"); Thread.sleep(1000 * 10); System.out.println("call method over~");
int a = 100; int b = 200;
return a + b; } });
Thread t = new Thread(task); t.start();
Object o = null; try { o = task.get(); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); }
System.out.println(o);
} }
|
wait和notify方法
- wait和notify方法不是线程对象的方法,是java中任何一个java对象都有的方法
- wait和notify方法建立在synchronized线程同步的基础之上
wait() 的作用
1 2
| Object o = new Object(); o.wait();
|
表示:让正在o对象上活动的线程进入等待状态,并且释放之前占有的o对象的锁,无期限等待直到被唤醒
notify() 的作用
1 2
| Object o = new Object(); o.notify();
|
表示:让正在o对象上活动的随机一个线程从等待状态唤醒,o.notify()只会通知,不会释放之前占有的o对象的锁
notifyAll() 作用
表示:唤醒在o对象上等待的所有线程
生产者和消费者模式
(这儿应该有张图… )
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 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89
| import java.util.ArrayList; import java.util.List; import java.util.Random;
public class _648_实现生产者和消费者模式 { public static void main(String[] args) { List list = new ArrayList();
Thread t1 = new Thread(new Producer(list)); t1.setName("生产者"); Thread t2 = new Thread(new Consumer(list)); t2.setName("消费者");
t1.start(); t2.start(); } }
class Producer implements Runnable{ private List list;
public Producer(List list) { this.list = list; }
@Override public void run() { while (true){ synchronized (list){ if (list.size() > 0){ try { list.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } else { double o = Math.random(); list.add(o); System.out.println(Thread.currentThread().getName() + "生产->" + o);
list.notify(); } } } } }
class Consumer implements Runnable{ private List list;
public Consumer(List list) { this.list = list; }
@Override public void run() { while (true) { synchronized (list){ if(list.size() == 0){ try { list.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } else { Object o = list.remove(0); System.out.println(Thread.currentThread().getName() + "消费->" + o);
list.notify(); } } } } }
|
线程作业
ta,tb交替输出
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 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97
| public class _650_布置线程作业实现交替输出 { public static void main(String[] args) { Sz sz = new Sz(1); ThreadA ta = new ThreadA(sz); ThreadB tb = new ThreadB(sz);
ta.setName("ta"); tb.setName("tb");
ta.start(); tb.start();
} }
class Sz{ int cnt;
public Sz(int cnt) { this.cnt = cnt; } }
class ThreadA extends Thread{
Sz sz;
public ThreadA(Sz sz) { this.sz = sz; }
@Override public void run() { while (true) { synchronized (sz){ if (sz.cnt%2 != 1){ try { sz.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } else { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "->" + sz.cnt); sz.cnt++; sz.notify(); } } } } }
class ThreadB extends Thread{
Sz sz;
public ThreadB(Sz sz) { this.sz = sz; }
@Override public void run() { while (true) { synchronized (sz){ if(sz.cnt%2 != 0){ try { sz.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } else{ try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "->" + sz.cnt); sz.cnt++; sz.notify(); } } } } }
|