【Java实现】Thread/线程安全/线程同步(思维训练)

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();//加锁
//判断余额是否足够   
tryif(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里,异常处理,详解见异常那一节。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值