Java多线程基础

Java多线程1 线程创建1.1 继承Thread类1.2 实现Runnable接口1.3 实现Callable接口1.4 静态代理模式1.5 线程创建总结2 线程状态2.1 线程停止2.2 线程休眠2.3 线程礼让2.4 线程强制执行2.5 观测线程状态2.6 线程优先级2.7 守护线程3 线程同步3.1 线程同步机制3.2 并发问题3.3 同步方法与同步块3.4 JUC安全类型集合3.5 死锁3.6 Lock锁4 线程通信4.1 线程通信方法4.2 管程法4.3 信号灯法5 线程池5.1使用线程池
Java多线程
1 线程创建
1.1 继承Thread类
创建线程方式一:继承Thread类
- 继承Thread类,重写run方法,调用start开始线程
xxxxxxxxxx201/*创建线程:继承Thread类,重写run方法,调用start开始线程*/2public class TestThread extends Thread{3 4 public void run() {5 for (int i = 0; i < 20; i++) {6 System.out.println("----线程执行----" + i);7 }8 }9
10 public static void main(String[] args) {11 /*创建线程对象*/12 TestThread testThread = new TestThread();13 /*调用start方法*/14 testThread.start();15
16 for (int i = 0; i < 20; i++) {17 System.out.println("----主线程执行----" + i);18 }19 }20}1.2 实现Runnable接口
创建线程方式二:实现runnable接口
- 实现runnable接口,重写run方法,执行start,开启线程
xxxxxxxxxx201/*实现runnable接口,重写run方法,执行start*/2public class TestThread implements Runnable {3
4 5 public void run() {6 for (int i = 0; i < 20; i++) {7 System.out.println("----线程执行----" + i);8 }9 }10
11 public static void main(String[] args) {12 /*创建Runnable接口的实现类对象*/13 TestThread test = new TestThread();14
15 /*创建线程对象*/16 Thread thread = new Thread(test);17
18 thread.start();19 }20}1.3 实现Callable接口
创建线程方式三:实现Callable接口
- 实现Callable接口,重写call方法,创建执行服务,提交执行,获取结果,关闭服务
xxxxxxxxxx271/*实现Callable接口2* 1.可以定义返回值3* 2.可以抛出异常4*/5public class TestCallable implements Callable<Boolean> {6 7 public Boolean call() {8 System.out.println(Thread.currentThread().getName()+"线程执行");9 return true;10 }11
12 public static void main(String[] args) throws ExecutionException, InterruptedException {13 TestCallable callable1 = new TestCallable();14 TestCallable callable2 = new TestCallable();15
16 /*创建执行服务*/17 ExecutorService ser = Executors.newFixedThreadPool(2);18 /*提交执行*/19 Future<Boolean> result1 = ser.submit(callable1);20 Future<Boolean> result2 = ser.submit(callable2);21 /*获取结果*/22 boolean rs1 = result1.get();23 boolean rs2 = result2.get();24 /*关闭服务*/25 ser.shutdown();26 }27}1.4 静态代理模式
- 代理对象和真实对象需要实现同一个接口,代理对象代理真实角色
- 代理对象可以做很多真实对象做不了的事 ,真实对象专注做自己的事情
xxxxxxxxxx371/*接口*/2interface Marry{3 void HappyMarry();4}5
6/*真实角色*/7class You implements Marry{8 9 public void HappyMarry() {10 System.out.println("Marry");11 }12}13
14/*代理角色*/15class WeddingCompany implements Marry{16
17 private Marry target;18
19 public WeddingCompany(Marry target){20 this.target = target;21 }22
23 24 public void HappyMarry() {25 before();26 this.target.HappyMarry();27 after();28 }29
30 private void after() {31 System.out.println("结婚后,收尾款");32 }33
34 private void before() {35 System.out.println("结婚前,布置现场");36 }37}main:
xxxxxxxxxx91public static void main(String[] args) {2 new Thread(()-> System.out.println("thread")).start();3
4 Marry you = new You();5 WeddingCompany weddingCompany = new WeddingCompany(you);6 weddingCompany.HappyMarry();7 System.out.println("------------");8 you.HappyMarry();9}1.5 线程创建总结
xxxxxxxxxx421public class ThreadNew {2 public static void main(String[] args) {3 //继承Thread4 new MyThread1().start();5 //实现Runnable6 new Thread(new MyThread2()).start();7 //实现Callable8 FutureTask<Integer> futureTask = new FutureTask<Integer>(new MyThread3());9 new Thread(futureTask).start();10
11 try {12 Integer integer = futureTask.get();13 System.out.println(integer);14 } catch (InterruptedException e) {15 e.printStackTrace();16 } catch (ExecutionException e) {17 e.printStackTrace();18 }19 }20}21
22class MyThread1 extends Thread{23 24 public void run() {25 System.out.println("Thread1 -- extends Thread");26 }27}28
29class MyThread2 implements Runnable{30 31 public void run() {32 System.out.println("Thread2 -- implements Runnable");33 }34}35
36class MyThread3 implements Callable<Integer>{37 38 public Integer call() throws Exception {39 System.out.println("Thread3 -- implements Callable");40 return 100;41 }42}2 线程状态
.png)
新建状态:
使用 new 关键字和 Thread 类或其子类建立一个线程对象后,该线程对象就处于新建状态。它保持这个状态直到程序 start() 这个线程。
就绪状态:
当线程对象调用了start()方法之后,该线程就进入就绪状态。就绪状态的线程处于就绪队列中,要等待JVM里线程调度器的调度。
运行状态:
如果就绪状态的线程获取 CPU 资源,就可以执行 run(),此时线程便处于运行状态。处于运行状态的线程最为复杂,它可以变为阻塞状态、就绪状态和死亡状态。
阻塞状态:
如果一个线程执行了sleep(睡眠)、suspend(挂起)等方法,失去所占用资源之后,该线程就从运行状态进入阻塞状态。在睡眠时间已到或获得设备资源后可以重新进入就绪状态。可以分为三种:
- 等待阻塞:运行状态中的线程执行 wait() 方法,使线程进入到等待阻塞状态。
- 同步阻塞:线程在获取 synchronized 同步锁失败(因为同步锁被其他线程占用)。
- 其他阻塞:通过调用线程的 sleep() 或 join() 发出了 I/O 请求时,线程就会进入到阻塞状态。当sleep() 状态超时,join() 等待线程终止或超时,或者 I/O 处理完毕,线程重新转入就绪状态。
死亡状态:
一个运行状态的线程完成任务或者其他终止条件发生时,该线程就切换到终止状态。
2.1 线程停止
线程停止:
- 线程最好是正常停止
- 需要停止线程设置一个标志位,通过设置标志位,改变标志位停止线程
xxxxxxxxxx331/*测试停止2* 1.建议正常停止3* 2.建议使用标志位*/4public class TestStop implements Runnable{5 /*设置一个标志位*/6 private boolean flag = true;7
8 9 public void run() {10 int i = 0;11 while (flag){12 System.out.println("run.......thread"+i++);13 }14 }15
16 public void stop(){17 this.flag = false;18 }19
20 public static void main(String[] args) {21 TestStop testStop = new TestStop();22
23 new Thread(testStop).start();24
25 for (int i = 0; i < 1500; i++) {26 System.out.println("main.......thread"+i);27 if(i == 1000){28 testStop.stop();29 System.out.println("stop......thread");30 }31 }32 }33}2.2 线程休眠
- sleep时间到达之后线程进入就绪状态
- 每个对象都有一个锁,sleep不会释放锁
xxxxxxxxxx11Thread.sleep(200);2.3 线程礼让
- 线程礼让,让当前执行的线程暂停,不阻塞
- 线程从运行状态变为就绪状态
- 礼让不一定成功,根据CPU调度执行
xxxxxxxxxx11Thread.yield();2.4 线程强制执行
- join,等待此线程执行完成才会执行其他线程,其他线程阻塞
xxxxxxxxxx11thread.join();2.5 观测线程状态
xxxxxxxxxx11thread.getState();xxxxxxxxxx291public class TestState {2
3 public static void main(String[] args) throws InterruptedException {4 Thread thread = new Thread(()->{5 for (int i = 0; i < 5; i++) {6 try {7 Thread.sleep(1000);8 } catch (InterruptedException e) {9 e.printStackTrace();10 }11 System.out.println("........");12 }13 });14
15 //观察状态16 Thread.State state = thread.getState();17 System.out.println(state);18
19 thread.start();20 state = thread.getState();21 System.out.println(state);22
23 while (state != Thread.State.TERMINATED){24 Thread.sleep(100);25 state = thread.getState();26 System.out.println(state);27 }28 }29}2.6 线程优先级
进入就绪状态的线程,线程调度器按照优先级决定哪个线程先执行
设置 获取优先级
xxxxxxxxxx21t2.setPriority(3);2t2.getPriority()xxxxxxxxxx361public class TestPriority {2
3 public static void main(String[] args) {4 /*主线程优先级*/5 System.out.println(Thread.currentThread().getName() + "-->" + Thread.currentThread().getPriority());6
7 MyPriority myPriority = new MyPriority();8
9 Thread t1 = new Thread(myPriority,"T1");10 Thread t2 = new Thread(myPriority,"T2");11 Thread t3 = new Thread(myPriority,"T3");12 Thread t4 = new Thread(myPriority,"T4");13 Thread t5 = new Thread(myPriority,"T5");14
15 t1.start();16
17 t2.setPriority(3);18 t2.start();19
20 t3.setPriority(5);21 t3.start();22
23 t4.setPriority(6);24 t4.start();25
26 t5.setPriority(Thread.MAX_PRIORITY);27 t5.start();28 }29}30
31class MyPriority implements Runnable{32 33 public void run() {34 System.out.println(Thread.currentThread().getName() + "-->" + Thread.currentThread().getPriority());35 }36}2.7 守护线程
守护进程创建:
xxxxxxxxxx11thread.setDaemon(true);xxxxxxxxxx351/*测试守护线程*/2public class TestDaemon {3 public static void main(String[] args) {4 God god = new God();5 You you = new You();6
7 Thread thread = new Thread(god);8 thread.setDaemon(true);9
10 thread.start();11
12 new Thread(you).start();13 }14}15
16/*God*/17class God implements Runnable{18 19 public void run() {20 while (true){21 System.out.println("god bless you");22 }23 }24}25
26/*you*/27class You implements Runnable{28 29 public void run() {30 for (int i = 0; i < 36500; i++) {31 System.out.println("第" + i + "天");32 }33 System.out.println("Goodbye world");34 }35}3 线程同步
3.1 线程同步机制
并发
同一个对象被多个线程同时操作
线程同步
多个线程访问一个对象,并且线程修改对象的值,这时候就需要线程同步,等待机制,多个同时访问对象的线程进入对象的等待池形成队列,一个线程使用完,下一个线程再使用
3.2 并发问题
例子1:抢票 没有线程同步,此时就会出现多个人抢一张票的情况,产生负数
xxxxxxxxxx251public class Unsafe implements Runnable{2 private int tickNums = 10;3
4 5 public void run() {6 while (tickNums > 0) {7
8 try {9 Thread.sleep(200);10 } catch (InterruptedException e) {11 e.printStackTrace();12 }13
14 System.out.println(Thread.currentThread().getName() + "拿到了第" + tickNums-- + "张票");15 }16 }17
18 public static void main(String[] args) {19 Unsafe thread = new Unsafe();20
21 new Thread(thread,"小明").start();22 new Thread(thread,"老师").start();23 new Thread(thread,"黄牛").start();24 }25}例子2:多线程插入List,出现重复插入导致数据缺失
xxxxxxxxxx181public class UnsafeList {2 public static void main(String[] args) {3 List<String> List = new ArrayList<>();4 for (int i = 0; i < 10000; i++) {5 new Thread(()->{6 List.add(Thread.currentThread().getName());7 }).start();8 }9
10 try {11 Thread.sleep(3000);12 } catch (InterruptedException e) {13 e.printStackTrace();14 }15
16 System.out.println(List.size());17 }18}3.3 同步方法与同步块
同步方法:public synchronized void method()
- synchronized 方法控制对对象的访问,每个对象对应一把锁,每个synchronized 方法都必须获得调用该方法的对象的锁才能执行,方法开始执行,就会独占该锁,直到执行结束才会释放锁,阻塞的线程才能获取这个锁
xxxxxxxxxx321public class TestSynchronized implements Runnable{2 private int tickNums = 10;3 boolean flag = true;4
5 6 public void run() {7 while (flag) {8 try {9 buy();10 } catch (InterruptedException e) {11 e.printStackTrace();12 }13 }14 }15
16 public synchronized void buy() throws InterruptedException{17 if (tickNums <= 0) {18 flag = false;19 return;20 }21 Thread.sleep(100);22 System.out.println(Thread.currentThread().getName() + "拿到了第" + tickNums-- + "张票");23 }24
25 public static void main(String[] args) {26 TestSynchronized thread = new TestSynchronized();27
28 new Thread(thread,"小明").start();29 new Thread(thread,"老师").start();30 new Thread(thread,"黄牛").start();31 }32}同步块:synchronized(obj){}
obj为同步监视器
- Obj为获取锁的对象,执行同步块时会获取Obj的锁,进行线程同步
- 同步方法默认同步监视器为this,或者为class
xxxxxxxxxx201public class TestSynList {2 public static void main(String[] args) {3 List<String> List = new ArrayList<>();4 for (int i = 0; i < 10000; i++) {5 new Thread(()->{6 synchronized (List){7 List.add(Thread.currentThread().getName());8 }9 }).start();10 }11
12 try {13 Thread.sleep(3000);14 } catch (InterruptedException e) {15 e.printStackTrace();16 }17
18 System.out.println(List.size());19 }20}3.4 JUC安全类型集合
CopyOnWriteArrayList
类本身能实现线程同步,直接使用
xxxxxxxxxx181public class TestJUC {2 public static void main(String[] args) {3 CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<String>();4 for (int i = 0; i < 10000; i++) {5 new Thread(()->{6 list.add(Thread.currentThread().getName());7 }).start();8 }9
10 try {11 Thread.sleep(3000);12 } catch (InterruptedException e) {13 e.printStackTrace();14 }15
16 System.out.println(list.size());17 }18}3.5 死锁
死锁的四个必要条件
- 1.互斥条件:一个资源只能被一个进程使用
- 2.请求与保持:一个进程因请求资源而阻塞时,对已有的资源保持不是放
- 3.不剥夺条件:进程已获得的资源,在未使用完之前不进行剥夺
- 4.循环等待:若干进程之间形成头尾相连的循环等待资源的关系
xxxxxxxxxx611/*死锁*/2public class DeadLock {3 public static void main(String[] args) {4 Makeup q1 = new Makeup(0,"A");5 Makeup q2 = new Makeup(1,"B");6
7 q1.start();8 q2.start();9 }10}11
12class Lipstick{13
14}15
16class Mirror{17
18}19
20class Makeup extends Thread{21
22 static Lipstick lipstick = new Lipstick();23 static Mirror mirror = new Mirror();24
25 private int choice;26 private String Name;27
28 Makeup(int choice,String name){29 this.choice = choice;30 this.Name = name;31 }32
33 34 public void run() {35 try {36 makeup();37 } catch (InterruptedException e) {38 e.printStackTrace();39 }40 }41
42 private void makeup() throws InterruptedException {43 if (choice == 0) {44 synchronized (lipstick){45 System.out.println(Name + "获得口红锁");46 Thread.sleep(1000);47 synchronized (mirror){48 System.out.println(Name + "获得镜子锁");49 }50 }51 }else {52 synchronized (mirror){53 System.out.println(Name + "获得镜子锁");54 Thread.sleep(2000);55 synchronized (lipstick){56 System.out.println(Name + "获得口红锁");57 }58 }59 }60 }61}3.6 Lock锁
ReentrantLock类实现了Lock,可以显式加锁与释放锁
xxxxxxxxxx411/*测试Lock锁*/2public class Lock {3 public static void main(String[] args) {4 TestLock L1 = new TestLock();5
6 new Thread(L1,"L1").start();7 new Thread(L1,"L2").start();8 new Thread(L1,"L3").start();9 }10}11
12
13class TestLock implements Runnable{14
15 private int tickNum = 10;16 private boolean flag= true;17
18 /*定义lock锁*/19 private ReentrantLock lock = new ReentrantLock();20
21 22 public void run() {23 while (flag){24 try {25 lock.lock();26 buy();27 }finally {28 lock.unlock();29 }30 }31 }32
33 public void buy(){34 if (tickNum <= 0) {35 flag = false;36 return;37 }38
39 System.out.println(Thread.currentThread().getName() + " " +tickNum--);40 }41}4 线程通信
4.1 线程通信方法
wait()
- 线程会一直等待,知道其他线程通知,与sleep不同,会释放锁
- wait可以加参数毫秒,表示指定等待的时间
notify()
- 唤醒一个处于的等待状态的线程
notifyAll()
- 唤醒同一个对象所有调用wait()方法的线程
4.2 管程法
生产者/消费者模型
- 生产者:负责产生数据的模块
- 消费者:负责处理数据的模块
- 缓冲区:生产者将生产好的数据放入缓冲区,消费者从缓冲区拿数据
xxxxxxxxxx1041/*生产者,消费者,产品,缓冲区*/2public class TestPC {3 public static void main(String[] args) {4 SynContainer container = new SynContainer();5
6 new Producer(container).start();7 new Consumer(container).start();8 }9}10
11class Producer extends Thread{12 SynContainer container;13
14 public Producer(SynContainer container) {15 this.container = container;16 }17
18 /*生产*/19 20 public void run() {21 for (int i = 0; i < 100; i++) {22 System.out.println("生产了第"+i+"只鸡");23 container.push(new Chicken(i));24 }25 }26}27
28class Consumer extends Thread{29 SynContainer container;30
31 public Consumer(SynContainer container) {32 this.container = container;33 }34
35 /*生产*/36 37 public void run() {38 for (int i = 0; i < 100; i++) {39 Chicken chicken = container.pop();40 System.out.println("吃了第"+chicken.getID()+"只鸡");41 }42 }43}44
45class Chicken{46 private int ID;47
48 public Chicken(int ID) {49 this.ID = ID;50 }51
52 public int getID() {53 return ID;54 }55}56
57class SynContainer{58 Chicken[] chickens = new Chicken[10];59 int count = 0;60
61 /*生产者放入产品*/62 public synchronized void push(Chicken chicken){63 /*容器满了,等待消费*/64 if (count == chickens.length) {65 try {66 /*等待消费*/67 this.wait();68 } catch (InterruptedException e) {69 e.printStackTrace();70 }71 }72
73 /*如果容器没满,放入产品*/74 chickens[count] = chicken;75 count++;76
77 /*通知消费者消费*/78 this.notifyAll();79 }80
81 /*消费者取出产品*/82 public synchronized Chicken pop(){83 /*判断是否存在产品*/84 if (count == 0) {85 try {86 /*等待生产*/87 this.wait();88 } catch (InterruptedException e) {89 e.printStackTrace();90 }91 }92
93 /*消费者消费*/94 count--;95 Chicken chicken = chickens[count];96 chickens[count] = null;97
98 /*通知生产者生产*/99 this.notifyAll();100
101 return chicken;102 }103
104}4.3 信号灯法
通过一个标志位来判断生产是否完成,消费是否完成
xxxxxxxxxx921/*信号量*/2public class TestPC2 {3 public static void main(String[] args) {4 TV tv = new TV();5
6 new Player(tv).start();7 new Watcher(tv).start();8 }9}10
11/*生产者-->演员*/12class Player extends Thread{13 private TV tv;14
15 public Player(TV tv){16 this.tv = tv;17 }18
19 20 public void run() {21 for (int i = 0; i < 20; i++) {22 if (i%2 == 0){23 tv.play("你所热爱的就是你的生活");24 }else{25 tv.play("柠檬什么时候熟");26 }27 }28 }29}30
31/*消费者-->观众*/32class Watcher extends Thread{33 private TV tv;34 35 public Watcher(TV tv){36 this.tv = tv;37 }38
39 40 public void run() {41 for (int i = 0; i < 20; i++) {42 tv.watch();43 }44 }45}46
47/*产品-->节目*/48class TV{49 /*演员表演,观众等待*/50 /*观众观看,演员等待*/51 String voice;52 boolean flag = true;53
54 /*表演*/55 public synchronized void play(String voice){56
57 if (!flag){58 try {59 this.wait();60 } catch (InterruptedException e) {61 e.printStackTrace();62 }63 }64
65 System.out.println("演员表演了:"+voice);66
67 /*通知观众观看*/68 this.notifyAll();69 this.voice = voice;70 this.flag = !this.flag;71 }72
73
74 /*观看*/75 public synchronized void watch(){76
77 if (flag){78 try {79 this.wait();80 }catch (InterruptedException e) {81 e.printStackTrace();82 }83 }84 System.out.println("观众观看了:"+voice);85 86 /*通知演员表演*/87 this.notifyAll();88 this.voice = "";89 this.flag = !this.flag;90 }91
92}5 线程池
提前创建好多个线程,放入线程池,直接获取使用,放回池内
- 提高响应速度
- 降低资源消耗
- 便于管理线程
5.1使用线程池
271/*测试线程池*/2public class TestThreadPool {3
4 public static void main(String[] args) {5 /*1.创建服务,创建线程池*/6 ExecutorService service = Executors.newFixedThreadPool(10);7
8 /*执行*/9 service.execute(new MyThread());10 service.execute(new MyThread());11 service.execute(new MyThread());12 service.execute(new MyThread());13
14 /*2.关闭链接*/15 service.shutdown();16 }17}18
19
20class MyThread implements Runnable{21 22 public void run() {23 for (int i = 0; i < 10; i++) {24 System.out.println(Thread.currentThread().getName()+" "+i);25 }26 }27}posted on 2021-12-03 18:26 Egoistic_Flowers 阅读(45) 评论(0) 收藏 举报
浙公网安备 33010602011771号