Java多线程同步解决线程安全的理解

Java多线程使用同步机制处理线程安全的理解

** ** Java中多线程的创建这里只说两种。
1.继承于Thread类
①即创建一个Thread的子类
②重写Thread类中的run()方法,方法体中写线程执行的操作
③通过子类的对象调用Thread类中的start()方法,通过start()方法启动线程,
由于通过动态绑定的机制,最终调用该线程的run()方法,即子类重写的。

//创建一个继承Thread类的子类
class ThreadTest extends Thread{

    //重写父类中的run()方法
    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            System.out.println(currentThread().getName() + ":" + i);
        }
    }
}

public class Textkang {
    public static void main(String[] args) {
        //创建子类对象
        ThreadTest threadTest = new ThreadTest();
        ThreadTest threadTest1 = new ThreadTest();
        //设置线程名
        threadTest.setName("线程1:");
        threadTest1.setName("线程2:");
        //调用start()方法启动线程
        threadTest.start();
        threadTest1.start();
    }
}

在这里插入图片描述

2.实现Runnable接口
①创建一个Runnable接口的实现类
②实现Runnable接口中的虚拟方法run(),方法体中写线程执行的操作
③以该实现类对象为参数传入Thread类的构造器中,创建Thread类的对象,
并通过Thread类的对象调用start()方法去启动该线程,调用该线程的run()方法

//创建一个是实现Runnable接口的实现类
class ThreadTest implements Runnable{

    //实现接口中的run()方法
    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            System.out.println(Thread.currentThread().getName() + ":" + i);
        }
    }
}

public class Textkang {
    public static void main(String[] args) {
        //创建实现类对象
        ThreadTest threadTest = new ThreadTest();
        //创建新线程
        Thread thread1 = new Thread(threadTest);
        Thread thread2 = new Thread(threadTest);
        //设置线程名字
        thread1.setName("线程一:");
        thread2.setName("线程二:");
        //调用start()方法启动线程
        thread1.start();
        thread2.start();
    }
}

在这里插入图片描述

多线程操作共享数据时,有时会出现线程安全的问题。

//创建一个销售商品的类,继承于Thread类
class Sale extends Thread{
    //设置商品数量为50个
    private static int  commodity = 50;
    @Override
    public void run() {
        for(;;){
            if(commodity > 0){
                System.out.println(getName() + ":商品为:" + commodity + "号");
                commodity--;
            }else {
                break;
            }
        }
    }
}

public class SaleTest {
    public static void main(String[] args) {
        //设置三个售货口一起销售一批商品
        Sale sale1 = new Sale();
        Sale sale2 = new Sale();
        Sale sale3 = new Sale();
        //设置售货口名字
        sale1.setName("售货口1:");
        sale2.setName("售货口2:");
        sale3.setName("售货口3:");

        sale1.start();
        sale2.start();
        sale3.start();

    }
}

这里没用做啥处理,所以出现了线程安全问题,比如同一个货物被三个窗口售卖,这显然是不被允许的。
在这里插入图片描述
同样用实现Runnable接口的方式如果不做处理,一样可能出现线程安全问题。
这里就不上代码了。

Java中通过同步机制处理线程安全问题,关键字:synchronized
同步机制中的同步监视器(也称“锁”)必须要唯一。
一、使用同步方法的理解
使用同步方法:①当使用继承于Thread类的方式创建线程,开多个线程时,就要new多个子类的对象,操作共享数据时,用同步方法处 理线程安全问题,那么同步方法就要声明为静态的,多个对象共享一个同步方法(同步方法的锁虽然没有声明出来,其实也是存在的,其实就是当前类充当的锁,类只加载一次,符合唯一性。)。即多个线程共抢一个锁,哪个线程抢到锁,就执行下去,其他线程则等待锁被释放。

//创建一个销售商品的类,继承于Thread类
class Sale extends Thread {
    //设置商品数量为50个
    private static int commodity = 500;

    @Override
    public void run() {
        for(;;) {
            sales();
        }
    }
    public static synchronized void sales() {//这里的同步监视器是该类本身 Sale.class 
            if (commodity > 0) {

                try {
                    sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                System.out.println(Thread.currentThread().getName() + "商品为:" + commodity + "号");
                commodity--;
            }
        }
    }

public class SaleTest {
    public static void main(String[] args) {
        //设置三个售货口一起销售一批商品
        Sale sale1 = new Sale();
        Sale sale2 = new Sale();
        Sale sale3 = new Sale();
        //设置售货口名字
        sale1.setName("售货口1:");
        sale2.setName("售货口2:");
        sale3.setName("售货口3:");

        sale1.start();
        sale2.start();
        sale3.start();

    }
}

结果由于是竖着的,不好截图,就没弄。可以复制代码上机验证。

②当使用实现Runnable接口的方式传进线程,开多个线程,也只new了一个实现类对象,操作共享数据时,用同步方法 处理线程安全问题的话,同步方法不用声明为静态的,抢锁同上。

//创建一个销售商品的类,实现Runnable接口
class Sale implements Runnable {
    //设置商品数量为50个
    private static int commodity = 500;

    @Override
    public void run() {
        for(;;) {
            sales();
        }
    }
    public static synchronized void sales() {//这里的同步监视器是该类本身 Sale.class
            if (commodity > 0) {

                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                System.out.println(Thread.currentThread().getName() + "商品为:" + commodity + "号");
                commodity--;
            }
        }
    }

public class SaleTest {
    public static void main(String[] args) {
        //设置三个售货口一起销售一批商品
        Sale sale = new Sale();
        Thread sale1 = new Thread(sale);
        Thread sale2 = new Thread(sale);
        Thread sale3 = new Thread(sale);
        //设置售货口名字
        sale1.setName("售货口1:");
        sale2.setName("售货口2:");
        sale3.setName("售货口3:");

        sale1.start();
        sale2.start();
        sale3.start();

    }
}

二、使用同步代码块理解
使用同步代码块:①当使用当使用继承于Thread类的方式创建线程,开多个线程时,就要new多个子类的对象,操作共享数据时,用同步代码块的话,因为多个对象,每个对象都有自己的run()方法,同步代码块在run()方法中,也有多个,但是锁是唯一的,即不管有多少个run()方法,只有一个锁,当某个线程抢到锁,它就顺利执行,其他线程则等待锁被释放。

//创建一个销售商品的类,继承于Thread类
class Sale extends Thread{
    //设置商品数量为50个
    private static int  commodity = 50;
    @Override
    public void run() {
        for(;;){
            synchronized(Sale.class) {//这里是用的该类的类名作为的同步监视器,
                                    // 也可用其他类的对象作为同步监视器,切记同步监视器要唯一
                if (commodity > 0) {
                    System.out.print(getName() + "商品为:" + commodity + "号");
                    commodity--;
                } else {
                    break;
                }
            }
        }
    }
}

public class SaleTest {
    public static void main(String[] args) {
        //设置三个售货口一起销售一批商品
        Sale sale1 = new Sale();
        Sale sale2 = new Sale();
        Sale sale3 = new Sale();
        //设置售货口名字
        sale1.setName("售货口1:");
        sale2.setName("售货口2:");
        sale3.setName("售货口3:");

        sale1.start();
        sale2.start();
        sale3.start();

    }
}

②当使用实现Runnable接口的方式传进线程,开多个线程,也只new了一个实现类对象,操作共享数据时,用同步代码块的话,由于只创建了一个实现类对象,那么run()方法就只有一份,同步代码块也只有一份,依然是哪个线程抢到锁,就执行,其他线程等待。

//创建一个销售商品的类,实现Runnable接口
class Sale implements Runnable {
    //设置商品数量为50个
    private static int commodity = 500;

    @Override
    public void run() {
        for(;;) {
            synchronized (this){//这里使用的是该对象作为的同步监视器,
                            // 还可以用其他的对象做同步监视器,保证唯一性就行
                if(commodity > 0) {

                    try {
                        Thread.sleep(15);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                    System.out.println(Thread.currentThread().getName() + "商品为:" + commodity + "号");
                    commodity--;
                }else {
                    break;
                }
            }
        }

        }
    }

public class SaleTest {
    public static void main(String[] args) {
        //设置三个售货口一起销售一批商品
        Sale sale = new Sale();
        Thread sale1 = new Thread(sale);
        Thread sale2 = new Thread(sale);
        Thread sale3 = new Thread(sale);
        //设置售货口名字
        sale1.setName("售货口1:");
        sale2.setName("售货口2:");
        sale3.setName("售货口3:");

        sale1.start();
        sale2.start();
        sale3.start();

    }
}
评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值