1、关于线程sleep方法
static void sleep(long millis)
静态方法,参数毫秒,作用:当前线程进入休眠(进入阻塞状态)放弃占用的CPU时间片,让给其它线程使用
public static void main(String[] args) { //使主线程睡眠5s /* try { Thread.sleep(1000*5); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName());//5s后输出 main */ //每间隔一秒输出 for (int i = 0; i < 10; i++) { System.out.println(Thread.currentThread().getName()+"-->"+i); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } }
2、终止线程睡眠
实现在主线程休眠五秒后叫醒分支线程,使用interrupt(),原理是使分支线程的睡眠出现异常,捕捉异常后,执行run中后续程序。
//MyThread01分支线程类
class MyThread01 implements Runnable{ /* 重点:run()方法不可以抛出异常,只能try...catch, 原因是因为Runnable中run方法没有抛出异常,重写的方法不能比接口中的方法抛出的异常多 */ @Override public void run() { System.out.println(Thread.currentThread().getName()+"开始");//Thread-0开始 try { Thread.sleep(1000*60*60*24);//休眠一天 } catch (InterruptedException e) { System.out.println(e.getMessage());//sleep interrupted } System.out.println(Thread.currentThread().getName()+"结束");//Thread-0结束 } }
//主线程
public static void main(String[] args) { //封装线程 Thread thread=new Thread(new MyThread01()); //启动线程 thread.start(); //主线程工作5s后终止分支线程的睡眠 try { Thread.sleep(1000*5); } catch (InterruptedException e) { e.printStackTrace(); } //终止run的睡眠 thread.interrupt();//原理是使分支线程的睡眠出现异常,捕捉异常后,执行run中后续程序 }
3、使用shop()终止线程
已过时,原因暴力终止线程会造成数据丢失
把2中的代码thread.interrupt()换成下面这行代码
thread.stop();//这种暴力终止线程,会使数据可能丢失
4、线程安全问题
1>、什么时候数据在多线程并发的环境下会存在安全问题?
满足三个条件:1、多线程并发,2、有共享数据,3、共享数据有修改的行为
2>、怎样解决线程安全问题?
当多线程并发的情况下,有共享数据,并且会被修改,此时就会存在线程安全问题,解决方法:线程排队执行(不能并发)称为线程同步机制, 线程排队则会牺牲效率,但是要以安全为主
3>、同步编程模型与异步编程模型
异步:(多线程并发)各自执行各自的
同步:多线程排队,两线程之间发生等待关系
5、关键字synchronized
//银行账户类
public class Account { private String act; private double blance; Object obj=new Object(); public Account(String act, double blance) { this.act = act; this.blance = blance; } public String getAct() { return act; } public void setAct(String act) { this.act = act; } public double getBlance() { return blance; } public void setBlance(double blance) { this.blance = blance; } public void WithDrowMoney(double money){ /* 关键字synchronized线程安全的,小括号里是线程的共享对象,在此程序中,共享对象是账户对象故用this,或者obj,因为obj每个线程都有且只有一个。 当一个线程遇到这个关键字,则会在这里(锁池)寻找对象锁,这时会释放之前占有的CPU时间片,如果没找到就要等待里面的线程执行完毕,找到之后 就会锁门,然后进入就绪状态继续抢夺CPU时间片,其他想要进来的线程(与此线程共享对象的,不共享对象的不需要排队)需要等待。 synchronized的代码块越少效率越高 */ synchronized(this){ //取之前的余额 double beforeBlance=this.blance; //取之后的余额 double afterBlance = beforeBlance-money; //修改余额 this.setBlance(afterBlance); } } }
当synchronized 在方法上:
public synchronized void WithDrowMoney(double money){}
缺点:
则对象锁只能是this,不灵活,整个方法体都需要同步,扩大了同步范围,导致效率低
优点:代码节俭了,如果共享的对象就是this且整个代码开都要同步则可以使用这种方式
//线程
public class ThreadTest extends Thread{ private Account act; //利用构造方法来使多个线程共享一个对象 public ThreadTest(Account act){ this.act=act; } @Override public void run() { //取钱,假设取5000 double money=5000; //取款 act.WithDrowMoney(money); System.out.println(Thread.currentThread().getName()+"账户:"+act.getAct()+"取走金额:"+money+",余额:"+act.getBlance()); } }
//测试类
public static void main(String[] args) { //创建账户对象 Account act=new Account("act-001",10000); //两个线程共享一个账户对象 Thread t1=new ThreadTest(act); Thread t2=new ThreadTest(act); //修改名字 t1.setName("t1"); t2.setName("t2"); //线程启动 t1.start(); t2.start(); }
6、总结synchronized
三种写法:
第一种:同步代码块
synchronized(线程共享对象){同步代码块;}
第二种:在实例方法上使用,表示共享对象一定是this,并且同步代码块是一整个方法体
第三种:在静态方法上使用synchronized,表示类锁,类锁永远只有一把。
7、写死锁
代码:
//线程MyThread05
class MyThread05 extends Thread{ Object o1; Object o2; public MyThread05(Object o1, Object o2) { this.o1 = o1; this.o2 = o2; } public void run(){ //先锁o1在锁o2 synchronized (o1){ try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (o2){ } } } }
//线程Mythread06
class Mythread06 extends Thread{ Object o1; Object o2; public Mythread06(Object o1, Object o2) { this.o1 = o1; this.o2 = o2; } public void run(){ //先锁o2在锁o1 synchronized (o2){ try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (o1){ } } } }
//主线程
public static void main(String[] args) { Object o1=new Object(); Object o2=new Object(); //两个线程共享o1,o2 Thread t1=new MyThread05(o1,o2); Thread t2=new Mythread06(o1,o2); t1.start(); t2.start(); }
8、在解决线程安全问题的时候
尽量不使用synchronized,它会使程序执行效率降低,用户体验不好
第一:尽量使用局部变量来替代实例变量和静态变量
第二:如果必须使用实例变量,那么可以考虑创建多个对象,这样内存就不会共享了
第三:如果不能使用局部变量,对象也不能创建多个,那么就可以选择synchronized了,线程同步机制