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之多线程详解的主要内容,如果未能解决你的问题,请参考以下文章

java之多线程的理解

java基础之多线程

Java基础之多线程

Java之多线程讲解

Java线程池详解

Java线程池详解