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

Java多线程6. 线程的同步

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

Java多线程6. 线程的同步

目录

1. 线程同步介绍

2. 互斥锁机制

3. 利用synchronized 关键字实现同步操作

1)同步代码块

实例代码

2)同步方法

实例代码

4. 对synchronized的进一步说明

5. Lock接口(java.util.concurrent.locks)实现线程同步(手动加解锁)

实例代码

6. 线程的死锁


1. 线程同步介绍

        在并发程序设计中,对多线程共享的资源或数据称为临界资源或同步资源,而把每个线程中访问临界资源的那一段代码称为临界代码或临界区。临界区必须互斥地使用,为了使临界代码对临界资源的访问成为一个不可被中断的原子操作,Java技术利用对象“互斥锁”机制来实现线程间的互斥操作。

2. 互斥锁机制

在Java中每个对象都有一个“互斥锁”与之相连。当线程A获得了一个对象的互斥锁后,线程B若也想获得该对象的互斥锁,就必须等待线程A完成规定的操作并释放出互斥锁后才能获得并执行相关的操作。

3. 利用synchronized 关键字实现同步操作

1)同步代码块

     synchronized(对象) {

       临界代码段

     }

对象是多个线程共同操作的公共变量,即需要锁定的临界资源,它将被互斥地使用(常为this、静态Object对象)

实例代码
package Learning;


public class $3_synchronized {
    public static void main(String[] args) {
        window winds = new window();
        Thread t1 = new Thread(winds, "窗口1");
        Thread t2 = new Thread(winds, "窗口2");
        Thread t3 = new Thread(winds, "窗口3");
        //利用同一继承Runnable接口类的对象构建多个线程,实现线程间的数据共享,利用同步代码块实现互斥
        t1.start();
        t2.start();
        t3.start();
    }
}

class window implements Runnable {
    private int tickets = 30;

    @Override
    public void run() {
        while (true) {
            synchronized (this) {
                if(tickets>0){
                    System.out.println(Thread.currentThread().getName() + "售出了" + tickets + "号票");
                    tickets--;
                }
                else break;
            }           //利用synchronized关键词使同步代码块互斥的使用

            try {
                Thread.sleep(50);
               //使用sleep函数,使线程阻塞便于演示
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
package Learning;


public class $4_synchronized {
    public static void main(String[] args) {
        window_t w1=new window_t("窗口1");
        window_t w2=new window_t("窗口2");
        window_t w3=new window_t("窗口3");
        w1.start();
        w2.start();
        w3.start();
    }
}

class window_t extends Thread{
    private static int tickets=30;           //将tickets设置为静态变量来实现线程间数据共享
    private static final Object obj=new Object();           //使用this仍无法实现互斥效果,因为不是同一个线程对象;故创建静态的Object对象使用

    public window_t(String t){
        this.setName(t);
    }
    public void run(){
        while(true) {
            synchronized (obj) {            //此处也可以使用window_t.class反射类(类也是对象?)
                if (tickets > 0) {
                    System.out.println(getName() + "售出了" + tickets + "号票");
                    tickets--;
                } else break;
            }
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

2)同步方法

     public synchronized 返回类型 方法名() {

       方法体

     }(常使用static方法解决继承Thread类的线程安全问题)

实例代码
package Learning;


public class $5_synchronized {
    public static void main(String[] args) {
        window_r winds=new window_r();
        Thread t1 = new Thread(winds, "窗口1");
        Thread t2 = new Thread(winds, "窗口2");
        Thread t3 = new Thread(winds, "窗口3");
        t1.start();
        t2.start();
        t3.start();
    }
}

class window_r implements Runnable {
    private int tickets = 30;

    @Override
    public void run() {
        while (sell()) {
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    private synchronized boolean sell() {
        if (tickets > 0) {
            System.out.println(Thread.currentThread().getName() + "卖出了" + tickets + "号票");
            tickets--;
            return true;
        } else return false;
    }           //synchronized方法,方法会被互斥执行
}

注意:当被synchronized限定的代码段执行完,就自动释放互斥锁。

4. 对synchronized的进一步说明

(1)由于所有锁定同一个临界代码的线程之间在synchronized代码块上是互斥的,这些线程的synchronized代码块之间是串行执行的,不再是互相交替穿插并发执行,因而保证了synchronized代码块操作的原子性

(2)synchronized代码块中的代码数量应只包含必要部分,否则会失去多线程并发执行的很多优势,但应该保证所有对临界代码中共享变量的访问与操作均在synchronized代码块中进行。

(3)临界代码中的共享变量应定义为private型。否则,其他类的方法可能直接访问和操作该共享变量,这样synchronized的保护就失去了意义

(4)对于static方法,要么整个方法是synchronized,要么不是

5. Lock接口(java.util.concurrent.locks)实现线程同步(手动加解锁)

ReentrantLock类:实现了Lock接口,可以利用其对象调用lock()、unlock()方法实现手动加解锁

优先使用顺序:Lock(更为灵活) > 同步代码块 > 同步代码块

实例代码
package Learning;

import java.util.concurrent.locks.ReentrantLock;


public class $6_Lock {
    public static void main(String[] args) {
        window_l wins=new window_l();
        Thread t1=new Thread(wins,"窗口1");
        Thread t2=new Thread(wins,"窗口2");
        Thread t3=new Thread(wins,"窗口3");
        t1.start();
        t2.start();
        t3.start();
    }
}

class window_l implements Runnable{
    private static int tickets=30;
    private final ReentrantLock lock=new ReentrantLock();
    @Override
    public void run() {
        while(true){
            try {
                lock.lock();            //加锁
                if(tickets>0){
                    System.out.println(Thread.currentThread().getName()+"卖出了"+tickets+"号票");
                    tickets--;
                }
                else break;
            }
            finally {           //加上finally,否则break后未解锁无法继续运行
                lock.unlock();          //解锁
            }
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

6. 线程的死锁

        两个或多个线程相互等待导致都不能执行;产生的条件为:

                (1)互斥条件

                (2) 请求和保持条件

                (3) 不剥夺条件

                (4) 环路等待条件

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

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

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