多线程安全

目录

线程调度:

主线程:

Thread类

构造方法:

常用方法:

创建线程方式一之继承类创建

代码示例:

结果:

创建线程方式二之Runnable类创建(一般都用这种)

Runnable接口

代码演示:

线程休眠:

代码演示

结果:

多线程安全 

怎样会出现线程安全:

怎么解决该问题?

怎么保证线程安全:

同步代码块:

加同步格式:

注意:

同步方法:

格式:

静态同步方法:

以上三种方法的代码演示

锁升级

代码举例


线程调度:

线程调度: ·分时调度 所有线程轮流使用CPU的使用权,平均分配每个线程占用CPU 的时间。·抢占式调度 优先让优先级高的线程使用CPU,如果线程的优先级相同,那么会随机选择一个(线程随机性) ,java使用的为抢占式调度。

主线程:

主线程:执行主(main)方法的线程

单线程程序:java程序中只有一个线程执行从main方法开始,从上到下依次执行

Thread类

Java使用java.lang.Thread类代表线程,所有的线程对象都必须是Thread类或其子类的实例。每个线程的作用是完成一定的任务,实际上就是执行一段程序流即一段顺序执行的代码。Java使用线程执行体来代表这段程序流。Java中通过继承Thread类来创建启动多线程

先学习Thread的两个简单的构造方法:

public Thread(): 无参构造
public Thread(String name) :指定线程名字

构造方法:

  • public Thread(Runnable target):分配一个带有指定目标新的线程对象。

  • public Thread(Runnable target,String name):分配一个带有指定目标新的线程对象并指定名字。

常用方法:

  • public String getName():获取当前线程名称。

  • public void setName():设置当前线程名称。

  • public void start():导致此线程开始执行; Java虚拟机调用此线程的run方法。

  • public void run():此线程要执行的任务在此处定义代码。

  • public static void sleep(long millis):使当前正在执行的线程以指定的毫秒数暂停(暂时停止执行)。

  • public static Thread currentThread():返回对当前正在执行的线程对象的引用。

这些方法都很简单,自己在代码上都会很快应用。

创建线程方式一之继承类创建

代码示例:

//新建的线程类继承线程,重写run方法
/**
 * 创建线程类的第一种方式继承Thread类
 */
public class MyThread extends Thread{
    //重写run方法
    @Override
    public void run() {
        super.run();
        for (int i = 0; i <5 ; i++) {
            System.out.println("run"+i);
        }
    }
}
​
​
​
​
//测试类,也就是主线程
public class Test {
    public static void main(String[] args) {
        //创建Thread类的子类对象
        MyThread myThread = new MyThread();
        //通过该对象调用start方法启动线程
        myThread.start();
​
        //主线程的调用
        for (int i = 0; i <5 ; i++) {
            //结果是两个线程随机抢夺cpu执行权
            System.out.println("main"+i);//main0 run0 main1 run1
        }
    }
}

结果:

 

创建线程方式二之Runnable类创建(一般都用这种)

概念:

创建一个类来实现Runnable接口,并实现Runnable接口中的run函数。

任务类实现Runnable,重写run方法

Runnable接口

1)我们发现,Thread类其实已经实现Runnable接口了,Thread类中的run方法就是实现来自Runnable接口的。

2)Runnable接口中,只有一个run方法,而run方法是用来封装线程任务的。因此,这个接口就是专门用来封装线程任务的接口。为了区分和上述的Thread类,

因此,我们可以这样理解:实现该接口的类,称为线程任务类。

构造方法:

  • public Thread(Runnable target):分配一个带有指定目标新的线程对象。

  • public Thread(Runnable target,String name):分配一个带有指定目标新的线程对象并指定名字。

注意:第二个构造函数的String类型的name是给自己定义的任务类线程起名字,不用在使用setName()给线程起名字。

代码演示:

//任务类实现Runnable,重写run方法
public class MyRunble implements Runnable{
    @Override
    public void run() {
        int sum=0;
        for (int i = 0; i <6 ; i++) {
            sum+=i;
        }
       //得到当前线程的名字 
    System.out.println(Thread.currentThread().getName());
    }
}
​
​
//测试类
public class RunbleTest {
    public static void main(String[] args) {
        //创建线程任务类对象
        MyRunble myRunble = new MyRunble();
​
        //创建线程对象(利用Thread的构造方法,对线程进行命名,而不是通过setname()方法给线程命名)
        Thread thread1 = new Thread(myRunble, "我是线程1");
        Thread thread2 = new Thread(myRunble, "我是线程2");
​
        //启动线程
        thread1.start();//我是线程1
        thread2.start();//我是线程2
    }
}
​

实现RunnobLe接口创建多线程程序的好处:

1.避免了单继承的局限性 一个类只能继承一个类(一个人只能有一个亲爹),类继承了Thread类就不能继承其他的类实现了Runnable接口,还可以继承其他的类,实现其他的接口

2.增强了程序的扩展性,降低了程序的耦合性(解耦) 实现RunnabLe接口的方式,把设置线程任务和开启新线程进行了分离(解耦)实现类中,重写了run方法:用来设墨线程任务 创建Thread类对象,调用start方法:用来开启新线程

线程休眠:

使用Thread类中的sleep()函数可以让线程休眠,函数如下所示:

static void sleep(long millis) 在指定的毫秒数内让当前正在执行的线程休眠(暂停执行)

代码演示

//自定义类
public class RunnableSleep implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i <5 ; i++) {
            //获取当前线程的名字
            System.out.println(Thread.currentThread().getName()+"->"+i);
            //休眠一秒,程序会暂停一秒运行出结果
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
​
​
//测试类
public class Test2 {
    public static void main(String[] args) {
        RunnableSleep sleep = new RunnableSleep();
        Thread thread = new Thread(sleep, "线程一");
        thread.start();
​
    }
}

结果:

 

多线程安全 

怎样会出现线程安全:

多线程访问了共享的数据,会产生线程安全问题(举个例子,就是卖票,有四个窗口在卖,但是突然出现了几个窗口卖同一张票,这时这几个窗口就访问了共享的数据(相同的一张票),就出现了线程安全问题)。

怎么解决该问题?

当cpu执行某个线程时,就只让该线程访问共享的数据,其他线程不被允许。这叫线程同步

怎么保证线程安全:

需要在操作共享数据的地方,加上线程的同步。

同步代码块和同步方法都可以保证线程安全。

同步代码块:

加同步格式:

synchronized( 需要一个任意的对象(锁) ){
​
    代码块中放操作共享数据的代码。
}

注意:

同步代码块上的锁,可以是随便任意的一个对象。你可以这样新建或者不新建

Object obj=new Object();
//或者这样
synchronized("随便叫什么都行"){
    
}

注意: 1.通过代码块中的锁对象,可以使用任意的对象

2.但是必须保证多个线程使用的锁对象是同一个

3.锁对象作用: 把同步代码块锁住,只让一个线程在同步代码块中执行

同步方法:

被synchronized关键字修饰的方法称为同步方法

格式:

public synchronized void method(){
    可能会出现线程安全的代码
}

静态同步方法:

就是在同步方法上添加static关键字就行

以上三种方法的代码演示

/**
 * 卖票问题
 *
 * 卖票案例出现了线程安全问题卖出了不存在的票和重复的票
 * 解决方法1.同步代码块
 * 2.同步方法
 * 3.静态同步方法
 *
 */
public class MaiPiao implements Runnable{
    static int  number=100;
    //新建一个锁对象
    Object obj=new Object();
    @Override
    public void run() {
      while (true){
          staticfanfa();
      }
        //1.同步代码块解决线程安全问题
        /*  //死循环一直卖票
      while (true){
          synchronized (obj){
              if (number>0){
                  //休眠,为了让别的线程有机会运行该程序
                  try {
                      Thread.sleep(1);
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }
                  //打印当前线程的名字和票号
                  System.out.println(Thread.currentThread().getName()+"===票号"+number);
                  number--;
              }
          }
      }*/
​
    }
    //3.静态同步方法
    public static synchronized void staticfanfa(){
        if (number>0){
            //休眠,为了让别的线程有机会运行该程序
            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //打印当前线程的名字和票号
            System.out.println(Thread.currentThread().getName()+"===票号"+number);
            number--;
        }
    }
​
    //2.同步方法
    public synchronized void fanfa(){
        if (number>0){
            //休眠,为了让别的线程有机会运行该程序
            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //打印当前线程的名字和票号
            System.out.println(Thread.currentThread().getName()+"===票号"+number);
            number--;
        }
    }
}
​
​
​
//测试类
public class Test {
    public static void main(String[] args) {
        //创建线程类任务对象
        MaiPiao maiPiao = new MaiPiao();
        //创建线程对象
        Thread thread = new Thread(maiPiao,"一号");
        Thread thread2 = new Thread(maiPiao,"二号");
        Thread thread3 = new Thread(maiPiao,"三号");
        Thread thread4 = new Thread(maiPiao,"四号");
        //启动线程
        thread.start();
        thread2.start();
        thread3.start();
        thread4.start();
    }
}

锁升级

使用Lock来解决线程安全问题

代码举例

//任务类
public class Task implements Runnable{
    int number=100;
    //锁对象
    Lock lock = new ReentrantLock();
    @Override
    public void run() {
        while (true){
            //加锁
            lock.lock();
                if (number>0){
                    //休眠,为了让别的线程有机会运行该程序
                    try {
                        Thread.sleep(1);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    //打印当前线程的名字和票号
                    System.out.println(Thread.currentThread().getName()+"===票号"+number);
                    number--;
                }
                //释放锁
            lock.unlock();
        }
    }
}
​
​
//测试类
public class Test2 {
    public static void main(String[] args) {
        //创建线程类任务对象
        Task task = new Task();
        //创建线程对象
        Thread thread = new Thread(task,"一号");
        Thread thread2 = new Thread(task,"二号");
        Thread thread3 = new Thread(task,"三号");
        Thread thread4 = new Thread(task,"四号");
        //启动线程
        thread.start();
        thread2.start();
        thread3.start();
        thread4.start();
    }
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值