Java多线程礼让yeild()和插队join()方法案例

 一.   yeild()和join()方法


        1.yeild():线程礼让,让当前正在执行的线程可能暂停,但不阻塞
        2.yeild():当前正在执行的线程可能暂停,也可能不暂停。
        3.yeild():是静态方法,而join()是非静态方法
        4.join():能起到线程插队的作用。

案例一:使用yeild(),线程有时礼让,有时不礼让 。

public class TestDemo07 implements Runnable {
	
    public static void main(String[] args) {
		// TODO Auto-generated method stub
		TestDemo07 t1=new TestDemo07();
		TestDemo07 t2=new TestDemo07();
		new Thread(t1,"线程A").start();
		new Thread(t1,"线程B").start();
		new Thread(t1,"线程C").start();
	}

	@Override
	public void run() {
		// TODO Auto-generated method stub
		System.out.println(Thread.currentThread().getName()+" 线程正在开始执行");
		Thread.yield();
		System.out.println(Thread.currentThread().getName()+" 线程停止执行");
	}
}

运行结果:

 

案例二:join()能起到线程插队的作用

package com.zc;

public class TestDemo08 implements Runnable {
	public static void main(String[] args) {
		//启动我们的线程
		TestDemo08 t1=new TestDemo08();
		Thread thread = new Thread(t1,"线程A");
		thread.start();
		
		//主线程
		for(int i = 0; i < 100; i++) {
			System.out.println("main"+i);
			if(i==30) {
				try{
					//新增的子线程插队
					thread.join();
				} catch(InterruptedException e){
					e.printStackTrace();
				}
				System.out.println("main"+i);
			}
		}
	}

	@Override
	public void run() {
		
		try {
			//线程休息3秒,插队的线程就会整整齐齐的显示出来
			Thread.sleep(3000);
		} catch (InterruptedException e) {
			System.out.println("线程休眠失败");
			e.printStackTrace();
		}
		
		for(int i = 0; i <50; i++) {
			System.out.println("插队的线程正在赶来" +i);
		}
	}
}

       线程安全问题
         .现象:多条线程消费(读取,更改)一些已经被消费过的数据。(消费:更改,更新)。
         .原因:
             1.每一个子线程都有自己的工作空间。
             2.破坏了数据的原子性(不可分割)。(同时成功或者同时失败)
             3.多条线程(并发)操作同一共享数据。
             4.几乎无法预测线程的执行顺序。(每个子线程工作过程不透明)
             5.只存在读数据的时候,不会产生线程安全问题。

二.    synchronized关键字和volatile关键字

          ●synchronized 属于一 种重量级锁,也是一种悲观锁。
         ●synchronized 可以在任意对象及方法上加锁,也可以存在synchronized代码块。而加锁的这段代码称为"互斥区 "或"临界区"

          ●synchronized所修饰的代码范围越少,往往性能就会越好。
         ●synchronized 采用的是monitor对象监视器(底层实现)
         ●synchronized 保证原子性操作,许多源码也有用到它{只要保证原子性,线程就是安全的。}
         ●synchronized 有颗粒性(粒度性),synchronized块能控制互斥区的范围。
         

注意:线程安全越高,往往性能越低。

案例一 :synchronized关键字使用

public class TestDemo05 implements Runnable {
	
	private int ticketNum = 100;

	//加synchronized块的run方法区就是互斥区的范围
	@Override
	public synchronized void run() {
		// 获取当前时间
		long time1 = System.currentTimeMillis();
		// 具体的卖票逻辑
		while (ticketNum > 0) {
			// currentThread():返回对当前正在执行的线程对象的引用。
			System.out.println(Thread.currentThread().getName() + "拿到了第"
					+ ticketNum-- + "票");
		}
		long time2 = System.currentTimeMillis();
		System.out.println("本次程序运行时间为: " + (time2 - time1));
	}

	public static void main(String[] args) {
		//synchronized 有颗粒性,所以只创建一个对象即可。
		TestDemo05 t1 = new TestDemo05();
		//TestDemo05 t2 = new TestDemo05();
		//TestDemo05 t3 = new TestDemo05();
		new Thread(t1, "张三").start();
		new Thread(t1, "李四").start();
		new Thread(t1, "王五").start();
	}

}

运行结果:张三拿到了100张票。李四,王五都没打印。

 三.synchronized锁的是什么东西?(颗粒性/粒度性)

        1. 如果在非静态方法中使用synchronized,锁住的是当前的调用对象。public synchronized                 void    methodA(){}
        2 .如果在静态方法中使用synchronized,锁住的是当前的调用类。public synchronized static                 void methodA(){}
        3 .synchronized(this),锁住的是当前代码块的调用者。(当前方法的调用者)
        4 .synchronized("string"),锁住的是对应的变量。

        5.synchronized存在对象锁和非对象锁,同一对象锁之间存在互斥,其他类似的锁也是同理。
        

public class TestDemo06 extends Thread{
	
	//A方法是在方法上加synchronized
	//打印一句话并且休眠3S  
	public synchronized void methodA(){
		System.out.println("methodA......");
		try {
			Thread.sleep(3000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
	
	//B方法锁的是当前对象的代码块
	//这里的this指的是TestDemo06的实例化对象
	public void methodB(){
		synchronized (this) {
			System.out.println(Thread.currentThread().getName()+"methodB......");
		}
	}
	
	//C方法锁的是一个简单的字符串
	//打印一句话
	public void methodC(){
		String str="hello";
		synchronized (str) {
			System.out.println("methodC......");
		}
	}
	
	public static void main(String[] args){
	
		final TestDemo06 t6=new TestDemo06();
		//final TestDemo06 t7=new TestDemo06();
	
		//第一个线程
		Thread t1=new Thread(new Runnable() {
			
			@Override
			public void run() {
				t6.methodA();
			}
		});
		
		t1.start();
		
		//第二个线程
		Thread t2=new Thread(new Runnable() {
			
			@Override
			public void run() {
				t6.methodB();
			}
		},"线程t2");
		
		t2.start();
		
		//第三个线程
		Thread t3=new Thread(new Runnable() {
			
			@Override
			public void run() {
				t6.methodC();
			}
		});
		
		t3.start();
		
		//第四个线程
		/*Thread t4=new Thread(new Runnable() {
			
			@Override
			public void run() {
				t7.methodB();
			}
		},"线程t4");
		
		t4.start();*/
		
	}
}

运行结果1:

 运行结果2:去掉对象final TestDemo06 t7=new TestDemo06();和第四个线程的注释

 分析图:

 

四.volatile变量使用案例

        ●volatile 变量可以被看作是一种  "轻量的synchronized"
         ●volatile 关键字的主要作用是使变量在多个线程间可见,也就是满足线程安全的                  可见性。
         ●volatile 用来及时刷新线程中的变量值
         ●volatile 不保证原子性操作。子线程A在执行操作时,会先去主线程空间去刷取到最新的值,然后再做自己的操作。

public class TestDemo08 implements Runnable {
	
	private volatile int ticketNum = 100;
	
	@Override
	public  void run() {
		//获取当前时间
		long time1=System.currentTimeMillis();
		
		//具体的卖票逻辑
		while(ticketNum > 0) {
			//currentThread():返回对当前正在执行的线程对象的引用。
			System.out.println(Thread.currentThread().getName()+ "拿到了第" + ticketNum-- +"票");
		}
		
		long time2=System.currentTimeMillis();
		System.out.println("本次程序运行时间为: "+ (time2-time1));
	}
	
	
	public static void main(String[] args) {
		
		TestDemo08 t1 = new TestDemo08();
		//张三李四王五,不会拿到序号相同的票数。
		new Thread(t1,"张三").start();
		new Thread(t1,"李四").start();
		new Thread(t1,"王五").start();	
	}
		
	
}

运行结果:volatile不能完全保证线程安全性。但是对于本次例子是可以保证线程安全的。


版权声明:本文为weixin_52014130原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。