zoukankan      html  css  js  c++  java
  • java多线程及线程安全详解

    为什么要使用多线程:

    单线程只能干一件事  而多线程可以同时干好多事(将任务放到线程里执行  效率高)

    而所谓同时干并不是真正意义上的同时   只是(这里就叫CPU)cpu在每个线程中随机切换来执行 线程中要干的活

    多线程编写:

    1)第一种:(线程类)

    class Stu1 extends Thread{

      //重写 run方法 

    }

    调用:Stu1 su = new Stu1();

    su.start()//内部会自动调用run方法    把run方法放到线程上调用

    2)第二种:普通任务类(由于第一种类只能是单继承 就没法实现继承其他父类并且继承线程类 所以第一种方法扩展性比较差)

    直接创建线程对象    线程内干的事  放到一个自定义对象里面实现   (用到修饰设计模式)

    底层代码

    class Thread{

    private Runnable r

    public void Thread(Runnable r){ //利用有参构造将自定义对象传进来

    this.r = r;

    }

    public void start(){

      r.run();

    }

    }

    class Stu implements Runnable{}  通过实现接口   线程类底层start调用的run方法 实际就是我们自定义类中的run方法

    Thread th = new  Thread(new Stu())  //直接创建线程对象

    th.start();

    我们用第二种方法创建多线程如下:

    public static void main(String[] args) {
    //		不同线程干同一件事
    		SaleWindow sw = new SaleWindow();
    		Thread t1 = new Thread(sw);
    		Thread t2 = new Thread(sw);
    		t1.setName("窗口A");
    		t2.setName("窗口B");
    		t1.start();
    		t2.start();
    	}
    
    public class SaleWindow implements Runnable {
    	private int ticketCount = 10;
    
    	@Override
    	public void run() {
    		// TODO Auto-generated method stub
    //		多个窗口卖票
    		for(int i = 0;i<10;i++){
    			if(ticketCount>0){
    				//字符串拼接信息   变量+""  就可以拼接成字符串
    				System.out.println(Thread.currentThread().getName()+"卖出"+ticketCount+"张票");
    				ticketCount--;
    				try {
    					Thread.sleep(500);
    				} catch (InterruptedException e) {
    					// TODO Auto-generated catch block
    //					e.printStackTrace();
    				}
    			}
    		}
    	}
    	
    }
    

     最后结果图:

    从运行结果来看:同一张票卖给了二个人  这是在现实生活中不允许的 这样就会产生线程安全问题。

    而产生线程安全问题的有三个要素必须同时满足才会产生线程安全:

    1、必须有共享资源
    2、必须是多线程环境
    3、每个线程都适用了共享资源

    而上面的例子:票是共享资源、又是多线程环境、线程执行任务的时候又使用了共享资源 所以会产生线程安全

    怎么解决线程安全?

    解决线程安全其核心:就是将某一个线程中的任务给锁(同步锁)起来,这个时候JVM就不得不等到本任务里的代码执行完以后在去执行另外一个线程里的任务。

    (ps:在没加同步锁时jvm可以随时在线程之间随机切换,很有可能本线程的任务没有执行完就切换到别的线程上去了)

    二种方法:

    1、同步代码块:

    public class SaleWindow implements Runnable {
    	private int ticketCount = 10;
    
    	@Override
    	public void run() {
    		// TODO Auto-generated method stub
    		// 多个窗口卖票
    		for (int i = 0; i < 10; i++) {//可以随意设置锁
    			synchronized (this) {
    				if (ticketCount > 0) {
    					// 字符串拼接信息 变量+"" 就可以拼接成字符串
    					System.out.println(Thread.currentThread().getName() + "卖出"
    							+ ticketCount + "张票");
    					ticketCount--;
    					try {
    						Thread.sleep(500);//每隔500毫秒 线程休眠 随后自己自动会唤醒(目的是为了调节线程速度)
    					} catch (InterruptedException e) {
    						// TODO Auto-generated catch block
    						// e.printStackTrace();
    					}
    				}
    			}
    		}
    	}
    
    }
    

     2、同步方法:

    public class SaleWindow implements Runnable {
    	private int ticketCount = 10;
         //默认固定的锁对象this
    //将产生线程安全的代码封装到方法里并设置成同步方法 public synchronized void syncB() { if (ticketCount > 0) { // 字符串拼接信息 变量+"" 就可以拼接成字符串 System.out.println(Thread.currentThread().getName() + "卖出" + ticketCount + "张票"); ticketCount--; try { Thread.sleep(500); } catch (InterruptedException e) { // TODO Auto-generated catch block // e.printStackTrace(); } } } @Override public void run() { // TODO Auto-generated method stub // 多个窗口卖票 for (int i = 0; i < 10; i++) { syncB(); } } }

     结果图:

    最后结果每个窗口不能卖同样的票解决了线程安全问题

    线程之间的通信

    (ps:线程之间的通信和线程安全无关联二者不是一回事)

    怎样才能让二个线程之间进行通信?

    线程之间是相互独立的互不联系 而真正意义上的通信是通过中间件(同步锁 必须是同一把锁)来达到线程之间通信的目的

    案例:二个线程来回交替输出一条数据(意思就是必须按照我说一句你说一句的这个规则走)

    class BingB extends Thread {
    	public void run(){
    		for(int i = 0;i<5;i++){
    			System.out.println("冰冰美,如花丑");
    		}
    	}
    }
    
    class RuH extends Thread{
    	public void run(){
    		for(int i = 0;i<5;i++){
    			System.out.println("如花美,冰冰丑");
    		}
    	}
    }
    
    public class Test {
    	public static void main(String[] args) {
    		BingB bb = new BingB();
    		RuH rh = new RuH();
    		bb.start();
    		rh.start();
    	}
    }
    

     结果图:此结果不是交替出现的

    要想达到交替的目的代码如下:

    class MyLock{
    	static Object o = new Object();
    }
    
    class BingB extends Thread {
    	public void run(){
    		for(int i = 0;i<5;i++){
    			//加锁(意思是必须等到本线程的任务代码执行完以后才去执行别的线程的代码)
    			synchronized (MyLock.o) {
    				System.out.println("冰冰美,如花丑");
    				MyLock.o.notify();//唤醒另一个线程(这里表示如花线程)
    				try {
    					MyLock.o.wait();//暂时彻底休眠本线程(不会自动唤醒 需要手动唤醒)   同时解锁  阻塞线程 代码就不会往下继续走  jvm会切换到另外一个线程中去
    				} catch (InterruptedException e) {
    					// TODO Auto-generated catch block
    				}
    			}
    		}
    	}
    }
    
    class RuH extends Thread{
    	public void run(){
    		for(int i = 0;i<5;i++){
    			synchronized (MyLock.o) {
    				System.out.println("如花美,冰冰丑");
    				MyLock.o.notify();//唤醒另一个线程(这里表示如冰冰程)
    				try {
    					MyLock.o.wait();//暂时彻底休眠本线程   同时解锁  阻塞线程
    				} catch (InterruptedException e) {
    					// TODO Auto-generated catch block
    					e.printStackTrace();
    				}
    			}
    		}
    	}
    }
    
    public class Test {
    	public static void main(String[] args) {
    		BingB bb = new BingB();
    		RuH rh = new RuH();
    		bb.start();
    		rh.start();
    	}
    }
    

     

    从以上例子可以看出产生线程安全并解决线程安全 到实现线程间的通信 都用到了 同步锁  所以 同步锁既可以解决线程安全问题  又可以解决线程之间的通信

    多线程状态流程图:

    写到这里有关线程的基本讲解完毕,如果内容有我自己理解错误的地方还请各位大神指教,小弟不吝求教!

  • 相关阅读:
    模式识别 第一章 概论
    高等代数9 欧几里得空间
    离散数学6 初等数论
    高等代数6 线性空间
    高等代数5 二次型
    GMOJ 6870. 【2020.11.17提高组模拟】ckw的树 (tree)
    1
    Virtual Tree 学习笔记
    [2020.11.14提高组模拟] 鬼渊传说(village)
    企业购置新车,各项费用会计入账以及案例分析
  • 原文地址:https://www.cnblogs.com/nianzhilian/p/9034018.html
Copyright © 2011-2022 走看看