我是靠谱客的博主 悲凉豆芽,这篇文章主要介绍黑马程序员——java多线程,现在分享给大家,希望可以做个参考。

android培训、 java培训、期待与您交流! ----------


java——多线程学习总结

    1、进程和线程的含义
        1)、在多任务系统中,每个独立的执行的程序称为进程。
        2)、每个进程里面含有一个或多个线程,一个线程就是一个程序中的执行过程,如果一个程序中有多个代码要同时交替运行,就会产生多个线程,并指定每个线程上面运行的代码,这就是多线程。

一、首先我们看看单线程的编程

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class ThreadTest{ public static void main(String[] args){ new Run().run(); while(true){ System.out.println("main--------->"+Thread.currentThread().getName()); } } } class Run{ public void run(){ while(true){ System.out.println("Run--------->"+Thread.currentThread().getName()); } } }
我们看看打印结果

复制代码
1
2
3
4
5
6
Run--------->main Run--------->main Run--------->main Run--------->main Run--------->main Run--------->main
结果只打印出了run---->,这说明当我们的main方法在执行new Run().run()的时候进入了死循环,且由于我们是单线程编程,所以没有其他线程去执行下面的System.out.println("main")。


二、下面我们改改代码,看看多线程编程的结果。

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class ThreadTest{ public static void main(String[] args){ new Run().start(); while(true){ System.out.println("main--------->"+Thread.currentThread().getName()); } } } class Run extends Thread { public void run(){ while(true){ System.out.println("Run--------->"+Thread.currentThread().getName()); } } }
打印结果:


复制代码
1
2
3
4
5
6
7
8
9
10
Run--------->Thread-0 main--------->main main--------->main main--------->main main--------->main main--------->main Run--------->Thread-0 Run--------->Thread-0 Run--------->Thread-0 Run--------->Thread-0
我们看到下面的代码的Run类是继承了线程类Thread,然后调用Thread里面的start方法,从而调用自己的run方法。当主线程main执行到new Run().start()的时候新开了一个线程去执行RUN线程,主线程继续往下面执行。


三、前台线程和后台线程(也称为用户线程和守护线程),在java中只有前台线程在,java程序就不会结束,那么像上面的代码,把Run r = new Run(); r.setDaemon(true)的时候,就是把这个线程设置为后台线程(守护线程)。当main线程运行完的时候,这个run线程也随之结束。典型的java垃圾回收线程就是一个守护线程,当程序中没有其他线程的时候,这个垃圾回收线程也就自动结束了。

四、join方法,join方法是合并线程,比如上面的代码run.join(),这个时候就只有一个线程了,只有当run运行结束后,才可以执行main线程。我们查看APIjoin方法有3个重载

void join()
等待该线程终止。
void join(long millis)
等待该线程终止的时间最长为millis毫秒。
void join(long millis, int nanos)
等待该线程终止的时间最长为millis毫秒 +nanos纳秒。

五、常用的生成线程的方法,1、继承Thread类,实现Runnable接口,然后调用new Thread(Runnable).start();两者区别

     1)、使用继承Thread的线程类,有一定的局限性,不能再继承其他类。

     2)、在需要多个线程访问同一个资源的时候,实现Runnable接口要比Thread方便。下面我们用买火车票去模拟看看

     在买火车票的时候,我们启动4个线程,首先就必须要new4个Thread对象,

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class ThreadTest{ public static void main(String[] args){     new Run().start(); //这里new了4个Thread对象,所以产生了4个线程,但运行结果发现4个线程都有自己的资源,都有自己的那100张票,而不是共有的100张票      new Run().start();     new Run().start();     new Run().start(); } } class Run extends Thread{     int i = 100;     public void run(){      while(true){          if(i>0){              System.out.println(Thread.currentThread().getName()+"--------------->"+i);              i--;          }      } }
我们把上面的代码更改成实现Runnable接口看看效果。


复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class ThreadTest{      public static void main(String[] args){         Run r = new Run();//这里利用实现Runnalbe接口来实现,下面虽然也new了4个Thread,但他们都公用了一个RUN对象,都是公用的一个资源         new Thread(r).start();          new Thread(r).start();         new Thread(r).start();         new Thread(r).start();      } } class Run implements Runnable{     int i = 100;     public void run(){      while(true){          if(i>0){              System.out.println(Thread.currentThread().getName()+"--------------->"+i);              i--;          }      } }
这里需要给大家特别提示点,对于变量定义的位置,尽量不要定义在run方法里面,因为这个run方法是线程执行的具体代码,如果把变量定义在了run方法里面,那么就达不到共享资源了。


六、多线程同步问题

像我们上面的代码其实会出现不同问题,运行结果可以看见我们卖出的票有可能卖到负数了。这就是我们的4个线程在访问同一个资源的时候,并没有实现同步问题。

实现同步问题有两种方式1、synchronized 代码块  2、synchronized 方法。

我们先看看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
public class ThreadTest{ public static void main(String[] args){ Run r = new Run(); new Thread(r).start(); new Thread(r).start(); new Thread(r).start(); new Thread(r).start(); } } class Run implements Runnable{ int i = 1000; String str=""; public void run(){ while(true){ synchronized(str){ if(i>0){ try{Thread.sleep(10);}catch(Exception e){}//如果这里的sleep的时间太长或者上面的I定义太小的话可能会出现运行的时候你只能看见一个线程在卖票,原因是当可能在睡眠的时候CPU让其他3个线程执行的时候我们的锁没有释放,其他3个线程运行不了,刚刚当我们的代码快运行完后,CPU又刚好执行到我们第一个线程。 System.out.println(Thread.currentThread().getName()+"--------------->"+i--); } } } } }

下面是采用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
public class ThreadTest{ public static void main(String[] args){ Run r = new Run(); new Thread(r).start(); new Thread(r).start(); new Thread(r).start(); new Thread(r).start(); } } class Run implements Runnable{ int i = 1000; String str=""; public void run(){ while(true){ sale(); } } public synchronized void sale(){ if(i>0){ try{Thread.sleep(10);}catch(Exception e){}//如果这里的sleep的时间太长或者上面的I定义太小的话可能会出现运行的时候你只能看见一个线程在卖票,原因是当可能在睡眠的时候CPU让其他3个线程执行的时候我们的锁没有释放,其他3个线程运行不了,刚刚当我们的代码快运行完后,CPU又刚好执行到我们第一个线程。 System.out.println(Thread.currentThread().getName()+"--------------->"+i--); } } }
复制代码
1
synchronized methods(){} 与synchronized(this){}之间没有什么区别,只是synchronized methods(){} 便于阅读理解,而synchronized(this){}可以更精确的控制冲突限制访问区域,有时候表现更高效率。

synchronized关键字是不能继承的,继承时子类的覆盖方法必须显示定义成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
public class ThreadTest{ public static void main(String[] args){ Run r = new Run(); new Thread(r).start(); new Thread(r).start(); } } class Run implements Runnable{ String str = ""; public void run(){ while(true){ synchronized(str){//这里获取到str这个对象锁后 try{Thread.sleep(10);}catch(Exception e){} synchronized(this){//第一次运行到这里可以获取this对象锁,但第二次就被下面的run2得到了this对象锁且没释放 System.out.println("RUN1------->"+Thread.currentThread().getName()); } } run2();//这里执行run2方法,run2方法中获取了this对象。但当他要往下面执行的时候需要str这个锁,但上面的的run已经获取到了str锁。并且在等待this对象锁。 } } public synchronized void run2(){ try{Thread.sleep(100);}catch(Exception e){} synchronized(str){ System.out.println("run2----------------"+Thread.currentThread().getName()); } } }

八、售卖火车票多线程访问不同锁定问题

大家可以从下面的代码开出来,虽然我们看是采取了线程同步机制,而且是运用了synchronized代码块,和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
/* 需求:简单的卖票程序 多个窗口同时卖票 创建线程的第二种方式: 通过实现Runnable接口的子类对象作为实际参数传递给Thread的构造函数。 然后通过Thread类的start()方法去启用Runnable接口中run方法。 继承Thread和实现Runnable接口的区别: 1、继续Thread的方式有一定的局限性,而通过实现Runnable接口可以实现多继承多扩展。 2、实现Runnable接口可以让资源共享。 注意:new Thread()类就是创建一个线程,然后调用new Thread().start()方法 才是告诉cpu我这个线程已经准备就绪,你可以来调用我了。 我们为了解决线程同步的问题,可以采用:同步代码块。 我们今天把同步代码块,改为同步函数试试。 同步函数使用的所对象是this 通过该程序进行验证, 使用两个线程来卖票,一个线程在同步代码块中,一个线程在同步函数中。 */ class Ticket implements Runnable{ private int tick = 1000; Object obj = new Object();//这里生产一个obj对象,来作为同步代码块的对象。 public boolean flag = true; public void run(){ if(flag){ synchronized (obj){//这里用的obj锁 while(true){ if(tick>0){ try{Thread.sleep(100);}catch(Exception e){} System.out.println(Thread.currentThread().getName()+"sale : "+tick--); } } } }else{ while(true){ show(); } } } //我们在此封装一个方法,用于卖票 public synchronized void show(){//这里是this锁就造成 上下两个方法的锁不一样。就造成不同步 if(tick>0){ try{Thread.sleep(10);}catch(Exception e){} System.out.println(Thread.currentThread().getName()+"show : "+tick--); } } } class ThreadThis{ public static void main(String[] args)throws Exception{ Ticket t = new Ticket(); Thread thread1 = new Thread(t); Thread thread2 = new Thread(t); thread1.start(); Thread.sleep(10);//让主线程睡10毫秒,好让线程分开执行到 t.flag = false; thread2.start(); } }


经过上面的这一些代码,我们来总结下线程编程应该注意到问题。

1、通过是实现Runnbale接口和继承Thread的区别

    继承:线程代码存放在Thread子类run方法中,就是子类重写父类的run方法。

    实现:线程代码存放在接口顶接口Runnable的实现类run方法中,实现接口中的run方法。

    相对比较,采取实现Runnable接口避免了单继承的局限性,在多线程编程的时候建议使用实现方式

2、直接调用run方法和调用start()方法,然后通过start方法调用run方法有什么区别?

    1、如果直接运行run的话,其实就跟我们调用普通方法一样,根本就和线程没什么关系,他就是一个单线程顺序执行而已。

    2、而调用start方法的时候,他是通过Thread线程类来调用的,从而在start里面调用run方法,从而作为一个Thread方法执行下去,实现多线程。

3、

转载于:https://my.oschina.net/u/158350/blog/99319

最后

以上就是悲凉豆芽最近收集整理的关于黑马程序员——java多线程的全部内容,更多相关黑马程序员——java多线程内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(120)

评论列表共有 0 条评论

立即
投稿
返回
顶部