一. 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 stubTestDemo07 t1=new TestDemo07();TestDemo07 t2=new TestDemo07();new Thread(t1,"线程A").start();new Thread(t1,"线程B").start();new Thread(t1,"线程C").start();}@Overridepublic void run() {// TODO Auto-generated method stubSystem.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);}}}@Overridepublic 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方法区就是互斥区的范围@Overridepublic 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() {@Overridepublic void run() {t6.methodA();}});t1.start();//第二个线程Thread t2=new Thread(new Runnable() {@Overridepublic void run() {t6.methodB();}},"线程t2");t2.start();//第三个线程Thread t3=new Thread(new Runnable() {@Overridepublic void run() {t6.methodC();}});t3.start();//第四个线程/*Thread t4=new Thread(new Runnable() {@Overridepublic 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;@Overridepublic 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不能完全保证线程安全性。但是对于本次例子是可以保证线程安全的。