黑马程序员——java学习6(127-151)——多线程

1、多线程

进程:是一个正在执行中的程序。

  每一个进程执行都有一个执行顺序,该顺序是一个执行路径,或者叫控制单元。

线程:就是进程中的一个独立的控制单元

  线程在控制着进程的执行

一个进程中至少有一个线程。

Java VM 启动的时候回有一个进程java.exe

该进程中至少一个线程负责java程序的执行

而且这个线程运行的代码存在于main方法中

该线程称之为主线程。

扩展:其实更细节说明jvm,启动的还有垃圾回收机制的线程

 创建线程的方式

1.1继承Thread

(1)定义类,继承Thread

(2)覆写Thread类中的run方法

  目的:将自定义的代码存储在run方法中,让线程运行。

(3)调用线程的start方法,

   作用:启动线程,调用run方法.

多线程的随机性:每次运行结果不同,单核CPU在某一个时刻只能执行一个程序,cpu在快速切换,以达到看上去同时运行的效果。

1.1.1为何覆盖run方法?

Thread用于描述线程,该类就定义了一个功能,用于存储线程要运行的代码,该存储功能就是run方法。

即run用于存储线程要运行的代码。

 1 class Demo extends Thread{
 2     public void run(){
 3         for(int x=0;x<60;x++)
 4         {
 5             System.out.println("demo run---"+x);
 6         }
 7     };
 8 }
 9 public class ThreadDemo {
10     public static void main(String[] args) {
11         Demo d=new Demo();
12         //d.start();//开启线程并执行该线程的run方法,demorun与helloworld相互执行
13         d.run();//仅仅是对象调用方法,而线程创建了,并没有运行,demorun全部执行完才执行helloworld
14         for(int x=0;x<60;x++)
15         {
16             System.out.println("Helloworld---"+x);
17         }
18 }
19 }

1.1.2、线程方法

static Thread currentThread();获取当前线程对象,静态的,调用时为Thread.currentThread();

getName();获取线程名称

设置线程名称:setName();或者构造函数

1.2、创建线程的第二种方法(常用)

(1)定义类实现Runnable接口

(2)覆盖Runnable接口中的run方法

  将线程要运行的代码存放在该run方法中

(3)通过Thread类建立线程对象

(4)将Runnable接口的子类对象作为实际参数传递给Thread类的构造函数

  为何将Runnable接口的子类对象传递

  因为自定义的run方法所述的对象是Runnable接口的子类对象

  所以要让线程去执行指定对象的run方法,就必须明确该run所属对象

(5)调用Thread类的start方法开启线程并调用Runnable接口子类的run方法

好处:避免单继承的局限性

区别:

继承Runnable:线程代码存放在Thread子类的run方法中,无父类时用

实现Runnable:线程代码存在接口的子类的run方法中,避免局限

 1 /*
 2  * 需求:
 3  * 银行有一个金库
 4  * 有两个储户分别存300元,每次存100,分3次
 5  * 
 6  * 目的:该程序是否有安全问题,如果有,如何解决?
 7  * 
 8  * 如何找问题
 9  * 1、明确哪些代码是多线程运行代码
10  * 2、明确共享数据
11  * 3、明确多线程运行代码中哪些语句是操作共享数据的
12  * */
13 
14 package learn;
15 class Bank
16 {
17     private int sum;
18     public void add(int n)
19     {
20         sum=sum+n;
21         try{Thread.sleep(10);}catch(Exception e){}
22         System.out.println("sum"+sum);
23     }
24 }
25 class Cus implements Runnable
26 {
27     private Bank b=new Bank();
28     public void run()
29     {
30         for(int x=0;x<3;x++)
31         {
32             b.add(100);
33         }
34     }
35 }
36 public class BankDemo {
37     public static void main(String[] args) {
38         Cus c=new Cus();
39         Thread t1=new Thread(c);
40         Thread t2=new Thread(c);
41         t1.start();
42         t2.start();
43     }
44 }

1.3、安全问题

当多条语句操作同一个线程共享数据时,一个线程对多条语句只执行了一部分,还没有执行完,

另一个线程参与执行,导致共享数据的错误

解决办法:

  对多条操作共享数据的语句,只能让一个线程都执行完。在执行过程中其他线程不可参与执行

同步代码块。

synchronized(对象)

{

  需要被同步的代码  

}

例:火车上的厕所

同步的前提:

(1)必须有两个或以上的线程

(2)必须多个线程使用同一个锁

PS:必须保证同步中只有一个线程在执行

好处:解决了多线程的安全问题

弊端:判断锁,消耗资源

同步的方式:

(1)同步代码块,使用的锁是obj

(2)同步函数(作修饰符加入),使用的锁是this

(3)静态方法用过的锁是class,即该方法所在类的字节码文件对象,函数列表中写作(类名.class)

1.4、单例

 1 package learn;
 2 /*
 3  * class Single
 4  * {
 5  *     private static final Single s=new Single();
 6  *  private Single(){};
 7  *  public static Single getInstance()
 8  *  {
 9  *      return s;
10  *  }
11  * }
12  * */延迟加载的单例模式示例
13 class Single
14 {
15     private static Single s=null;
16     private Single(){}
17     public static Single getInstance(){
18         if(s==null)
19             synchronized(Single.class){
20                 if(s==null)
21                     s=new Single();
22             }
23         return s;
24     }
25 }
26 public class SingleDemo {
27 
28 }

1.5、死锁

同步中嵌套同步

 1 package learn;
 2 class Test implements Runnable
 3 {
 4     private boolean flag;
 5     Test(boolean flag)
 6     {
 7         this.flag=flag;
 8     }
 9     public void run(){
10         if(flag)
11         {
12             synchronized(MyLock.locka)
13             {
14                 System.out.println("if locka ");
15                 synchronized(MyLock.lockb)
16                 {
17                     System.out.println("if lockb");
18                 }
19             }
20         }
21         else
22         {
23             synchronized(MyLock.lockb)
24             {
25                 System.out.println("else lockb");
26                 synchronized(MyLock.locka)
27                 {
28                     System.out.println("else locka");
29                 }
30             }
31         }
32     }
33 }
34 
35 class MyLock{
36     static Object locka=new Object();
37     static Object lockb=new Object();
38 }
39 public class DeadLockTicket {
40     public static void main(String[] args) {
41         Thread t1=new Thread(new Test(true));
42         Thread t2=new Thread(new Test(false));
43         t1.start();
44         t2.start();
45     }
46 }

1.6、线程间通信

1.6.1等待唤醒机制

wait都存放在线程池,notify通常唤醒第一个被等待的,notifyall唤醒全部,三者都必须用在同步中(因须有锁),也需加前缀obj.wait

只有同一个锁上的被等待线程,可以被同一个锁上的notify唤醒

而锁是任意对象,所以可以被任意对象调用的方法定义在object类中

 1 package learn;
 2 class Res{
 3     private String name;
 4     private String sex;
 5     private boolean flag=false;
 6     public synchronized void set(String name,String sex)
 7     {
 8         if(flag)
 9             try{this.wait();}catch(Exception e){}
10         this.name=name;
11         this.sex=sex;
12         flag=true;
13         this.notify();
14     }
15     public synchronized void out()
16     {
17         if(!flag)
18             try{this.wait();}catch(Exception e){}
19         System.out.println(name+"..."+sex);
20         flag=false;
21         this.notify();
22     }
23 }
24 
25 class Input implements Runnable{
26     private Res r;
27     Input(Res r)
28     {
29         this.r=r;
30     }
31     public void run()
32     {
33         int x=0;
34         while(true)
35         {
36             if(x==0)        
37                 r.set("mike", "nan");            
38             else            
39                 r.set("lili", "nv");
40             x=(x+1)%2; 
41         }
42     }
43 
44 }
45 
46 class Output implements Runnable{
47     private Res r;
48     Output(Res r)
49     {
50         this.r=r;
51     } 
52     public void run()
53     {
54         int x=0;
55         while(true)
56         {
57             r.out();
58         }
59     }
60 }
61 public class InputOutputDemo {
62     public static void main(String[] args) {
63         Res r=new Res();
64         //匿名对象
65         new Thread(new Input(r)).start();
66         new Thread(new Output(r)).start();
67         
68 //        Input in=new Input(r);
69 //        Output out =new Output(r);
70 //        Thread t1=new Thread(in);
71 //        Thread t2=new Thread(out);
72 //        t1.start();
73 //        t2.start();
74     }
75 }

1.6.2、生产者消费者例子

多个生产者,多个消费者,while循环判断标记,notifyAll

 1 package pack;
 2 class Resource{
 3     private String name;
 4     private int count=1;
 5     private boolean flag=false;
 6     
 7     public  synchronized void set(String name)
 8     {
 9         while(flag)
10             try{wait();}catch(Exception e){}
11         this.name=name+"..."+count++;
12         System.out.println(Thread.currentThread().getName()+"生产者"+this.name);
13         flag=true;
14         this.notifyAll();
15     }
16     public synchronized void out(){
17         while(!flag)
18             try{wait();}catch(Exception e){}
19         System.out.println(Thread.currentThread().getName()+"消费者............"+this.name);
20         flag=false;
21         this.notifyAll();
22     }
23 }
24 
25 class Producer implements Runnable{
26     private Resource res;
27     Producer(Resource res){
28         this.res=res;
29     }
30     public void run(){
31         while(true)
32         {
33             res.set("+商品+");
34         }
35     }
36 }
37 
38 class Consumer implements Runnable{
39     private Resource res;
40     Consumer(Resource res){
41         this.res=res;
42     }
43     public void run(){
44         while(true)
45         {
46             res.out();
47         }
48     }
49 }
50 public class ProduceConsumerDemo {
51     public static void main(String[] args) {
52         Resource r=new Resource();
53         Producer pro=new Producer(r);
54         Consumer con=new Consumer(r);
55         
56         Thread t1=new Thread(pro);
57         Thread t2=new Thread(pro);
58         Thread t3=new Thread(con);
59         Thread t4=new Thread(con);
60         t1.start();
61         t2.start();
62         t3.start();
63         t4.start();
64     }
65 }

1.6.3、多线程升级锁Lock

JDK1.5中提供了多线程升级解决方案

将同步syn替换成现实Lock操作

将Object中的wait,notify,notifyAll,替换了Condition对象

该对象可以Lock锁,进行获取

实现了本方只唤醒对方操作

 1 package learn;
 2 
 3 import java.util.concurrent.locks.Condition;
 4 import java.util.concurrent.locks.Lock;
 5 import java.util.concurrent.locks.ReentrantLock;
 6 
 7 class Resource{
 8     private String name;
 9     private int count=1;
10     private boolean flag=false;
11     private Lock lock=new ReentrantLock();
12     private Condition condition_pro=lock.newCondition();
13     private Condition condition_con=lock.newCondition();
14     public  void set(String name)throws InterruptedException
15     {
16         lock.lock();
17         try{    
18             while(flag)
19             condition_pro.await();
20             this.name=name+"..."+count++;
21             System.out.println(Thread.currentThread().getName()+"生产者"+this.name);
22             flag=true;    
23             condition_con.signal();
24         }
25         finally{
26             lock.unlock();
27         }
28         
29     }
30     public void out()throws InterruptedException{
31         lock.lock();
32         try{
33         while(!flag)
34             condition_con.await();
35         System.out.println(Thread.currentThread().getName()+"消费者............"+this.name);
36         flag=false;
37         condition_pro.signal();
38         }
39         
40         finally
41         {
42             lock.unlock();
43         }
44     }
45 }
46 
47 class Producer implements Runnable{
48     private Resource res;
49     Producer(Resource res){
50         this.res=res;
51     }
52     public void run(){
53         try{
54         while(true)
55         {
56             res.set("+商品+");
57         }
58         }
59         catch(Exception e)
60         {}
61     }
62 }
63 
64 class Consumer implements Runnable{
65     private Resource res;
66     Consumer(Resource res){
67         this.res=res;
68     }
69     public void run(){
70         while(true)
71         {
72             try{
73             res.out();
74             }
75             catch(Exception e)
76             {}
77         }
78     }
79 }
80 public class ProducerConsumerDemo2 {
81     public static void main(String[] args) {
82         Resource r=new Resource();
83         Producer pro=new Producer(r);
84         Consumer con=new Consumer(r);
85         
86         Thread t1=new Thread(pro);
87         Thread t2=new Thread(pro);
88         Thread t3=new Thread(con);
89         Thread t4=new Thread(con);
90         t1.start();
91         t2.start();
92         t3.start();
93         t4.start();
94     }
95 }

1.7、如何停止线程?

stop方法已过时,只能让run方法结束,即控制run中的循环结束

特殊情况:

当线程处于冻结状态

就不会读取到标记,那么线程就不会结束

当没有指定的方式让冻结的线程回复到运行状态时,这时需要对冻结进行清除

强制让线程回复到运行状态中来,这样就可以操作标记让线程结束

interrput();方法

1.8、守护线程

1.9、join

当A线程执行到B线程的join方法时,A就会等到B执行完再执行

 1 package pack;
 2 class Demo implements Runnable
 3 {
 4     public void run(){
 5         for(int x=0;x<70;x++)
 6             System.out.println(Thread.currentThread().getName()+"..."+x);
 7     }
 8 }
 9 public class JoinDemo {
10  public static void main(String[] args) throws InterruptedException {
11     Demo d=new Demo();
12     Thread t1=new Thread(d);
13     Thread t2=new Thread(d);
14     t1.start();
15     //t1抢夺CPU执行权
16     t1.join();
17     t2.start();
18     
19     for(int x=0;x<80;x++)
20     {
21         System.out.println("main..."+x);
22     }
23     System.out.println("over");
24 }
25 }

1.9.1、线程优先级

所有线程默认优先级5,一共1-10级

1.9.2、yield();

尽量轮流执行

1.9.3、开发举例

 1 package pack;
 2 
 3 public class ThreadTest {
 4     public static void main(String[] args) {
 5         //匿名内部类更方便
 6         new Thread()
 7         {
 8             public void run()
 9             {
10                 for(int x=0;x<100;x++)
11                 {
12                     System.out.println(Thread.currentThread().getName()+"..."+x);
13                 }
14             }
15         }.start();
16         
17         for(int x=0;x<100;x++)
18         {
19             System.out.println(Thread.currentThread().getName()+"..."+x);
20         }
21             
22         
23         Runnable r=new Runnable(){
24             public void run(){
25                 for(int x=0;x<100;x++)
26                 {
27                     System.out.println(Thread.currentThread().getName()+"..."+x);
28                 }
29                 
30             }    
31         };
32         new Thread(r).start();
33     }
34 }
35 
36 //麻烦
37 //class Test1 extends Thread
38 //{
39 //    public void run()
40 //    {
41 //        for(int x=0;x<100;x++)
42 //            System.out.println(Thread.currentThread().getName());
43 //    }
44 //}
原文地址:https://www.cnblogs.com/sunxlfree1206/p/4679111.html