zoukankan      html  css  js  c++  java
  • 多线程

    1.什么是进程

       进程是程序的一次动态执行过程,他经历了代码加载,执行到完毕的一个完整的过程,整个过程也是进程从产生,发展到消亡的过程。

    1.1 什么是线层线程

      是比进程更小的单位,线程是在进程的基础上的进一步划分,所谓多线程是指一个进程在执行过程中可以产生多个线程,这些线程可以同时存在,同时运行,一个进程可能包含了多个同时执行的线程。所有的线程一定要依附于进程才能够存在。

    2.多线程的实现

        2.1 继承Thread类实现多线程

    package 多线程;
    
    public class test extends Thread {
    	private String title;
    
    	public test(String title) {
    		this.title = title;
    	}
    
    	@Override
    	public void run() {
    		// 线程主体方法
    		for (int n = 0; n < 5; n++) {
    			System.out.println(this.title + "运行,x=" + n);
    		}
    	}
    	public static void main(String[] args) {
    		test t = new test("线程1");
    		test t2 = new test("线程2");
    		test t3 = new test("线程3");
    		t.start();//线程的启动必须依靠Thread类的方法Public void start(),表示真正启动多线程,调用此方法后会间接调用run()方法。
    		t2.start();
    		t3.start();
    	}
    }
    

     2.2 为什么线程启动必须调用start()而不是直接调用run()?

       start方法其实是在一个新的操作系统线程上面去调用run方法。换句话说,直接调用run方法而不是调用start方法的话,它并不会开启新的线程,而是在调用run的当前的线程当中执行你的操作

     3.利用Runable接口实现多线程

      利用Thread类的确可以方便的进行多线程的实现,但是这种方法最大的缺点就是单继承的问题,为此java也可以利用Runable接口来实现多线程

    package 多线程;
    
    public class test2 implements Runnable{
    	
        private String title;
        public test2(String title) {
        	this.title=title;
        }
    	@Override
    	public void run() {
    		//线程主体
    		for(int x=0;x<6;x++) {
    			System.out.println(this.title+"运行,x="+x);
    		}
    	}
    	public static void main(String[] args) {
    		test2 t=new test2("线程1");
    	    //如果想要实现多线程要依靠Thread类的start()方法完成,之前继承了Thread类可以将此方法
    		//继承下来使用,但是现在实现的是Runnable接口,没有这个方法可以继承,但是在Thread类中有
    		//个构造方法public Thread(Runnable target),通过这个方法接收Runnable接口对象,并利用Thead类
    		//启动多线程
    		new Thread(t).start();
    		
    	}
    
    }
    

     4.Thread类和Runnable接口实现多线程的区别

         多线程的两种实现方式都需要一个线程的主类,而这个类可以实现Runnable接口或者继承Thread类,不管何种方式都必须在子类中复写run()方法,这是线程的主方法.

          Thread类是Runnable接口的子类,使用Runable接口可以避免单继承局限,以更加方便地实现数据共享的概念。

    5.线程的操作状态

         任何一个线程一般具有5种状态,即创建,就绪,运行,阻塞和终止

    5.1 创建

        在程序中用构造方法创建了一个线程对象后,新的线程对象便处于新建状态,此时它已经有了相应的内存空间和其他资源,但还处于不可运行的状态。

    5.2 就绪状态

        新建线程对象后,调用该线程的start()方法就可以启动线程。当线程启动时,即进入就绪状态。此时线程将进入线程队列排队,等待CPU服务,这表明它已经具备了运行条件。

    5.3 运行状态

       当就绪状态的线程被调用并获得处理器资源时,线程就进入了运行状态。此时调用该线程对象的run()方法。run()方法定义了该线程的操作和功能。

    5.4 堵塞状态

        一个正在运行的线程在某些特殊的情况下,如被人为挂起或需要执行耗时的输入输出操作时,将让出CPU并暂时终止自己的执行,进入堵塞状态。在可执行状态下,如果调用sleep(),suspend(),wait()等方法,线程都将进入堵塞状态。堵塞时,线程不能进入排队队列,只有当引起堵塞的原因消除后,线程才可以转入就绪状态。

    5.5终止状态

      线程调用stop()方法或run()方法执行结束后,线程即处于终止状态。终止的线程不具有继续运行的能力

    6.线程的命名和取得

    1.public Thread(Runnable target,String name)//实例化线程对象,接收Runnable接口子类对象,同时设置线程名称
    2.public final void setName(String name)//设置线程名称
    3.public final String getName()//取得线程名称
    

    由于线程的状态不确定,所以线程的名字就成为了唯一的分辨标记,则在定义线程名称时一定要在线程启动前设置,并尽量不要重名,而且每次能够取得的线程对象,指的是当前正在执行的run()方法的线程

    4.public static Thread currentThread()//取得线程名称
    

     线程的命名和取得

    package 多线程;
    
    public class test2 implements Runnable {// 线程主体类
    
    	private String title;
    
    	@Override
    	public void run() {// 线程住方法
    		System.out.println(Thread.currentThread().getName());// 当前线程的名称
    	}
    
    	public static void main(String[] args) {
    		test2 t = new test2();// 定义Runnable接口对象
    		new Thread(t, "线程A").start();// 线程A,多线程的实现需要依赖start()方法
    		new Thread(t, "线程B").start();// 线程B
    		new Thread(t, "线程c").start();// 线程C
    		new Thread(t).start();// Thread-0,如果不设置名字的话,会自动分配一个名称,以Thread-x的形式出现
    		new Thread(t).start();// Thread-1
    		new Thread(t).start();// Thread-2
    		new Thread(t).start();// Thread-3
    	}
    }
    

     7.线程的休眠

    线程的休眠是让程序执行的速度慢一些,在Thraed类中提供了以下的操作方法。

    public static void sleep(long millis) throws InterruptedException//设置的休眠单位是毫秒
    

     线程的休眠

    package 多线程;
    
    public class test2 implements Runnable {// 线程主体类
    
    	private String title;
    
    	@Override
    	public void run() {// 线程住方法
    		for(int x=0;x<100;x++) {//每个x代表一个线程
    			try {
    				Thread.sleep(100);//每次休眠100ms
    			}catch(InterruptedException e) {
    				e.printStackTrace();
    			}
    			System.out.println(Thread.currentThread().getName());// 当前线程的名称
    		}
    		
    	}
    	public static void main(String[] args) {
    		test2 t = new test2();// 定义Runnable接口对象
    		new Thread(t, "线程A").start();// 线程A
    		new Thread(t, "线程B").start();// 线程B
    		new Thread(t, "线程c").start();// 线程C
    		new Thread(t).start();// Thread-0
    		new Thread(t).start();// Thread-1
    		new Thread(t).start();// Thread-2
    		new Thread(t).start();// Thread-3
    	}
    }
    

     以A线程为例

    new Thread(t, "线程A").start();// 线程A
    

     调用线程对象的start()方法后,线程处于就绪状态,就绪好后执行run()方法

    public void run() {// 线程住方法
       for(int x=0;x<100;x++) {//每个x代表一个线程
    	try {
    	  Thread.sleep(100);//每次休眠100ms
    	    }catch(InterruptedException e) {
    		e.printStackTrace();
    	}
    	System.out.println(Thread.currentThread().getName());// 当前线程的名称
    }
    

     此时进入for循环打印出当前线程的名称线程A,并输出100个,但是是多线程状态,也会执行其它的线程对象的run()方法,并打印不同的线程名称。

    8.线程的优先级

    在java的线程操作中,所有的线程在运行前都会保持在就绪的状态,此时哪个线程的优先级高,就有可能会先执行

    如果想要设置优先级,Thread类中有以下方法

    1.public static final int MAX_PRIORITY//常量,最高优先级,数值10
    2.public static final int NORM_PRIORITY//常量,中等优先级,数值5
    3.public static final int MIN_PRIORITY//常量,最低优先级,数值1
    4.public static void setPriority(int newPriority)//设置程序优先级
    5.public final int getPriority();//取得线程优先级
    

     优先级举例

    package 多线程;
    
    public class test2 implements Runnable {// 线程主体类
    
    	private String title;
    
    	@Override
    	public void run() {// 线程住方法
    			try {
    				Thread.sleep(100);//每次休眠100ms
    			}catch(InterruptedException e) {
    				e.printStackTrace();
    			}
    			System.out.println(Thread.currentThread().getName());// 当前线程的名称
    			System.out.println(Thread.currentThread().getPriority());//获得当前线程的优先级
    		}
    		
    	public static void main(String[] args) {
    		test2 t = new test2();// 定义Runnable接口对象
    		Thread t1=new Thread(t, "线程A");// 线程A
    		Thread t2=new Thread(t, "线程B");// 线程B
    		Thread t3=new Thread(t, "线程c");// 线程C
    		t1.setPriority(Thread.MAX_PRIORITY);//有可能先执行,并不是一定执行
    		t2.setPriority(Thread.NORM_PRIORITY);
    		t3.setPriority(Thread.MIN_PRIORITY);	
    	    t3.start();
    	    t1.start();
    	    t2.start();
    
    	}
    
    }
    

     9.线程的同步与死锁

    9.1同步

    同步问题指的是多个线程操作同一资源时所带来的信息的安全性问题

    package 多线程;
    
    public class test2 implements Runnable {// 线程主体类
    
    	private int ticket=6;//票数
    
    	@Override
    	public void run() {// 线程主方法
    		for(int n=0;n<10;n++) {
    			 if(this.ticket>0) {
    					try {
    						Thread.sleep(100);//每次休眠100ms
    					}catch(InterruptedException e) {
    						e.printStackTrace();
    					}
    					System.out.println(Thread.currentThread().getName()+"卖票,票还有="+this.ticket--);//卖票
    				}
    		}   
    	}
    	public static void main(String[] args) {
    		test2 t = new test2();// 定义Runnable接口对象
    	    new Thread(t, "黄牛A").start();// 线程A
    	    new Thread(t, "黄牛B").start();// 线程B
    	    new Thread(t, "黄牛C").start();// 线程C
    
    	}
    
    }
    

     运行结果如下

    可以看出出现了不同步现象,黄牛都认为自己是第一个买票的人

    这个时候需要使用同步,所谓的同步就是指多个操作在同一时间内只有一个线程能执行,其他线程要等待此线程完成后才可以继续执行。

    方法有两种,一个是同步代码块,一个是同步方法同步代码块,

    同步代码块

    在进行同步的时候必须设置一个要同步的对象,而这个对象应该理解为当前对象this

    synchronized(同步对象){//需要同步的代码}
    

     举例:

    package 多线程;
    
    public class test2 implements Runnable {// 线程主体类
    
    	private int ticket=6;//票数
    
    	@Override
    	public void run() {// 线程主方法
    		for(int n=0;n<9999;n++) {
    			 synchronized(this) {
    				 if(this.ticket>0) {
    						try {
    							Thread.sleep(100);//每次休眠100ms
    						}catch(InterruptedException e) {
    							e.printStackTrace();
    						}
    					System.out.println(Thread.currentThread().getName()+"卖票,票还有="+this.ticket--);//卖票
    					}else {
    						break;
    					}
    			 }
    		}   
    	}
    	public static void main(String[] args) {
    		test2 t = new test2();// 定义Runnable接口对象
    	    new Thread(t, "黄牛A").start();// 线程A
    		new Thread(t, "黄牛B").start();// 线程B
    	    new Thread(t, "黄牛C").start();// 线程C
    
    	}
    
    }
    

     运行结果为

    同步方法

    package 多线程;
    
    class test23 implements Runnable {// 线程主体类
    
    	private int ticket = 6;// 票数
    
    	@Override
    	public void run() {// 线程主方法
    		for (int n = 0; n < 9999; n++) {
    			this.sale();
    		}
    	}
    
    	public synchronized void sale() {
    		if (this.ticket > 0) {
    			try {
    				Thread.sleep(100);// 每次休眠100ms
    			} catch (InterruptedException e) {
    				e.printStackTrace();
    			}
    			System.out.println(Thread.currentThread().getName() + "卖票,票还有=" + this.ticket--);// 卖票
    		}
    	}
    
    }
    
    public class test2 {
    	public static void main(String[] args) {
    		test23 t = new test23();// 定义Runnable接口对象
    		new Thread(t, "黄牛A").start();// 线程A
    		new Thread(t, "黄牛B").start();// 线程B
    		new Thread(t, "黄牛C").start();// 线程C
    
    	}
    }
    

     9.2 死锁

       所谓的死锁就是指两个线程都在等待彼此先完成,造成了程序的停滞状态

       多个线程访问同一个资源时一定要考虑到同步的问题,过多的同步会导致死锁。

    10.线程间的经典案例

       10.1生产者与消费者问题

        生产者生产手机,消费者购买手机

        手机类

    package 多线程;
    
    public class Telephone {
    
    	private String phone_name;//手机的名称
    	private String phone_content;//手机的参数
    	public String getPhone_name() {
    		return phone_name;
    	}
    	public void setPhone_name(String phone_name) {
    		this.phone_name = phone_name;
    	}
    	public String getPhone_content() {
    		return phone_content;
    	}
    	public void setPhone_content(String phone_content) {
    		this.phone_content = phone_content;
    	}
    	
    	
    }
    

     生产者类

    package 多线程;
    
    public class Producer implements Runnable {//定义生产者
        private Telephone telephone=null;
        public Producer (Telephone telephone) {
        	this.telephone=telephone;
        }
    	@Override
    	public void run() {
    		for(int x=0;x<50;x++) {//生产50部手机,一半诺基亚,一半小米
    			if(x%2==0) {
    				this.telephone.setPhone_name("NOKIA");
    				try {
    					Thread.sleep(100);
    				}catch(InterruptedException e) {
    					e.printStackTrace();
    				}
    				this.telephone.setPhone_content("X7");
    			}else {
    				this.telephone.setPhone_name("XIAOMI");
    				try {
    					Thread.sleep(100);
    				}catch(InterruptedException e) {
    					e.printStackTrace();
    				}
    				this.telephone.setPhone_content("9");
    			}
    		}
    		
    	}
    
    }
    

     消费者类

    package 多线程;
    
    public class Consumer implements Runnable{
        private Telephone telephone;
        public Consumer(Telephone telephone) {
        	this.telephone=telephone;
        }
    	@Override
    	public void run() {
    		for(int x=0;x<50;x++) {
    			try {
    				Thread.sleep(100);
    			}catch(InterruptedException e) {
    				e.printStackTrace();
    			}
    			System.out.println(this.telephone.getPhone_name()+"-->"+this.telephone.getPhone_content());
    		}
    		
    	}
    
    }
    

     商场类

    package 多线程;
    
    public class Market {
         public static void main(String[] args) {
        	 Telephone tel=new Telephone();
        	 new Thread(new Producer(tel)).start();
        	 new Thread(new Consumer(tel)).start();
         }
    }
    

     运行结果

    出现了消息不对应的结果

    为了解决这个问题还是需要用到同步对手机类的setter和getter方法进行同步处理

    对程序进行修改

    手机类

    package 多线程;
    
    public class Telephone {
    
    	private String phone_name;//手机的名称
    	private String phone_content;//手机的参数
    	private boolean flag=true;
    	
    	public  synchronized void set(String phone_name,String phone_content) {//重新设计set方法
    		if(this.flag==false) {//不能生产
    			try {
    				super.wait();//线程等待,使用的是Object的方法
    			}catch(InterruptedException e) {
    				e.printStackTrace();
    			}
    			
    		}
    		this.phone_name=phone_name;
    		//存在延时操作
    		try {
    			Thread.sleep(100);
    		}catch(InterruptedException e) {
    			e.printStackTrace();
    		}
    		
    		this.phone_content=phone_content;
    		this.flag=false;//表示已生产
    		if(flag==false) {
    			super.notify();//线程唤醒
    		}
    		
    	}
    	
    	public synchronized void get() {
    		if(this.flag==true) {
    			try {
    				super.wait();
    			}catch(InterruptedException e) {
    				e.printStackTrace();
    			}
    		}
    		//存在延时操作
    		try {
    			Thread.sleep(100);
    		}catch(InterruptedException e) {
    			e.printStackTrace();
    		}
    		System.out.println(this.phone_name+"-->"+this.phone_content);
    		this.flag=true;
    		super.notify();
    	}
    	
    	public String getPhone_name() {
    		return phone_name;
    	}
    	public void setPhone_name(String phone_name) {
    		this.phone_name = phone_name;
    	}
    	public String getPhone_content() {
    		return phone_content;
    	}
    	public void setPhone_content(String phone_content) {
    		this.phone_content = phone_content;
    	}
    	
    	
    }
    

     生产者

    package 多线程;
    
    public class Producer implements Runnable {//定义生产者
        private Telephone telephone=null;
        public Producer (Telephone telephone) {
        	this.telephone=telephone;
        }
    	@Override
    	public void run() {
    	
    			for(int x=0;x<50;x++) 
    				//生产50部手机,一半诺基亚,一半小米
    					if(x%2==0) {
    						this.telephone.set("Nokia", "X7");
    					}else {
    						this.telephone.set("XIAOMI","9");
    						
    					}
    				}
    		}
    		
    

     消费者

    package 多线程;
    
    public class Consumer implements Runnable{
        private Telephone telephone;
        public Consumer(Telephone telephone) {
        	this.telephone=telephone;
        }
    	@Override
    	public void run() {
    		for(int x=0;x<50;x++) {
    			this.telephone.get();
    			
    		}
    		
    	}
    
    }
    

    sleep()和wait()的区别

    sleep()是Thread类定义的static方法,表示线程休眠,将执行机会给其他线程,但是监控状态会依然保持,到时候会自动恢复

    wait()是Object类定义的方法,表示线程等待,一旦执行了nofify()或notyfAAll()后才结束等待

    11.线程的生命周期

    new Thread()-->新的线程

    start()-->进入就绪状态

    suspend()-->暂时挂起线程

    sleep()-->线程休眠,停止线程

    一般的,在停止线程上会使用标识法,在run()里面添加标记

       

      

      

       

    ---恢复内容结束---

  • 相关阅读:
    static 关键字
    gitlab 配置到jenkins
    Eclipse下代码字体背景变红/变绿/变黄原因
    构造方法(和python初始化变量类似)
    面向对象(实际就像python跳用自己写的库那样)
    Python 的AES加密与解密
    break 和 continue 的用法
    for循环
    Eclipse快捷键
    java三元运算符
  • 原文地址:https://www.cnblogs.com/cainame/p/10414528.html
Copyright © 2011-2022 走看看