精华内容
下载资源
问答
  • synchronized关键字经常被用来做线程互斥锁,但是使用不当的话,经常达不到目的。初学者常对锁住的是对象还是类有疑问。 原理:无论是对象还是类都有唯一的锁,synchronized只是声明了函数调用时需要什么锁,每个锁...

    synchronized关键字经常被用来做线程互斥锁,但是使用不当的话,经常达不到目的。初学者常对锁住的是对象还是类有疑问。
    原理:无论是对象还是类都有唯一的锁,synchronized只是声明了函数调用时需要什么锁,每个锁同一时间只能由一个线程获取,借此实现了线程互斥。
    (1)分析对象锁
    A.synchronized修饰非静态函数
    接下来看实例:

    public enum Person {
        Alice,
        Bob;
    
        public synchronized void say() {
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + " say hello world! " +TimeUtil.getCurrentTime());
        }
    
        public synchronized void speak() {
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + " speak hello world! " + TimeUtil.getCurrentTime());
        }
    
        public void print() {
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + " print hello world! " + TimeUtil.getCurrentTime());
        }
    
    }

    这是一个Person类,其中有三个函数,say()和speak()这种声明方式意味着调用该函数需要持有Person实例的对象锁,即用哪个实例调用,则需要持有哪个对象的锁。print()函数无需任何锁。

    public class ThreadA implements Runnable {
        @Override
        public void run() {
            Person.Alice.say();
        }
    }
    
    public class ThreadB implements Runnable {
        @Override
        public void run() {
            Person.Bob.say();
        }
    }
    
    public class ThreadC implements Runnable {
        @Override
        public void run() {
            Person.Alice.print();
        }
    }
    
    public static String getCurrentTime() {
        SimpleDateFormat sdf = new SimpleDateFormat("mm:ss");
        return sdf.format(new Date());
    }
    

    创建多个线程,然后执行函数:

    public class Main {
        public static void main(String[] args) {
            Thread threadA = new Thread(new ThreadA());
            threadA.setName("threadA");
            Thread threadB = new Thread(new ThreadB());
            threadB.setName("threadB");
            Thread threadC = new Thread(new ThreadC());
            threadC.setName("threadC");
            threadA.start();
            System.out.println("A启动了 " + TimeUtil.getCurrentTime());
            threadB.start();
            System.out.println("B启动了 " + TimeUtil.getCurrentTime());
            threadC.start();
            System.out.println("C启动了 " + TimeUtil.getCurrentTime());
        }
    }

    输出结果为:
    A启动了 20:26
    B启动了 20:26
    C启动了 20:26
    threadA say hello world! 20:28
    threadC print hello world! 24:21
    threadB say hello world! 20:28

    可以看出,用两个实例分别调用say()函数是不会出现互斥的。函数执行时,每个函数都可以拿到调用对象的锁。
    接下来我们进行改动,将ThreadB改为:

    public class ThreadB implements Runnable {
        @Override
        public void run() {
            Person.Alice.say();
        }
    }

    执行输出:
    A启动了 25:33
    B启动了 25:33
    C启动了 25:33
    threadA say hello world! 25:35
    threadC print hello world! 25:35
    threadB say hello world! 25:37
    可以明显看出线程B和A存在对象锁竞争,A持有Alice锁的时候,B等待。
    改动ThreadB为

    public class ThreadB implements Runnable {
        @Override
        public void run() {
            Person.Alice.speak();
        }
    }

    执行输出:
    A启动了 27:29
    B启动了 27:29
    C启动了 27:29
    threadA say hello world! 27:31
    threadC print hello world! 27:31
    threadB speak hello world! 27:33
    可以看出即使用Alice调用不同的函数,还是会出现等待.
    总结:对象锁同一时间只能由一个线程持有,此时其余线程无法再用此对象调用需要此对象锁的函数。
    B.synchronized修饰代码块
    实例如下:

    public enum Person {
        Alice,
        Bob;
    
        public synchronized void say() {
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + " say hello world! " + TimeUtil.getCurrentTime());
        }
    
        public void speak() {
            synchronized (Alice) {
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + " speak hello world! " + TimeUtil.getCurrentTime());
            }
        }
    }
    
    public class ThreadA implements Runnable {
        @Override
        public void run() {
            Person.Alice.say();
        }
    }
    
    public class ThreadB implements Runnable {
        @Override
        public void run() {
            Person.Alice.speak();
        }
    }
    
    public class Main {
        public static void main(String[] args) {
            Thread threadA = new Thread(new ThreadA());
            threadA.setName("threadA");
            Thread threadB = new Thread(new ThreadB());
            threadB.setName("threadB");
            threadA.start();
            System.out.println("A启动了 " + TimeUtil.getCurrentTime());
            threadB.start();
            System.out.println("B启动了 " + TimeUtil.getCurrentTime());
        }
    }

    执行输出:
    A启动了 35:26
    B启动了 35:26
    threadA say hello world! 35:28
    threadB speak hello world! 35:30
    可以看出A和B出现了互斥,A调用的say()函数用
    synchronized关键字修饰,所以此时A占用Alice锁,speak中的代码块用synchronized (Alice)修饰,说明同样需要Alice锁,因此互斥。
    改动speak函数:

    public void speak() {
        synchronized (Bob) {
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + " speak hello world! " + TimeUtil.getCurrentTime());
        }
    }

    将需求锁改为Bob,执行输出:
    A启动了 38:36
    B启动了 38:36
    threadA say hello world! 38:38
    threadB speak hello world! 38:38
    可以看到互斥消失了。
    继续更改:

        public void speak() {
            synchronized (this) {
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + " speak hello world! " + TimeUtil.getCurrentTime());
            }
        }

    这里的this指的是需要调用当前函数的对象锁,执行输出:
    A启动了 40:45
    B启动了 40:45
    threadA say hello world! 40:47
    threadB speak hello world! 40:49
    互斥又出现了,因为又在竞争Alice锁
    (2)分析类锁
    每一个类都有唯一且同一时间只能被唯一线程持有的类锁。
    实例如下:

    public enum Person {
        Alice,
        Bob;
    
        public static synchronized void say() {
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + " say hello world! " + TimeUtil.getCurrentTime());
        }
    
        public static synchronized void speak() {
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + " speak hello world! " + TimeUtil.getCurrentTime());
        }
    }
    
    public class ThreadA implements Runnable {
        @Override
        public void run() {
            Person.say();
        }
    }
    
    public class ThreadB implements Runnable {
        @Override
        public void run() {
            Person.speak();
        }
    }
    
    public class Main {
        public static void main(String[] args) {
            Thread threadA = new Thread(new ThreadA());
            threadA.setName("threadA");
            Thread threadB = new Thread(new ThreadB());
            threadB.setName("threadB");
            threadA.start();
            System.out.println("A启动了 " + TimeUtil.getCurrentTime());
            threadB.start();
            System.out.println("B启动了 " + TimeUtil.getCurrentTime());
        }
    }

    执行输出:
    A启动了 44:18
    B启动了 44:18
    threadA say hello world! 44:20
    threadB speak hello world! 44:22
    明显看到线程互斥。
    改动代码:

        public static void speak() {
            synchronized (Person.class) {
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + " speak hello world! " + TimeUtil.getCurrentTime());
            }
        }

    改动speak函数,将里面的的代码用 synchronized (Person.class) 修饰,执行输出:
    A启动了 44:18
    B启动了 44:18
    threadA say hello world! 44:20
    threadB speak hello world! 44:22
    两个线程依旧互斥,因为还在竞争类锁。

    总结:类锁类似对象锁,唯一且同一时间只能由唯一线程持有。

    最后补充一点:类锁和对象锁是两套互斥机制,互不影响,具体看你的函数需求的是对象锁(哪个对象)还是类锁

    展开全文
  • 答案是A, synchronized关键字是同步代码块关键字, 对对象加互斥锁 详解: synchronized: 用来给对象和方法或者代码块加锁. 当它锁定一个方法或者一个代码块的时候, 同一时刻最多只有一个线程执行这个段代码 volatile:...

    A. synchronized
    B. volatile
    C. serialize
    D. static

    答案是A, synchronized关键字是同步代码块关键字, 对对象加互斥锁

    详解:

    synchronized:

    用来给对象方法或者代码块加锁. 当它锁定一个方法或者一个代码块的时候, 同一时刻最多只有一个线程执行这个段代码

    volatile:

    用来确保将变量的更新操作通知到其他线程, 当把变量声明为volatile类型后, 编译器与运行时都会注意到这个变量是共享的, 因此不会将该变量上的操作与其他内存操作一起重排序. 然而, 在访问volatile变量时, 不会执行加锁操作, 因此也就不会使执行线程阻塞, 因此, volatile变量是一种比synchronized关键字更轻量级的同步机制

    serialize:

    Java对象序列化为二进制文件

    static:

    修饰变量, 方法, 静态代码块
    静态变量:

    1. 由static修饰的变量称为静态变量

    2. 静态变量属于类, 而不属于某个类

    3. 静态变量的副本只有一个

    静态方法:

    1. 在静态方法中只能调用静态变量和静态方法
    2. 在非晶态方法中, 可以调用静态方法或者变量
    3. 在静态方法中, 可以调用静态方法或者变量
    4. 在非静态方法中, 可以调用静态方法或者变量
    5. 在静态方法中不能使用this和super关键字

    静态代码块:

    1. 用来给静态成员变量初始化
    展开全文
  • Linux互斥锁的使用

    2020-08-28 23:12:09
    如上图所示:如果多个线程要访问一段共享资源的内存区域时,其中一个线程(如图中线程1)首先读取共享区域时,会在共享区域外设置一把互斥锁,其它线程阻塞在互斥锁处,线程1结束共享资源的访问后,会解锁该内存区域...

    目录

    1. 互斥锁原理

    2. 互斥锁相关函数

    3. 互斥锁使用过程

    4. 互斥锁使用示例

    5. 死锁


    1. 互斥锁原理

    如上图所示:如果多个线程要访问一段共享资源的内存区域时,其中一个线程(如图中线程1)首先读取共享区域时,会在共享区域外设置一把互斥锁,其它线程阻塞在互斥锁处,线程1结束共享资源的访问后,会解锁该内存区域,此时其它的线程才可以继续访问共享资源的内存区域。本来多线程访问数据时是并行访问内存区域的,加上互斥锁后变为串行处理。

    多线程编程是建议使用互斥锁,这样可以对公共区域的数据进行保护。互斥锁的缺点就是串行,数据访问的效率会有一定的降低。

    2. 互斥锁相关函数

    1.创建互斥锁函数
    pthread_mutex_t mutex;
    
    2.初始化互斥锁函数:
    int pthread_mutex_init(pthread_mutex_t *restrict mutex, 
                           const pthread_mutexattr_t *restrict attr
                            ); 
        参数1:传出参数,调用是创建的互斥锁变量。
        参数2:传入参数,互斥锁属性,一般为NULL表示为默认属性
        这里的restrict关键字,表示指针指向的内容只能通过这个指针进行修改
        restrict关键字作用可以理解为:
    	    用来限定指针变量。被该关键字限定的指针变量所指向的内存操作,必须由本指针完成。
        
        初始化可以使用静态初始化和动态初始化,示例:
        pthread_mutex_t mutex;
    		1. pthread_mutex_init(&mutex, NULL);   			动态初始化。
    		2. pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;	静态初始化。
    
    3.加锁
    int pthread_mutex_lock(pthread_mutex_t *mutex);
         参数:mutex
         没有上锁,当前线程会将这把锁上锁
         已经上锁,当前线程会阻塞在此
                  锁被打开之后,线程会解除阻塞
    
    4.尝试加锁
    int pthread_mutex_trylock(pthread_mutex_t *mutex);
          参数:mutex
          没有上锁:同上
          已经上锁,不阻塞返回
    
    5.解锁
    int pthread_mutex_unlock(pthread_mutex_t *mutex);
    
    6.销毁一个互斥锁
    int pthread_mutex_destroy(pthread_mutex_t *mutex);
    

    3. 互斥锁使用过程

    (1)创建互斥锁:pthread_mutex_t mutex;

    (2)初始化互斥锁: pthread_mutex_init(&mutex, NULL);

    (3)找到线程共同操作的共享资源

               加锁:prhread_mutex_lock(&mutex);     //如果共享区域已经上锁,会阻塞线程

                          pthread_mutex_trylock(&mutex);   //如果共享区域已经上锁,直接返回,不会阻塞。

    (4)访问内存区域

    (5)解锁:pthread_mutex_unlock(&mutex);

    (6)销毁:pthread_mutex_destory(&mutex);

     使用注意:

    尽量保证锁的粒度,越小越好,一般访问共享数据前加锁,访问结束立即解锁

            互斥锁,本质是结构体。 我们可以看成整数。 初值为 1。(pthread_mutex_init() 函数调用成功。)

            加锁: --操作, 阻塞线程。

            解锁: ++操作, 唤醒阻塞在锁上的线程。

            try锁:尝试加锁,成功--。失败,返回。同时设置错误号 EBUSY

    4. 互斥锁使用示例

    pthread_mutex.c:主线程建立两个子线程对全局变量进行++操作。

    #include <stdio.h>
    #include <unistd.h>
    #include <stdlib.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <string.h>
    #include <pthread.h>
    
    
    #define MAX 10000
    // 全局变量
    int number;
    //创建一把互斥锁
    pthread_mutex_t mutex;
    
    
    // 线程A处理函数
    void* funcA_num(void* arg)
    {
        for(int i=0; i<MAX; ++i)
        {
            //访问全局变量之前加锁
            //如果mutex被锁上了,代码阻塞在当前位置
            pthread_mutex_lock(&mutex);
            int cur = number;
            cur++;
            number = cur;
            printf("Thread A, id = %lu, number = %d\n", pthread_self(), number);
            //解锁
            pthread_mutex_unlock(&mutex);
            usleep(10);
        }
     
        return NULL;
    }
    // 线程B处理函数 
    void* funcB_num(void* arg)
    {
        for(int i=0; i<MAX; ++i)
        {
            pthread_mutex_lock(&mutex);
            int cur = number;
            cur++;
            number = cur;
            printf("Thread B, id = %lu, number = %d\n", pthread_self(), number);
                    pthread_mutex_unlock(&mutex);
            usleep(10);
        }
     
        return NULL;
    }
    
    /*主函数创建两个线程,分别操作共享区域内存:全局变量number*/
    int main(int argc, const char* argv[])
    {
        pthread_t p1, p2;
            //初始化互斥锁
        pthread_mutex_init(&mutex,NULL);
     
        // 创建两个子线程
        pthread_create(&p1, NULL, funcA_num, NULL);
        pthread_create(&p2, NULL, funcB_num, NULL);
     
        // 阻塞,资源回收
        pthread_join(p1, NULL);
        pthread_join(p2, NULL);
     
            //释放互斥锁资源
        pthread_mutex_destroy(&mutex);
     
        return 0;
    }

    运行结果:AB两个线程逐个访问共享内存区域。

    5. 死锁

    程序运行是要避免死锁的情况。通常出现死锁的情况有两种:
           1.  对一个锁反复lock:对一个地方使用了两次锁,没有及时解锁

    2. 两个线程,各自持有一把锁,请求另一把锁,如下图所示:A,B两个线程访问AB两个共享数据区域时。

    解决方法:

            1. 让线程按照一定的顺序去访问共享资源

            2. 在访问的其他锁的时候,需要先将字节的锁解开

            3.trylock方式定义锁

    展开全文
  • 哪个关键字可以对对象加互斥锁?() A synchronized B volatile C serialize D static synchronized的4种用法 1.方法声明时使用,放在范围操作符(public等)之后,返回类型声明(void等)之前.这时,线程获得的...

    哪个关键字可以对对象加互斥锁?()

    A synchronized
    B volatile
    C serialize
    D static

    synchronized的4种用法

    1.方法声明时使用,放在范围操作符(public等)之后,返回类型声明(void等)之前.这时,线程获得的是成员锁,即一次只能有一个线程进入该方法,其他线程要想在此时调用该方法,只能排队等候,当前线程(就是在synchronized方法内部的线程)执行完该方法后,别的线程才能进入.

      public synchronized void synMethod() {
            //方法体
          }

    2.对某一代码块使用,synchronized后跟括号,括号里是变量,这样,一次只有一个线程进入该代码块.此时,线程获得的是成员锁.例如:

     public int synMethod(int a1){
            synchronized(a1) {
              //一次只能有一个线程进入
            }
          }

        3.synchronized后面括号里是一对象,此时,线程获得的是对象锁.例如:

    public class MyThread implements Runnable {
        public static void main(String args[]) {
        MyThread mt = new MyThread();
        Thread t1 = new Thread(mt, "t1");
        Thread t2 = new Thread(mt, "t2");
        Thread t3 = new Thread(mt, "t3");
        Thread t4 = new Thread(mt, "t4");
        Thread t5 = new Thread(mt, "t5");
        Thread t6 = new Thread(mt, "t6");
        t1.start();
        t2.start();
        t3.start();
        t4.start();
        t5.start();
        t6.start();
      }
    
      public void run() {
        synchronized (this) {
          System.out.println(Thread.currentThread().getName());
        }
      }
    } 

    对于3,如果线程进入,则得到当前对象锁,那么别的线程在该类所有对象上的任何操作都不能进行.在对象级使用锁通常是一种比较粗糙的方法。为什么要将整个对象都上锁,而不允许其他线程短暂地使用对象中其他同步方法来访问共享资源?如果一个对象拥有多个资源,就不需要只为了让一个线程使用其中一部分资源,就将所有线程都锁在外面。由于每个对象都有锁,可以如下所示使用虚拟对象来上锁:

    class FineGrainLock {
    
       MyMemberClass x, y;
       Object xlock = new Object(), ylock = new Object();
    
       public void foo() {
          synchronized(xlock) {
             //access x here
          }
    
          //do something here - but don't use shared resources
    
          synchronized(ylock) {
             //access y here
          }
       }
    
       public void bar() {
          synchronized(this) {
             //access both x and y here
          }
          //do something here - but don't use shared resources
       }
      }
    

    4.synchronized后面括号里是类,此时,线程获得的是对象锁.例如:

    class ArrayWithLockOrder{
      private static long num_locks = 0;
      private long lock_order;
      private int[] arr;
    
      public ArrayWithLockOrder(int[] a)
      {
        arr = a;
        synchronized(ArrayWithLockOrder.class) {//-----这里
          num_locks++;             // 锁数加 1。
    
          lock_order = num_locks;  // 为此对象实例设置唯一的 lock_order。
        }
      }
      public long lockOrder()
      {
        return lock_order;
      }
      public int[] array()
      {
        return arr;
      }
      }
    
      class SomeClass implements Runnable
     {
      public int sumArrays(ArrayWithLockOrder a1,
                           ArrayWithLockOrder a2)
      {
        int value = 0;
        ArrayWithLockOrder first = a1;       // 保留数组引用的一个
        ArrayWithLockOrder last = a2;        // 本地副本。
        int size = a1.array().length;
        if (size == a2.array().length)
        {
          if (a1.lockOrder() > a2.lockOrder())  // 确定并设置对象的锁定
          {                                     // 顺序。
            first = a2;
            last = a1;
          }
          synchronized(first) {              // 按正确的顺序锁定对象。
            synchronized(last) {
              int[] arr1 = a1.array();
              int[] arr2 = a2.array();
              for (int i=0; i<size; i++)
                value += arr1[i] + arr2[i];
            }
          }
        }
        return value;
    
      }
      public void run() {
        //
      }
      }
    
    

    对于4,如果线程进入,则线程在该类中所有操作不能进行,包括静态变量和静态方法,实际上,对于含有静态方法和静态变量的代码块的同步,我们通常用4来加锁.

    Volatile原理

    Java语言提供了一种稍弱的同步机制,即volatile变量,用来确保将变量的更新操作通知到其他线程。当把变量声明为volatile类型后,编译器与运行时都会注意到这个变量是共享的,因此不会将该变量上的操作与其他内存操作一起重排序。volatile变量不会被缓存在寄存器或者对其他处理器不可见的地方,因此在读取volatile类型的变量时总会返回最新写入的值。

      在访问volatile变量时不会执行加锁操作,因此也就不会使执行线程阻塞,因此volatile变量是一种比sychronized关键字更轻量级的同步机制。

    当对非 volatile 变量进行读写的时候,每个线程先从内存拷贝变量到CPU缓存中。如果计算机有多个CPU,每个线程可能在不同的CPU上被处理,这意味着每个线程可以拷贝到不同的 CPU cache 中。

      而声明变量是 volatile 的,JVM 保证了每次读变量都从内存中读,跳过 CPU cache 这一步。

    当一个变量定义为 volatile 之后,将具备两种特性:

      1.保证此变量对所有的线程的可见性,这里的“可见性”,如本文开头所述,当一个线程修改了这个变量的值,volatile 保证了新值能立即同步到主内存,以及每次使用前立即从主内存刷新。但普通变量做不到这点,普通变量的值在线程间传递均需要通过主内存来完成。

      2.禁止指令重排序优化。有volatile修饰的变量,赋值后多执行了一个“load addl $0x0, (%esp)”操作,这个操作相当于一个内存屏障(指令重排序时不能把后面的指令重排序到内存屏障之前的位置),只有一个CPU访问内存时,并不需要内存屏障;(什么是指令重排序:是指CPU采用了允许将多条指令不按程序规定的顺序分开发送给各相应电路单元处理)。

    对象的串行化(Serialization)

    一、串行化的概念和目的
    1.什么是串行化
                对象的寿命通常随着生成该对象的程序的终止而终止。有时候,可能需要将对象的状态保存下来,在需要时再将对象恢复。我们把对象的这种能记录自己的状态以便将来再生的能力。叫作对象的持续性(persistence)。对象通过写出描述自己状态的数值来记录自己 ,这个过程叫对象的串行化(Serialization) 。串行化的主要任务是写出对象实例变量的数值。如果交量是另一对象的引用,则引用的对象也要串行化。这个过程是递归的,串行化可能要涉及一个复杂树结构的单行化,包括原有对象、对象的对象、对象的对象的对象等等。对象所有权的层次结构称为图表(graph)。
    2.串行化的目的
                Java对象的单行化的目标是为Java的运行环境提供一组特性,如下所示:
    1)       尽量保持对象串行化的简单扼要 ,但要提供一种途径使其可根据开发者的要求进行扩展或定制。
    2)       串行化机制应严格遵守Java的对象模型 。对象的串行化状态中应该存有所有的关于种类的安全特性的信息。
    3)       对象的串行化机制应支持Java的对象持续性。
    4)       对象的串行化机制应有足够的 可扩展能力以支持对象的远程方法调用(RMI)。
    5)       对象串行化应允许对象定义自身 的格式即其自身的数据流表示形式,可外部化接口来完成这项功能。

    二、串行化方法 
                从JDK1.1开始,Java语言提供了对象串行化机制 ,在java.io包中,接口Serialization用来作为实现对象串行化的工具 ,只有实现了Serialization的类的对象才可以被串行化。
                Serializable接口中没有任何的方法。当一个类声明要实现Serializable接口时,只是表明该类参加串行化协议,而不需要实现任何特殊的方法。下面我们通过实例介绍如何对对象进行串行化。
    1.定义一个可串行化对象
                一个类,如果要使其对象可以被串行化,必须实现Serializable接口。我们定义一个类Student如下:

    
        import java.io.Serializable;   
          
        public class Student implements Serializable {   
          
            int id;// 学号   
          
            String name;// 姓名   
          
            int age;// 年龄   
          
            String department; // 系别   
          
            public Student(int id, String name, int age, String department) {   
          
                this.id = id;   
          
                this.name = name;   
          
                this.age = age;   
          
                this.department = department;   
          
            }   
          
        }  
    

    2.构造对象的输入/输出流
                要串行化一个对象,必须与一定的对象输出/输入流联系起来,通过对象输出流将对象状态保存下来,再通过对象输入流将对象状态恢复。
                java.io包中,提供了ObjectInputStream和ObjectOutputStream将数据流功能扩展至可读写对象 。在ObjectInputStream 中用readObject()方法可以直接读取一个对象,ObjectOutputStream中用writeObject()方法可以直接将对象保存到输出流中。

    
      
            FileInputStream fi = new FileInputStream("data.ser");   
      
            ObjectInputStream si = new ObjectInputStream(fi);   
      
            try {   
      
                stu = (Student) si.readObject();   
      
                si.close();   
      
            } catch (IOException e)   
      
            {   
                System.out.println(e);   
            }   
      
            System.out.println("Student Info:");   
      
            System.out.println("ID:" + stu.id);   
      
            System.out.println("Name:" + stu.name);   
      
            System.out.println("Age:" + stu.age);   
      
            System.out.println("Dep:" + stu.department);   
      
        }   
      
    }  

    运行结果

     Student Info: 
    
      ID:981036 
    
      Name:LiuMing 
    
      Age:18 
    
      Dep:CSD

     在这个例子中,我们首先定义了一个类Student,实现了Serializable接口 ,然后通过对象输出流的writeObject()方法将Student对象保存到文件 data.ser中 。之后,通过对家输入流的readObjcet()方法从文件data.ser中读出保存下来的Student对象 。从运行结果可以看到,通过串行化机制,可以正确地保存和恢复对象的状态。 

    接着我们看看别人的评论

    synchronized 关键字 : 用来给对象和方法或者代码块加锁,当它锁定一个方法或者一个代码块的时候,同一时刻最多只有一个线程执行这个段代码。

    volatile:用来确保将变量的跟新操作通知到其他线程,当把变量声明为volatile类型后,编译器与运行时都会注意到这个变量是共享的,因此不会将该变量上的操作与其他内存操作一起重排序。然而,在访问volatile变量时不会执行加锁操作,因此也就不会使执行线程阻塞,因此volatile变量是一种比 synchronized关键字更轻量级的同步机制。

    serialize:Java 对象序列化为二进制文件。

    static关键字: static关键字可以修饰变量,方法,静态代码块。

                              静态变量:

                                              由static修饰的变量称为静态变量

                                              静态变量属于类,而不属于某个对象

                                              静态变量它的副本只有一个(静态变量在类中只加载一)

                             静态方法:

                                              在静态方法中只能调用静态变量和静态方法

                                              在非静态方法中,可以调用静态方法或者变量。

                                              在静态方法中不能使用this和super关键字。

                            静态代码块

                                              作用:用来给静态成员变量初始化

    答案A

    文章仅个人理解,来在各大网站。如有不合理之处,欢迎吐槽。

    阅读目录(置顶)(长期更新计算机领域知识)https://blog.csdn.net/weixin_43392489/article/details/102380691

    阅读目录(置顶)(长期更新计算机领域知识)https://blog.csdn.net/weixin_43392489/article/details/102380882

    阅读目录(置顶)(长期科技领域知识)https://blog.csdn.net/weixin_43392489/article/details/102600114

    歌谣带你看java面试题 https://blog.csdn.net/weixin_43392489/article/details/102675944

    展开全文
  • 使用mutex(互斥量、互斥锁)一般步骤: pthread_mutex_init 函数 初始化一个互斥锁(互斥量) —> 初值可看作1 int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr...
  • 可重入锁(递归锁) & 互斥锁属性设置

    千次阅读 2018-10-10 19:21:51
    前言: 上一次刷博客的时候,看到了自旋锁,通过学习Linux内核,对自旋锁有了一定的了解。...最常见的进程/线程的同步方法有互斥锁(或称互斥量Mutex),读写锁(rdlock),条件变量(cond),信号量(Semophore)等。...
  • 互斥锁 synchronized分析

    千次阅读 2019-04-21 13:01:26
    互斥锁是为了保证同一个方法同时间只有一个线程去执行,这个也是在多线程开发当中最基本的实现。在java体系当中有很多方法可以实现目的,如: synchronized ,lock ,redis分布式锁,zk分布式锁,基于数据库实现悲观...
  • 针对多道程序开发过程中存在的互斥锁标准不统一、使用复杂、易造成死锁等不足,提出扩展C/C++标准语法,增加临界资源定义及操作关键字的方案,通过隐藏互斥锁的概念及实现从而降低开发复杂度、实现平台无关....
  • golang互斥锁和读写锁性能分析

    千次阅读 2019-04-05 21:42:20
    在并发操作中为了防止多任务同时修改共享资源导致的不确定结果,我们可能会用到互斥锁和读写锁。 一:互斥锁 1.互斥锁有两种操作,获取锁和释放锁 2.当有一个goroutine获取了互斥锁后,任何goroutine都不可以获取...
  • Java之加互斥锁

    2021-07-18 18:30:24
    哪个关键字可以对对象加互斥锁?(A) A synchronized B volatile C serialize D static synchronized的4种用法 方法声明时使用,放在范围操作符(public等之后),返回类型声明(void等)之前,这时,线程获得的是成员...
  • 互斥锁(mutex)

    万次阅读 多人点赞 2018-09-03 15:19:31
    Linux中提供一把互斥锁mutex(也称之为互斥量)。 每个线程在对资源操作前都尝试先加锁,成功加锁才能操作,操作结束解锁。 但通过“锁”就将资源的访问变成互斥操作,而后与时间有关的错误也不会再产生了。 ...
  • 如何解决线程切换带来的...答案是**保证多线程之间的互斥性。也就是说,在同一时刻只有一个线程在执行!**如果我们能够保证对共享变量的修改是互斥的,那么,无论是单核CPU还是多核CPU,都能保证多线程之间的原子性了。
  • linux之互斥锁

    2020-04-02 20:05:39
    Linux中提供一把互斥锁mutex(也称之为互斥量)。 每个线程在对资源操作前都尝试先加锁,成功加锁才能操作,操作结束解锁。 1、主要应用函数: pthread_mutex_init()函数 功能:初始化一个互斥锁 pthread_mutex_...
  • Qt 使用互斥锁

    千次阅读 2020-03-28 22:51:07
    这样每次只有一个线程可以访问它(这类似于Java synchronized关键字)。 通常最好将互斥对象与QMutexLocker一起使用,因为这样可以很容易地确保一致地执行锁定和解锁。 2、官方示例: QMutex mutex; int number = 6;...
  • Java中的“互斥锁”机制

    千次阅读 2019-01-09 22:48:24
    在Java多线程运行中为了解决共享资源时存在的多并发问题,采用“互斥锁”机制。 互斥锁:在访问共享资源之前对进行加锁操作,在访问完成之后进行解锁操作。用于保证不会出现某个线程总是竞争不过其它线程长时间不被...
  • 线程同步---互斥锁和读写锁

    千次阅读 2019-01-09 23:21:26
    静态初始化:如果互斥锁 mutex 是静态分配的(定义在全局,或加了static关键字修饰),可以直接使用宏进行初始化。 例如: pthead_mutex_t muetx = PTHREAD_MUTEX_INITIALIZER 动态初始化:局部变量应采用动态...
  • 互斥锁 / 读写锁 乐观锁 / 悲观锁 分段锁 偏向锁 / 轻量级锁 / 重量级锁 自旋锁 上面是很多锁的名词,这些分类并不是全是指锁的状态,有的指锁的特性,有的指锁的设计,下面总结的内容是对每个锁的名词进行一定的...
  • 首先对java中同步锁与互斥锁进行区分,主要来源于知乎中的大佬总结如下: 1.锁的概念 锁的目的就是避免多个线程对同一个共享的数据并发修改带来的数据混乱。 锁的实现要处理的大概就只有这4个问题:(知乎大宽宽...
  • 互斥锁在串口通信中的简单应用

    千次阅读 2018-08-03 10:31:28
    互斥锁(英文:Mutual exclusion,常缩写为Mutex)是一种常用在多线程编程中,防止多个线程对一个公共资源做读写操作的机制,以保证共享操作的数据的完整性。互斥锁是最基本的进程或者线程间同步的方法,用来保护临界...
  • 互斥量(互斥锁)

    万次阅读 2018-10-26 15:32:27
    Linux提供一把互斥锁mutex(也称之为互斥量) 每个线程在对资源操作前都尝试先加锁,成功加锁才能操作,操作结束后解锁。 资源还是共享的,线程间也还是竞争的,但通过锁将资源的访问变为互斥操作,而后与时间有关的...
  • 线程安全 线程同步与互斥 ...互斥锁(量) 互斥锁接口 可重入函数&线程安全 死锁 条件变量 条件变量接口 条件变量使用规范 为什么pthread_cond_wait()中要传入互斥锁? 为什么互斥锁和条件变量要配合使用?
  • 虽然加锁能够保证执行临界区代码的互斥性,并能够解决并发问题,但是也不会随便用一把锁就会有效,所以必须深入分析锁定的对象和受保护资源的关系,综合考虑受保护资源的访问路径,多方面考量才能用好互斥锁。...
  • 互斥锁:线程会从sleep(加锁)——>running(解锁),过程中有上下文的切换(主动出让时间片,线程休眠,等待下一次唤醒),cpu的抢占,信号的发送等开销。 自旋锁:线程一直是running(加锁——>解锁),死循环(忙...
  • 互斥锁、独占锁、内置锁:并没有“同步锁”这个名词,Java的synchronized正确的叫法应该是“互斥锁”,“独占锁”或者“内置锁”。但有的人“顾名思义”叫它同步锁。 下面我们简单的来扩展一下。比如下面的程序:...
  • synchronized简单互斥锁

    2021-07-22 15:15:49
    //Synchronized这个关键字加上之后两个线程不能同时执行,只能一个线程执行之后下一个线程执行 public static void main(String[] args) { SynchronizedDemo thread=new SynchronizedDemo(); Runnable runnable=...
  • 自旋锁与互斥锁有点类似,只是自旋锁不会引起调用者睡眠,如果自旋锁已经被别的执行单元保持,调用者就一直循环在那里看是 否该自旋锁的保持者已经释放了锁,"自旋"一词就是因此而得名。其作用是为了解决某项资源的...
  • 互斥锁避免死锁实践

    2019-04-23 16:29:42
    有线程T1 和线程T2 ,T1占用资源A也就是住了A对象synchronized(A),T2占用资源B也就是住了资源B对象syncrhonized。此时T1线程在去申请获取B对象的时候,T2线程还没有释放资源。T1线程就会一直等待资源B,并且也...
  • 在上篇中提到,受保护资源和之间合理的关联关系应该是N:1的关系,也就是说一把可以用来保护多个资源,但是不能用多把来保护一个资源。 当我们要保护多个资源时,首先要区分这些资源是否存在关联关系。 保护...
  • 线程同步能够保证多个线程安全访问竞争资源,最简单的同步机制是引入互斥锁互斥锁 互斥锁为资源引入一个状态:锁定/非锁定 某个线程要更改共享数据时,先将其锁定,此时资源的状态为“锁定”,其他线程不能更改;...
  • Qt多线程:使用互斥锁

    万次阅读 多人点赞 2018-09-04 22:08:06
    这样每次只有一个线程可以访问它(这类似于Java synchronized关键字)。 通常最好将互斥对象与QMutexLocker一起使用,因为这样可以很容易地确保一致地执行锁定和解锁。 2、官方示例: QMutex mutex; ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 51,703
精华内容 20,681
关键字:

互斥锁关键字