Java之多线程详解
Posted 摸鱼科技资讯
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java之多线程详解相关的知识,希望对你有一定的参考价值。
点击“上方蓝字”关注摸鱼君啦
1.什么是多线程?
线程其实就是程序执行的一条路径 一个进程可以包含多个线程
多线程是并发进行的
底层 其实还是单线程(cpu运行效率极其高 )
并发:两个任务同时请求运行 而处理器只能执行一个 不过由于处理的特别快感觉是两个任务同时运行
并行:就是两个任务一起同步运行(需要多核cpu)
2.java程序运行原理
jvm是多线程的 有两个线程: 垃圾回收线程 主函数main是个线程
3.Thread类
继承Thread
重写run方法
调用start方法
1public class Main{
2 public static void main(String args[]) throws Exception{
3
4 MyThread mt = new MyThread();
5 mt.start();
6
7 for(int i=1;i<=1000;i++) {
8 System.out.println("主线程代码");
9 }
10 }
11}
12class MyThread extends Thread{
13 public void run()
14 {
15 for(int i=1;i<=1000;i++) {
16
17 System.out.println("我的线程代码");
18 }
19 }
20}
4.实现Runnable
实现Runnable接口
重写run方法
因为要启动线程必须通过Thread类的start方法才能使用!
所以必须要创建Thread的对象 把Runable作为target参数传递给Thread
1public class Main{
2 public static void main(String args[]) {
3 demo de = new demo();
4 new Thread(de).start(); //1.第一种启动线程的方法 匿名对象
5
6 Thread t = new Thread(de); //2.第二种启动线程的方法
7 t.start();
8
9 Runnable run = de; //3.第三种启动线程的方法 父类引用指向子类对象
10 new Thread(run).start();
11
12 for(int i=1;i<=1000;i++) {
13 System.out.println("主线程代码");
14 }
15 }
16}
17class demo implements Runnable
18{
19 @Override
20 public void run() {
21 for(int i=1;i<=1000;i++) {
22 System.out.println("我的线程代码");
23 }
24 }
25}
Runnable能够实现多线程的原理 (源码分析)
通过作为Thread类的构造参数 传入底层的init方法 最后给Runnable 类型的target赋值 然后去调用run方法
两者的区别:
1.继承 代码比较简单 不过有父类的话就不用了
2.实现 代码比较复杂 可以多实现
个人而言喜欢使用Runnable接口 扩展性比较强
当然具体情况具体分析
6.用匿名内部类实现多线程
1public class Main{
2 public static void main(String args[]) {
3 new Thread() {
4 public void run()
5 {
6 for(int i=1;i<=1000;i++) {
7 System.out.println("线程1111111111代码");
8 }
9 }
10 }.start();
11
12 new Thread(new Runnable() {
13 public void run()
14 {
15 for(int i=1;i<=1000;i++) {
16 System.out.println("线程33333333333代码");
17 }
18 }
19 }).start();
20 }
21}
7.Thread的常用方法
1.给线程设置名字
1public class Main{
2 public static void main(String args[]) {
3
4 //1.通过String name的构造来取名字
5 new Thread("线程1") {
6 public void run()
7 {
8 System.out.println(this.getName()+"...线程1111111111代码");
9 }
10 }.start();
11
12 new Thread("线程2") {
13 public void run()
14 {
15 System.out.println(this.getName()+"...线程2222222222代码");
16 }
17 }.start();
18
19 //2.通过Thread类的setName方法
20 new Thread() {
21 public void run()
22 {
23 this.setName("线程3");
24 System.out.println(this.getName()+"...线程33333333代码");
25 }
26 }.start();
27
28 Thread t1 = new Thread() {
29 public void run()
30 {
31 System.out.println(this.getName()+"...线程4444444代码");
32 }
33 };
34 t1.setName("线程4");
35 t1.start();
36 }
37}
38}
2.获取当前线程的引用
1public class Main{
2 public static void main(String args[]) {
3
4 new Thread(new Runnable() {
5
6 @Override
7 public void run() {
8 System.out.println(Thread.currentThread().getName()+"...线程111111");
9 }
10 }).start();
11
12 System.out.println(Thread.currentThread().getName()+"...线程222222");
13 }
14}
3.休眠线程
异常处理只能自己处理
因为父类的run方法没有抛出 所以子类的方法一定不能抛出!!
sleep方法是自行调用处理
wait方法是必须由别人调用才能处理
1public class Main{
2 public static void main(String args[]) {
3 //倒计时器
4 new Thread() {
5 @Override
6 public void run() {
7
8 for(int i=10;i>0;i--) {
9 try {
10 Thread.sleep(1000);
11 } catch (InterruptedException e) {
12 e.printStackTrace();
13 }
14 System.out.println("倒计时"+i+"秒");
15 }
16 }
17 }.start();
18 }
19}
4.守护线程
守护线程就是例如象棋中的车马炮
而被守护的线程就是象棋中的帅 如果当帅的线程结束了 那么整个守护线程就立马结束(但是实际情况中是守护线程还是会执行一下 有遗留的数据要处理)
本demo中 t1为非守护线程
1public class Main{
2 public static void main(String args[]) {
3 Thread t1 = new Thread() {
4 @Override
5 public void run() {
6
7 for(int i=2;i>0;i--) {
8 System.out.println("线程1....111");
9 }
10 }
11 };
12
13 Thread t2 = new Thread() {
14 @Override
15 public void run() {
16
17 for(int i=10;i>0;i--) {
18 System.out.println("线程2....222");
19 }
20 }
21 };
22
23 t2.setDaemon(true);
24 t1.start();
25 t2.start();
26 }
27}
5.加入线程
相当于插队 !
就是线程1在运行的时候 线程2插队进来 等线程2结束后 线程1才继续执行
匿名内部类在使用所在方法中的局部变量时该变量必须要使用final修饰!或者事实上为final的
1public class Main{
2 public static void main(String args[]) {
3 Thread t1 = new Thread() {
4 @Override
5 public void run() {
6
7 for(int i=10;i>0;i--) {
8 System.out.println("线程1....1111111111111111");
9 }
10 }
11 };
12
13 Thread t2 = new Thread() {
14 @Override
15 public void run() {
16
17 for(int i=10;i>0;i--) {
18 if(i==8) {
19 try {
20 t1.join(); //t1进来插队
21 //t1.join(1); //t1进来插队 1毫秒以后 t1 t2又继续交替运行
22 } catch (InterruptedException e) {
23 // TODO Auto-generated catch block
24 e.printStackTrace();
25 }
26 }
27 System.out.println("线程2....222");
28 }
29 }
30 };
31
32 t1.start();
33 t2.start();
34 }
35}
6.礼让线程
效果不是很明显 了解即可
7.设置线程的优先级
效果也不是很明显
1public class Main{
2 public static void main(String args[]) {
3 Thread t1 = new Thread() {
4 @Override
5 public void run() {
6
7 for(int i=100;i>0;i--) {
8 System.out.println("线程1....1111111111111111");
9 }
10 }
11 };
12 Thread t2 = new Thread() {
13 @Override
14 public void run() {
15
16 for(int i=100;i>0;i--) {
17 System.out.println("线程2....22");
18 }
19 }
20 };
21 t1.setPriority(Thread.MAX_PRIORITY);
22 t2.setPriority(Thread.MIN_PRIORITY);
23
24 t1.start();
25 t2.start();
26 }
27}
8.同步代码块
锁要一致
好处是 在每个任务的多条执行命令下不会错乱 线程之间的逻辑不会相互影响 造成乱码或者错误异常
例如本demo中的打印可能会出现 打印不正确的情况 虽然概率低不过存在
为了保证以上错误不会出现 所以要加锁!保证线程安全
1public class Demo1_Synchronized {
2
3 /**
4 * @param args
5 * 同步代码块
6 */
7 public static void main(String[] args) {
8 final Printer p = new Printer();
9
10 new Thread() {
11 public void run() {
12 while(true) {
13 p.print1();
14 }
15 }
16 }.start();
17
18 new Thread() {
19 public void run() {
20 while(true) {
21 p.print2();
22 }
23 }
24 }.start();
25 }
26
27}
28
29class Printer {
30 Demo d = new Demo();
31 public void print1() {
32 //synchronized(new Demo()) { //同步代码块,锁机制,锁对象可以是任意的
33 synchronized(d) {
34 System.out.print("黑");
35 System.out.print("马");
36 System.out.print("程");
37 System.out.print("序");
38 System.out.print("员");
39 System.out.print("\r\n");
40 }
41 }
42
43 public void print2() {
44 //synchronized(new Demo()) { //锁对象不能用匿名对象,因为匿名对象不是同一个对象
45 synchronized(d) {
46 System.out.print("传");
47 System.out.print("智");
48 System.out.print("播");
49 System.out.print("客");
50 System.out.print("\r\n");
51 }
52 }
53}
54
55class Demo{}
使用静态同步方法
1class Printer2 {
2 Demo d = new Demo();
3 //非静态的同步方法的锁对象是神马?
4 //答:非静态的同步方法的锁对象是this
5 //静态的同步方法的锁对象是什么?
6 //是该类的字节码对象
7 public static synchronized void print1() { //同步方法只需要在方法上加synchronized关键字即可
8 System.out.print("黑");
9 System.out.print("马");
10 System.out.print("程");
11 System.out.print("序");
12 System.out.print("员");
13 System.out.print("\r\n");
14 }
15
16 public static void print2() {
17 //synchronized(new Demo()) { //锁对象不能用匿名对象,因为匿名对象不是同一个对象
18 synchronized(Printer2.class) {
19 System.out.print("传");
20 System.out.print("智");
21 System.out.print("播");
22 System.out.print("客");
23 System.out.print("\r\n");
24 }
25 }
26}
使用非静态同步方法
1class Printer2 {
2 Demo d = new Demo();
3 //非静态的同步方法的锁对象是神马?
4 //答:非静态的同步方法的锁对象是this
5 //静态的同步方法的锁对象是什么?
6 //是该类的字节码对象
7 public synchronized void print1() { //同步方法只需要在方法上加synchronized关键字即可
8 System.out.print("黑");
9 System.out.print("马");
10 System.out.print("程");
11 System.out.print("序");
12 System.out.print("员");
13 System.out.print("\r\n");
14 }
15
16 public void print2() {
17 //synchronized(new Demo()) { //锁对象不能用匿名对象,因为匿名对象不是同一个对象
18 synchronized(this) {
19 System.out.print("传");
20 System.out.print("智");
21 System.out.print("播");
22 System.out.print("客");
23 System.out.print("\r\n");
24 }
25 }
26}
9.线程安全问题
第一种用Thread类继承
1public class Demo3_Ticket {
2
3 /**
4 * 需求:铁路售票,一共100张,通过四个窗口卖完.
5 */
6 public static void main(String[] args) {
7 new Ticket().start();
8 new Ticket().start();
9 new Ticket().start();
10 new Ticket().start();
11 }
12
13}
14
15class Ticket extends Thread {
16 private static int ticket = 100; //静态共享
17 //private static Object obj = new Object(); //如果用引用数据类型成员变量当作锁对象,必须是静态的
18 public void run() {
19 while(true) {
20 synchronized(Ticket.class) {
21 if(ticket <= 0) {
22 break;
23 }
24 try {
25 Thread.sleep(10); //线程1睡,线程2睡,线程3睡,线程4睡
26 } catch (InterruptedException e) {
27
28 e.printStackTrace();
29 }
30 System.out.println(getName() + "...这是第" + ticket-- + "号票");
31 }
32 }
33 }
34}
第二种用Runnable
1public class Demo4_Ticket {
2
3 /**
4 * @param args
5 * 火车站卖票的例子用实现Runnable接口
6 */
7 public static void main(String[] args) {
8 MyTicket mt = new MyTicket();
9 new Thread(mt).start();
10 new Thread(mt).start();
11 new Thread(mt).start();
12 new Thread(mt).start();
13
14 /*Thread t1 = new Thread(mt); //多次启动一个线程是非法的
15 t1.start();
16 t1.start();
17 t1.start();
18 t1.start();*/
19 }
20
21}
22
23class MyTicket implements Runnable {
24 private int tickets = 100;
25 @Override
26 public void run() {
27 while(true) {
28 synchronized(this) {
29 if(tickets <= 0) {
30 break;
31 }
32 try {
33 Thread.sleep(10); //线程1睡,线程2睡,线程3睡,线程4睡
34 } catch (InterruptedException e) {
35
36 e.printStackTrace();
37 }
38 System.out.println(Thread.currentThread().getName() + "...这是第" + tickets-- + "号票");
39 }
40 }
41 }
42}
10.多线程死锁问题
互相不肯释放
所以避免死锁 同步代码块不要嵌套
1public class Demo5_DeadLock {
2
3 /**
4 * @param args
5 */
6 private static String s1 = "筷子左";
7 private static String s2 = "筷子右";
8
9 public static void main(String[] args) {
10 new Thread() {
11 public void run() {
12 while(true) {
13 synchronized(s1) {
14 System.out.println(getName() + "...获取" + s1 + "等待" + s2);
15 synchronized(s2) {
16 System.out.println(getName() + "...拿到" + s2 + "开吃");
17 }
18 }
19 }
20 }
21 }.start();
22
23 new Thread() {
24 public void run() {
25 while(true) {
26 synchronized(s2) {
27 System.out.println(getName() + "...获取" + s2 + "等待" + s1);
28 synchronized(s1) {
29 System.out.println(getName() + "...拿到" + s1 + "开吃");
30 }
31 }
32 }
33 }
34 }.start();
35 }
36}
Collections 可以调用方法始集合都可以变成线程安全
所以说Vector和Hashtable都被淘汰了
摸鱼科技资讯
以上是关于Java之多线程详解的主要内容,如果未能解决你的问题,请参考以下文章