Thread的常用方法
getName()方法
public class Thread{
public static void main(String[] args){
Thread t1=new MyThread();
t1.start(); //启动线程
System.out.println(t1.getName());
for(int i=0;i<-5;i++){
System.out.println("main"+i);
}
}
思考:
怎么得到主线程的名字?
调用currentThread();
public class Thread{
public static void main(String[] args){
Thread t1=new MyThread();
t1.start(); //启动线程
System.out.println(t1.getName());
//哪个线程运行它,它就会得到哪个线程对象
Thread m=Thread.currentThread();
System.out.println(m.getName());
}
}
思考:
若有两个子线程怎么区分哪个线程在执行?
public class MyThread extends Thread{
@Override
public void run(){
Thread t=Thread.currentThread()'
for(int i=1;i<=5;i++){
System.out.println(t.getName()+i);
}
}
}
取名字setName();
注:在启动之前
public class Thread{
public static void main(String[] args){
Thread t1=new MyThread();
t1.setName();
t1.start(); //启动线程
System.out.println(t1.getName());
for(int i=0;i<-5;i++){
System.out.println("main"+i);
}
}
取名字有参构造器public Thread(String name)
public class Thread{
public static void main(String[] args){
Thread t1=new MyThread("1号线程");
t1.start(); //启动线程
System.out.println(t1.getName());
//哪个线程运行它,它就会得到哪个线程对象
Thread m=Thread.currentThread();
System.out.println(m.getName());
}
}
public class MyThread extends Thread{
public MyThread(String name){
super(name);
}
@Override
public void run(){
Thread t=Thread.currentThread()'
for(int i=1;i<=5;i++){
System.out.println(t.getName()+i);
}
}
}
说明:
需要调用super方法
sleep方法(暂停)
需求:在i==3时暂停5秒
public class Thread2{
public static void main(String[] args){
for(int i=1;i<=5;i++){
System.out.println(i);
if(i==3){
//会让当前线程暂停5秒
Thread.sleep(5000);
}
}
}
}
join方法(让当前调用的这个方法的线程先执行完)
面试题:线程顺序执行
public class Thread2{
public static void main(String[] args){
for(int i=1;i<=5;i++){
System.out.println(i);
if(i==3){
//会让当前线程暂停5秒
Thread.sleep(5000);
}
}
Thread t1=new MyThread("1号线程")
t1.start();
t1.join();
Thread t2=new MyThread("2号线程")
t2.start();
t2.join();
}
}
线程安全问题
定义:
多个线程,同时操作同一个共享资源时,存在修改共享资源,可能会出现业务安全问题。
用程序模拟场景(练习)
需求:
两个人有一个共同账户,余额10万元,模拟两个人同时去取钱10万元。
分析:
1.账户类
2.线程类
3.创建两个线程
4.启动两个线程
Step 1:
public class Account{
private double money;
public Account(double money){
this.money=money;
}
public Account(){
}
public double gerMoney(){
return money;
}
public double setMoney(){
this.money=money;
}
public void drawMoney(double money){
String name=Thread.currentThread().getName();
//判断余额是否足够
if(this.money>=money){
System.out.println(name+"来取钱"+money+"成功");
this.money-=money;
System.out.println(name+"取钱后余额"+this.money);
}else{
System.out.println(name+"来取钱,余额不足");
}
}
}
说明:
需要额外创建取钱的方法drawMoney
思考:
怎么知道是哪个线程取到了钱?
运用currentThread方法和有参构造以及getName(),具体方法说明见上文。
Step 2:
public class Thread{
public static void maini(String[] args){
Account acc=new Account(10);
//创建两个线程
new DrawThread(acc,"1号").start();
new DrawThread(acc,"2号").start();
}
}
说明:要将账户对象交给线程,需要用到有参构造器
Step 3:
public class DrawThread extends Thread{
private Account acc;
public DrawThread(Acount acc,String name){
this.acc=acc;
super(name);
}
@Override
public void run(){
acc.drawMoney(10);
}
}
说明:创建成员方法,使run可以调用acc账户
输出结果:出现了线程安全问题
线程同步(解决先线程安全问题)
使线程先后依次访问共享资源
加锁:
每次只允许一个线程加锁,加锁后才能进入访问,访问完毕后解锁,然后其他线程才能再加锁进去
加锁方式:
方式一
:同步代码块
作用:把访问的我核心代码上锁,保证线程安全
synchronized(同步锁){
访问的共享资源
}
注:同一个锁,同一个对象
public void drawMoney(double money){
String name=Thread.currentThread().getName();
synchronized("字符"){
if(this.money>=money){
System.out.println(name+"来取钱"+money+"成功");
this.money-=money;
System.out.println(name+"取钱后余额"+this.money);
}else{
System.out.println(name+"来取钱,余额不足");
}
}
}
说明:
锁为字符串,有且仅有一个
思考:
若有多个账号,怎么正确加锁?
public void drawMoney(double money){
String name=Thread.currentThread().
getName();
//用this代表共享资源
synchronized(this){
if(this.money>=money){ System.out.println(name+"来取钱"+money+"成功"); this.money-=money;
System.out.println(name+"取钱后余额"+this.money);
}else{ System.out.println(name+"来取钱,余额不足");}
}
}
静态方法加锁:
静态方法是通过类名访问的,建议使用类名.class作为锁,是一个字节码文件
public static void test(){
synchronized(Account.class){
......
}
}
总结:
锁对象随便选一个唯一的对象会影响其他无关线程的执行。
使用规范:
1.建议使用共享资源作为锁对象,对于实例方法建议使用this作业锁对象。
2.对于静态方法建议使用字节码(类名.class)对象作为锁对象。
方法二:
同步方法
作用:把访问共享资源的核心方法上锁,以保证线程安全。
修饰符 synchronized 返回值类型 方法名称(形参列表){
操作共享资源的代码
}
用同步方法解决取钱问题
public synchronized void drawMoney(double money){
String name=Thread.currentThread().getName(); //用this代表共享资源
if(this.money>=money){ System.out.println(name+"来取钱"+money+"成功");
this.money-=money;
System.out.println(name+"取钱后余额"+this.money); }else{
System.out.println(name+"来取钱,余额不足");}
}
}
说明:
底层有隐式锁对象
对比:
范围:同步代码块锁的范围小(性能好一点),同步方法锁的范围大。
可读性:同步方法更好。
方式三:
Lock锁
定义:
1.Lock锁是JDK5开始提供的一个新的锁定操作,通过它可以创建出锁对象进行加锁和解锁,更灵活方便。
2.Lock是一个接口,不能直接实例化,可以采用实现类ReentrantLock来构建锁对象。
public class Account{
private double money;
//创建一个锁对象
//加final保护锁对象
private final Lock lk=new ReentrantLock;
public Account(double money){
this.money=money;
}
public Account(){
}
public double gerMoney(){
return money;}
public double setMoney(){
this.money=money;
}
public void drawMoney(double money){
String name=Thread.currentThread().getName();
lk.lock();//加锁
//判断余额是否足够
try{if(this.money>=money){ System.out.println(name+"来取钱"+money+"成功");
this.money-=money;
System.out.println(name+"取钱后余额"+this.money);
}else{ System.out.println(name+"来取钱,余额不足");
}
}catch(Exception e){
e.printStackTrace();
}finally{
lk.unlock();
}
}
}
说明:为了保证解锁,要放到finally里,异常处理,详解见异常那一节。