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了,线程同步机制