栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Java线程同步

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Java线程同步

处理多线程问题时,多个线程访问同一个对象,并且某些线程还想修改这个对象。这时候我们就需要线程同步。线程同步其实就是一种等待机制,多个需要同时访问此对象的线程进入这个对象的等待池形成队列,等待前面线程使用完毕,下一个线程再使用

线程同步会用到队列和锁

同步方法

由于我们可以用过private关键字保证数据对象只能被方法访问,所以我们只需要针对方法提出一套机制,这套机制就是synchronized关键字,它包括两种用法:synchronized方法和synchronized块

//同步方法
public synchronized void method(int args){}

synchronized方法控制对"对象"的访问,每个对象对应一把锁,每个synchronized方法都必须获得调用该方法的对象的锁才能执行,否则线程会阻塞,方法一旦执行,就独占该锁,直到该方法返回才释放锁,后面被阻塞的线程才能获得这个锁,继续执行

缺陷:若将一个大的方法申明为synchronized将会影响效率

//synchronized方法默认锁的是this对象,有些时候,this方法运行run对象,但是要同步的数据并不是this对象,就需要使用同步块

同步块
synchronized(Obj){}

Obj称之为同步监视器

  • Obj可以是任何对象,但是推荐使用共享资源作为同步监视器
  • 同步方法中无需指定同步监视器,因为同步方法的同步监视器就是this,就是这个对象本身,或者是class【反射中讲解】
  • 同步监视器的执行过程
    1. 第一个线程访问,锁定同步监视器,执行其中代码
    2. 第二个线程访问,发现同步监视器被锁定,无法访问
    3. 第一个线程访问完毕,解锁同步监视器
    4. 第二个线程访问,发现同步监视器没有锁,然后锁定并访问
//正确的方法(以买票为例)
public class TestThread1 implements Runnable{
    int ticket=10;
    boolean flag=true;
    public synchronized void buyTicket() throws InterruptedException {
        if(ticket<=0){flag=false;return;}
        Thread.sleep(300);
        System.out.println(Thread.currentThread().getName()+"拿到了"+ticket--+"张票");
    }
    @Override
    public void run() {
        while(flag){

            try {
                buyTicket();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        TestThread1 ts=new TestThread1();
        new Thread(ts,"我").start();
        new Thread(ts,"你").start();
        new Thread(ts,"他").start();
    }
}




//两种错误的同步方法
//例子一
public class TestThread1 implements Runnable{
    int ticket=10;
    @Override
    public synchronized void run() {
        while(true){
            if(ticket<=0)break;
            System.out.println(Thread.currentThread().getName()+"拿到了"+ticket--+"张票");
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        TestThread1 ts=new TestThread1();
        new Thread(ts).start();
        new Thread(ts).start();
        new Thread(ts).start();
    }
}


//例子二,无法做到线程同步
public class TestThread2 {
    public static void main(String[] args) {
        Ticket ticket=new Ticket(10);
        Thread_run me=new Thread_run(ticket);
        Thread_run you=new Thread_run(ticket);
        me.start();
        you.start();
    }
}
class Ticket{
    int ticket;
    Ticket(int x){this.ticket=x;}
}

class Thread_run extends Thread{
    Ticket ticket;
    Thread_run(Ticket ticket){this.ticket=ticket;}

    @Override
    public synchronized void run() {
        while(true){
            if(ticket.ticket<=0)break;
            System.out.println(Thread.currentThread().getName()+"拿到了"+ticket.ticket--+"张票");
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

原因:synchronized方法本质是给this对象加锁。

第一个例子中,加锁的对象是ts,然后将ts放到3个线程中,线程在运行方法时发现对象有锁,就会同步执行。

第二个例子中,枷锁的对象是me和you。但实际上需要同步访问的对象是ticket。程序在运行的过程中,发现对象me和you有锁,但是实际上并没有多个线程同时访问me和you,所以me和you的方法同步执行run方法。从而导致ticket对象出现并发问题

对于第二个例子的问题,需要用到同步块

//使用同步块
public class TestThread2 {
    public static void main(String[] args) {
        Ticket ticket=new Ticket(10);
        Thread_run me=new Thread_run(ticket);
        Thread_run you=new Thread_run(ticket);
        me.start();
        you.start();
    }
}
class Ticket{
    int ticket;
    Ticket(int x){this.ticket=x;}
}

class Thread_run extends Thread{
    Ticket ticket;
    Thread_run(Ticket ticket){this.ticket=ticket;}

    @Override
    public synchronized void run() {
        while(true){
            
            
            synchronized (ticket){
                if(ticket.ticket<=0)break;
                System.out.println(Thread.currentThread().getName()+"拿到了"+ticket.ticket--+"张票");
                try {
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}



转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/352092.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号