Java多线程6. 线程的同步

Java多线程6. 线程的同步,第1张

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) 环路等待条件

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/5636951.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-16
下一篇 2022-12-16

发表评论

登录后才能评论

评论列表(0条)

保存