线程及线程安全

线程

Thread类

Java.lang.Thread类定义了有关线程的一些方法:
构造方法:
public Thread() :分配一个新的线程对象。
public Thread(String name) :分配一个指定名字的新的线程对象。
public Thread(Runnable target) :分配一个带有指定目标新的线程对象。
public Thread(Runnable target,String name) :分配一个带有指定目标新的线程对象并指定名字。
常用方法:
public String getName() :获取当前线程名称。
public void start() :导致此线程开始执行; Java虚拟机调用此线程的run方法。
public void run() :此线程要执行的任务在此处定义代码。
public static void sleep(long millis) :使当前正在执行的线程以指定的毫秒数
**ps:**创建线程的方式有两种,一种是继承Thread类方式,一种是实现Runnable接口方式

创建线程方式二(较为常用)

创建多线程程序的第二种方式:实现Runnable接口
java.lang.Runnable
Runnable接口应该由那些打算通过某一线程执行其实例的类来实现。类必须定义一个称为 run 的无参数方法。
.lang.Thread类的构造方法
Thread(Runnable target) 分配新的 Thread 对象。
Thread(Runnable target, String name) 分配新的 Thread 对象。
实现步骤:
1、创建一个Runnable接口的实现类
2、在实现类中重写Runnable接口中的run方法,设置线程任务
3、创建一个runnable接口的实现类对象
4、创建Tread对象,构造方法中传递Runnablejie接口的实现类对象
5、调用Thread类中start方法,开启新的线程执行run方法
tips:Runnable对象仅仅作为Thread对象的Target,Runnable实现类里包含的run方法仅作为线程的知兴替。而实际线程对象依然是Tread实例,只是该Tread线程负责执行其Target的run()方法

package RunnableTest;
//1、创建一个Runnable实现类
public class RunnableImpl implements Runnable{
    //2、在实现类中重写Runanble接口run方法,设置线程任务
    @Override
    public void run() {
        for (int i = 0; i <20 ; i++) {
            System.out.println(Thread.currentThread().getName()+"-->"+i);

        }
    }
}

package RunnableTest;

public class RunnableTestDemo {
    public static void main(String[] args) {
        //3、创建一个runnable接口的实现类对象
        RunnableImpl  run =new RunnableImpl();
        //4、创建Thread类对象,构造方法中传递runnable接口实现类对象
        Thread t = new Thread(run);
        //5、调用Thread类中start方法,开启新的线程执行run方法
        t.start();
        for (int i = 0; i <20 ; i++) {
            System.out.println(Thread.currentThread().getName()+"-->"+i);

        }
    }
}

实现Runnable接口创建多线程程序的好处:
1.避免了单继承的局限性
一个类只能继承一个类(一个人只能有一个亲爹),类继承了Thread类就不能继承其他的类
实现了Runnable接口,还可以继承其他的类,实现其他的接口
2.增强了程序的扩展性,降低了程序的耦合性(解耦)
实现Runnable接口的方式,把设置线程任务和开启新线程进行了分离(解耦)
实现类中,重写了run方法:用来设置线程任务
创建Thread类对象,调用start方法:用来开启新线程

创建线程方式三

匿名内部类方式实现线程的创建匿名内部类方式实现线程的创建
匿名:没有名字
**内部类:**写在其他类内部的类
匿名内部类的作用:简化代码
把子类继承父类,重写父类的方法,创建子类对象合一步完成
把实现类实现类接口,重写接口中的方法,创建实现类对象合成一步完成
**匿名内部类的最终产物:**子类/实现类对象,而这个类没有名字
格式:

new 父类/接口(){
    重写父类/接口中的方法
}
public class Demo01InnerClassThread {
    public static void main(String[] args) {
        //线程的父类是Thread
        // new MyThread().start();
        new Thread(){
            //重写run方法,设置线程任务
            @Override
            public void run() {
                for (int i = 0; i <20 ; i++) {
                    System.out.println(Thread.currentThread().getName()+"-->"+"黑马");
                }
            }
        }.start();

        //线程的接口Runnable
        //Runnable r = new RunnableImpl();//多态
        Runnable r = new Runnable(){
            //重写run方法,设置线程任务
            @Override
            public void run() {
                for (int i = 0; i <20 ; i++) {
                    System.out.println(Thread.currentThread().getName()+"-->"+"程序员");
                }
            }
        };
        new Thread(r).start();

        //简化接口的方式
        new Thread(new Runnable(){
            //重写run方法,设置线程任务
            @Override
            public void run() {
                for (int i = 0; i <20 ; i++) {
                    System.out.println(Thread.currentThread().getName()+"-->"+"传智播客");
                }
            }
        }).start();
    }

线程安全

线程安全

package RunnableTest;

public class Ticket implements Runnable {
    //定义一个多个线程共享的票据
    private int ticket =100;
    //设置线程任务:卖票
    @Override
    public void run() {
        while(true){
            if (ticket>0){
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+"-->"+ticket+"张票");
                ticket--;
            }
        }

    }
}

package RunnableTest;

public class Demo01Ticket {
    public static void main(String[] args) {
        Ticket tic= new Ticket();
        Thread th1 =new Thread(tic);
        Thread th2 =new Thread(tic);
        Thread th3 =new Thread(tic);
        th1.start();
        th2.start();
        th3.start();
    }
}

运行上述代码块出现了两个问题:
1、相同的票数,比如2这张票卖了两回
2、不存在的票,比如0或-1票是不存在的
出现上述问题为线程不安全
ps:线安全问题都是由全局变量及静态变量引起的,若每个线程中对全局变量,静态变量只有读操作,而无写操作,一般来说,这个全局变量是线程安全的,若有多个线程同时执行写操作,一般都要考虑线程同步,否则的话就可能影响线程安全。

线程同步

当我们使用多个线程访问同一资源的时候,且多个线程对程序都有写的操作,就容易出现线程安全问题
要解决上述多线程并发访问一个资源的安全性问题:也就是解决票不存在或重复问题,Java提供了同步机制(synchronized)来解决
为了保证每个线程都能正常执行原操作,Java引入了同步机制,有如下三种方式完成同步操作:
1、同步代码块
2、同步方法
3、锁机制

同步代码块

格式:

synchronized(锁对象){
可能会出现线程安全问题的代码(访问了共享数据的代码)
}

eg:Object obj =new Object();//创建一个锁对象

package RunnableTest;

public class TicketRunnableIplm01 implements Runnable{
    //定义一个多个线程共享的票据
    private int ticket =100;
    //创建一个锁对象
    Object obj = new Object();
    //设置线程任务:卖票
    @Override
    public void run() {
        //使用死循环,让卖票操作重复执行
        while (true){
            //同步代码块
            synchronized (obj){
                if (ticket>0){
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName()+"-->"+ticket+"张票");
                    ticket--;
                }
            }
        }


    }
}

package RunnableTest;

public class TicketRunnableIplm01Demo {
    public static void main(String[] args) {
        TicketRunnableIplm01 ticket = new TicketRunnableIplm01();
        //TicketRunnableImpl02 ticket = new TicketRunnableImpl02();
        // ticket = new TicketRunnableImpl03();
        Thread t1= new Thread(ticket);
        Thread t2= new Thread(ticket);
        Thread t3= new Thread(ticket);
        t1.start();
        t2.start();
        t3.start();
    }
}

注意:
1、锁对象,可以是任意对象
2、多个线程对象 要使用同一把锁
3、锁对象作用:
把同步代码块锁着,只让一个线程在同步代码块中执行,其他线程只能在外等着(block)

package RunnableTest;
 /*
       定义一个同步方法
       同步方法也会把方法内部的代码锁住
       只让一个线程执行
       同步方法的锁对象是谁?
       就是实现类对象 new RunnableImpl()
       也是就是this
    */
// public synchronized void payTicket(){
public class TicketRunnableIplm04 implements Runnable{
   private int ticket =100;
   @Override
   public void run() {
       System.out.println("this:"+this);
       //使用死循环,让卖票操作重复执行
       while (true){
           //同步代码块
           synchronized (this){
               if (ticket>0){
                   try {
                       Thread.sleep(10);
                   } catch (InterruptedException e) {
                       e.printStackTrace();
                   }
                   System.out.println(Thread.currentThread().getName()+"-->"+ticket+"张票");
                   ticket--;
               }
           }
       }
   }
}

package RunnableTest;

public class TicketRunnableIplm01Demo {
    public static void main(String[] args) {
        //TicketRunnableIplm01 ticket = new TicketRunnableIplm01();
        //TicketRunnableImpl02 ticket = new TicketRunnableImpl02();
        //TicketRunnableImpl03 ticket = new TicketRunnableImpl03();
        TicketRunnableIplm04 ticket = new TicketRunnableIplm04();
        System.out.println("run:"+ticket);
        Thread t1= new Thread(ticket);
        Thread t2= new Thread(ticket);
        Thread t3= new Thread(ticket);
        t1.start();
        t2.start();
        t3.start();
    }
}

    /*
        静态的同步方法
        锁对象是谁?
        不能是this
        this是创建对象之后产生的,静态方法优先于对象
        静态方法的锁对象是本类的class属性-->class文件对象(反射)
     */
  //  public static synchronized void payTicketStatic(){
        synchronized (RunnableImpl.class){
            //先判断票是否存在
            if(ticket>0){
                //提高安全问题出现的概率,让程序睡眠
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                //票存在,卖票 ticket--
                System.out.println(Thread.currentThread().getName()+"-->正在卖第"+ticket+"张票");
                ticket--;
            }
        }

   // }  

使用同步方法

使用synchronized修饰的方法,就叫做同步方法,保证A线程执行该方法的时候,其他线程只能在外等着
格式:

public synchronized void method(参数列表){
可能会产生线程安全问题的代码
}

使用步骤:
1、把访问了共享数据的代码抽取出来,放到一个方法当中
2、在这个方法添加synchronized修饰符

package RunnableTest;

public class TicketRunnableImpl02 implements Runnable{
    //定义一个多个线程共享的票据
    private int ticket =100;
    //设置线程任务:卖票
    @Override
    public void run() {
        //使用死循环,让卖票操作重复执行
        while (true){
            sellTicket();

        }


    }

    public synchronized void sellTicket() {
            if (ticket>0){
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+"-->"+ticket+"张票");
                ticket--;
            }

    }
}

使用Lock锁

java.util.concurrent.locks.Lock接口
Lock 实现提供了比使用 synchronized 代码块和synchronized方法更广泛的锁定操作,同步代码块/同步方法具有的功能Lock都有,除此之外更强大,更体现面向对象。
Lock接口中的方法:
void lock()获取锁。
void unlock() 释放锁。
java.util.concurrent.locks.ReentrantLock implements Lock接口
使用步骤:
1、在成员位置创建一个ReentrantLock对象
2、在可能出现安全问题的代码前调用Lock接口中的方法lock获取锁

 Lock lock= new ReentrantLock();

3、在可能出现安全问题代买前调用Lock接口中的unLock释放锁

package RunnableTest;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class TicketRunnableImpl03 implements Runnable {
    //定义一个多个线程共享的票据
    private int ticket =100;
    Lock lock= new ReentrantLock();
    //设置线程任务:卖票
    @Override
    public void run() {
        //使用死循环,让卖票操作重复执行
        while (true){
            lock.lock();
                if (ticket>0){
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName()+"-->"+ticket+"张票");
                    ticket--;
                }
                lock.unlock();
            }



    }
}

package RunnableTest;

public class TicketRunnableIplm01Demo {
    public static void main(String[] args) {
        //TicketRunnableIplm01 ticket = new TicketRunnableIplm01();
        //TicketRunnableImpl02 ticket = new TicketRunnableImpl02();
        TicketRunnableImpl03 ticket = new TicketRunnableImpl03();
        Thread t1= new Thread(ticket);
        Thread t2= new Thread(ticket);
        Thread t3= new Thread(ticket);
        t1.start();
        t2.start();
        t3.start();
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值