黑马程序员—多线程


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



    日志完成于11年12月19日
    
    开通了CSDN博客才知道三天后才能发表文章,所以趁着这段时间自己就赶紧把自学的知识好好地整理了一下!禁期一解,就可以展现自己的劳动成果了!现在进入正题......

    之前在自学“多线程”这一块知识时,就感觉挺费劲的,所以用了一天的时间又仔仔细细的看了看毕向东老师关于这一块知识的讲解,有了更深入更透彻的理解,有点兴奋加 激动,嘻嘻!

1、进程、线程概念

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

线程:就是进程中的一个独立的控制单元。线程在控制着进程的执行。

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

【 J VM  启动时(通过命令行参数java启动)会有一个java.exe进程。该进程中至少有一个线程负责java程序的执行。而且这个线程运行的代码存在于main方法中。该线程称之为主线程。

扩展:更加细节的说明jvm,其实jvm启动不止一个线程,还有负责垃圾回收机制的线程。】

 

2、创建线程方式有两种

第一种方式:继承Thread类。

步骤:

1,定义类继承Thread。

2,复写Thread类中的run方法。

目的:将自定义代码存储在run方法中,让线程通过start方法启动后来运行。

3,调用线程的start方法,

该方法两个作用:启动线程,调用run方法。

范例:

class  Demo  extends  Thread {

         public void run(){

                   for(int x=0; x<60; x++)

                            System.out.println("demo run----"+x);

         }

}

Public  class  ThreadDemo {

         public static void main(String[] args) {

                   Demo d = new Demo();  //创建一个线程。

                   d.run();  //仅仅是对象调用run方法。而线程创建了,并没有运行。

        d.start();  //开启线程并执行该线程的run方法。

 

                   for(int x=0; x<60; x++)

                            System.out.println("Hello World!--"+x);

         }

}

 

3、通过观察运行结果可以:发现运行结果每一次都不同?

因为多个线程都获取CPU的执行权,CPU执行到谁,谁就运行。(这就是多线程的一个特性:随机性。)我们可以形象的把多线程的这种运行行为理解为在互相抢夺CPU的执行权。

明确一点,在某一个时刻,只能有一个程序在运行。(多核除外)

CPU在做着快速的切换,以达到看上去是同时运行的效果。

 

练习:创建两个线程,和主线程交替运行。

线程都有自己默认的名称:Thread-编号该编号从0开始。

static  Thread  currentThread():获取当前线程对象。

getName():获取线程名称。

设置线程名称:setName或者构造函数。

class Test extends Thread {

         public Test(String name){

                   super(name);

         }

         public void run(){

                   for(int x=0; x<60; x++){

                            System.out.println(this.getName()+"  run: "+x);  //使用父类中的getName()取得线程默认的名称

                   }

         }

}

 

public class ThreadTest {

         public static void main(String[] args) {

                   Test t1 = new Test("one");

                   Test t2 = new Test("two");

                   t1.start();

                   t2.start();

                   for(int x=0; x<60; x++){

                            System.out.println("main: "+x);

                   }

         }

}

4、线程的运行状态:

 

 

5、第二种方式:实现Runable接口

步骤:

1,定义类实现Runnable接口

2,覆盖Runnable接口中的run方法。

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

3,通过Thread类建立线程对象。

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

         为什么要将Runnable接口的子类对象传递给Thread的构造函数。

         :因为,自定义的run方法所属的对象是Runnable接口的子类对象。

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

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

售票范例:多个窗口同时卖100张票。

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

class Ticket  implements  Runnable // extends Thread {    //继承Thread类

         private  int  tick = 100;  //private static int tick = 100;    将tick定义为全局变量

         public void run(){

                   while(true){

                            if(tick>0){

                                     System.out.println(Thread.currentThread().getName()+"  sale : "+ tick--);

                            }

                   }

         }

}

 

public class  TicketDemo{

         public static void main(String[] args) {

                   Ticket t = new Ticket();                       //Ticket t1=new Ticket();  创建4个线程类,共享tick变量

                   Thread t1 = new Thread(t);  //创建了一个线程;  // Ticket t2=new Ticket();

                   Thread t2 = new Thread(t);  //创建了一个线程;  // Ticket t3=new Ticket();

                   Thread t3 = new Thread(t);  //创建了一个线程;  // Ticket t4=new Ticket();

                   Thread t4 = new Thread(t);  //创建了一个线程;  //t1. start();  t2. start();  t3. start();  t4. start();

                   t1.start();

                   t2.start();

                   t3.start();

                   t4.start();

         }

}

 

6、通过分析发现:打印出了0,-1,-2等错票,则多线程的运行出现了安全问题。

问题的原因重点):

         当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分,还没有执行完,另一个线程参与进来执行。导致共享数据的错误。

解决办法

         对多条操作共享数据的语句,只能让一个线程都执行完。在执行过程中,其他线程不可以参与执行。Java对于多线程的安全问题提供了专业的解决方式,就是同步代码块

synchronized(对象)

{

需要被同步的代码

}

(PS: 同步就是协同步调,按预定的先后次序进行运行。如:你说完,我再说。“同”字从字面上容易理解为一起动作。其实不是,“同”字应是指协同、协助、互相配合。

对象如同锁( 监视器 ),持有锁的线程可以在同步中执行。没有持有锁的线程即使获取cpu的执行权,也进不去,因为没有获取锁。     经典例子:火车上的卫生间。

同步的前提

1,必须要有两个或者两个以上的线程。

2,必须是多个线程使用同一个锁

必须保证同步中只能有一个线程在运行。

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

弊端:多个线程需要判断锁,较为消耗资源。

 

class Ticket implements Runnable {

         private  int tick = 100;

         Object obj = new Object();

         public void run(){    //在此处不能手动抛出异常,因为覆写的run方法中没有抛出异常

                   while(true){

                            synchronized(obj){  //同步代码块:对于多线程,只有当一个线程执行完操作共享数据的语句后,才允许下一个线程再执行,否则会出现多线程的安全问题

                                     if(tick>0){

                                               try{  Thread.sleep(10);

                                               } catch ( Exception e ) { }

                                               System.out.println(Thread.currentThread().getName()+"  sale : "+ tick--);

                                     }

                            }

                   }

         }

}

public class  TicketDemo02{

         public static void main(String[] args) {

                   Ticket t = new Ticket();

                   Thread t1 = new Thread(t);

                   Thread t2 = new Thread(t);

                   Thread t3 = new Thread(t);

                   Thread t4 = new Thread(t);

                   t1.start();  t2.start();  t3.start();  t4.start();

         }

}

需求:银行有一个金库,有两个储户分别存300元,每次存100元,存3次。

目的:该程序是否有安全问题,如果有,如何解决?

思路:如何找问题:

1,明确哪些代码是多线程运行代码

2,明确共享数据。

3,明确多线程运行代码中哪些语句是操作共享数据的。

class Bank{

         private int sum//共享数据

         //Object obj = new Object();

         public synchronized void add(int n){    // 同步函数

         //public void add(int n){

                   //synchronized(obj) {

                            sum = sum + n;

                            try{Thread.sleep(10);}catch(Exception e){}

                            System.out.println("sum="+sum);

                   //}

         }

}

class Cus implements Runnable{

         private Bank b = new Bank();

         public void run() {  //run()里面的代码是多线程运行的代码         

                   for(int x=0; x<3; x++){

                            b.add(100);  //多线程运行代码中add()是操作共享数据sum的

                   }

         }

}

public class  BankDemo

{

         public static void main(String[] args)

         {

                   Cus c = new Cus();

                   Thread t1 = new Thread(c);

                   Thread t2 = new Thread(c);

                   t1.start();

                   t2.start();

         }

}

:public synchronized viod xxx() 同步函数用的是哪一个锁呢?

:同步函数需要被对象调用,那么函数都有一个所属对象引用,就是this,所以同步函数使用的锁是this。

通过该程序验证:使用两个线程来买票,一个线程在同步代码块中,一个线程在同步函数中,都在执行买票动作。

class Ticket implements Runnable {

         private  int  tick = 100;

         Object obj = new Object();

         boolean flag = true;

         public void run(){

                   if(flag){

                            while(true){  //线程t1

                                synchronized(this){ //此时的锁都是this,若此处对象是obj,则同步的锁不同,存在线程安全问题

                                               if(tick>0){

                                                        try{Thread.sleep(10);}catch(Exception e){}

                                                        System.out.println(Thread.currentThread().getName()+"....code : "+ tick--);

                                               }

                                     }

                            }

                   }

                   else{

                            while(true)

                                     show();  //同步函数使用的锁是this

                   }

         }

         public synchronized void show(){  //同步函数使用的锁是this

                   if(tick>0){

                            try{Thread.sleep(10);}catch(Exception e){}

                            System.out.println(Thread.currentThread().getName()+"....show.... : "+ tick--);

                   }

         }

}

 

public class  ThisLock{

         public static void main(String[] args) {

                   Ticket t = new Ticket();

                   Thread t1 = new Thread(t);

                   Thread t2 = new Thread(t);

                   t1.start();  //启动线程t1,此时主线程正在运行,所以线程t1处于阻塞(临界)状态

                   try{Thread.sleep(10);}catch(Exception e){}  //主线程休眠10毫秒,此时线程具有t1执行权

                   t.flag = false;

                   t2.start();

         }

}

publicstatic synchronzied void xxx()静态同步函数的锁是该方法所属类的字节码文件对象,即:类名.class

单例设计模式

饿汉式

class Single{

         private  static  final  Single s  =  new  Single();

         private  Single() {}

         public static Single getInstance(){

                   return s;

         }

}

懒汉式:特点在于实例的延迟加载,如果多线程访问时会存在安全问题,可以加同步来解决,但有些低效,加同步时的锁为该类所属的字节码文件对象,即类名.class。

class Single{

         private static Single s = null;

         private  Single() {}

         public static  Single getInstance(){

                   if(s==null){

                            synchronized(Single.class){

                                     if(s==null)

s = new Single();

                            }

                   }

                   return s;

         }

}

public class SingleDemo {

         public static void main(String[] args) {

                   System.out.println("Hello World!");

         }

}

死锁:同步中嵌套同步,易产生死锁。(理解掌握)

class Test implements Runnable{

         private boolean flag;

         public Test(boolean flag){

                   this.flag = flag;

         }

         public void run(){  //覆写接口中的run方法

                   if(flag){

                            while(true){

                                     synchronized(MyLock.locka){

                                               System.out.println(Thread.currentThread().getName()+"...if locka ");

                                               synchronized(MyLock.lockb){

                                                        System.out.println(Thread.currentThread().getName()+"..if lockb");                                        

                                               }

                                     }

                            }

                   }

                   else{

                            while(true){

                                     synchronized(MyLock.lockb){

                                               System.out.println(Thread.currentThread().getName()+"..else lockb");

                                               synchronized(MyLock.locka){

                                                        System.out.println(Thread.currentThread().getName()+".....else locka");

                                               }

                                     }

                            }

                   }

         }

}

class MyLock{

         static Object locka = new Object();

         static Object lockb = new Object();

}

public class  DeadLockTest{

         public static void main(String[] args) {

                   Thread t1 = new Thread(new Test(true));

                   Thread t2 = new Thread(new Test(false));

                   t1.start();

                   t2.start();

         }

}


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

详细请查看:http://edu.csdn.net/heima



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值