终于完成所有的lab了,在最后的这个lab里面接触到了多线程,甚感其高效也有感于其危险的地方,这里就大概记录一下,以免以后需跳坑。

1.Java多线程入门

1.1 Thread类与Runnable接口

要学习Java多线程,首当其冲的就是Thread类和基于这个类的Runnable接口,这里不多做解释,大家直接看代码就好。

/**
 *方法一:继承Thread类
 *
 *当你需要多线程启动的类里有大量的方法和Rep的时候推荐
 *这一种,因为抛开多线程的run来说这是一个完整的类,可
 *以创建实例,拥有自己的方法等等。
 */
//---------------------------------------------------
public class HelloThread extends Thread {
    public void run() {
        System.out.println("Hello from a thread!");
    }
    public static void main(String args[]) {
        HelloThread p = new HelloThread();
        p.start();
    }
//----------启动该线程的两个方式
    public static void main(String args[]) {
        (new HelloThread()).start();
    }
}

/**
 *方法二:完成Runnable接口
 *
 *当你的类需要继承其他类的时候,上面的方法就不好使了,
 *因为Java并不支持多继承,但是可以实现很多的接口,而
 *启动线程可以通过接入创建的Runnable接口来实现,就
 *避免了麻烦的产生。
 */
//---------------------------------------------------
public class HelloRunnable implements Runnable {
    public void run() {
        System.out.println("Hello from a thread!");
    }
    public static void main(String args[]) {
        (new Thread(new HelloRunnable())).start();
    }
}

/**
 *方法二:直接在创建Thread类时完成Runnable接口
 *
 *这个是最方便快捷的创建线程的方法,当你的线程只是简单
 *的代码片时,这种写法会简洁很多,减少了不少没必要的
 *工作。
 */
//--------------------------------------------------
new Thread(new Runnable() {
    public void run() {
        System.out.println("Hello");
    }
}).start();

1.2 Java多线程的简单原理剖析以及危险性预警

可能很多人会有疑问,多线程不就是开几个进程么,为啥会有安全性问题?
当你问出这样的问题的时候,你就已经错了,线程和进程是两个完全不相同的概念,进程是完整的一个程序,由操作系统调度分配CPU的玩意,而线程是一个进程里面创建的多个线程,这些线程是由进程来调度起作用的,虽然有多线程,但只有n个核,每个时刻只能执行n个线程,真正的多线程实现原理是通过进程调度,将CPU分成时间切片,安排不同的线程使用时间切片,来达到多线程的效果。可能会有人说,电脑里跑着上百个进程都没有问题,为啥几个小小的线程就会有问题了呢?这里不得不提一个重要的概念:“内存共享”,在进程里面是无法共享内存的,所有的进程都会分配到自己的一片独立的内存空间,大家各用个的,不会出现内存被不小心篡改的情况,但是多线程不一样,多线程会共享内存,考虑这样的一个情景:当两个线程都要访问某个变量的信息,并且将其修改,这对每一个线程来说,都感觉没有问题,但是两个线程都不知道最后这个结果是属于自己的,还是被修改过了,按照这样执行下去,必然会带来安全问题。

1.3 防范安全性问题

我个人觉得这种问题只可能被尽量防范,完全达到百分之百安全不太现实,因为多线程由于其不稳定性,本来就很难复现bug,别说debug了,但是多费费心思,最后的结果还是不错的。如何防范嘞,上面说到可能会有多个线程访同一时间问一个内存地址,这里的原因就是这个方法不是原子的,换句话说,这个方法可能会被执行到一半去执行其他的方法,这时候可以使用synchronized (obj) { ... }给其加“锁”,来保证这个方法在执行的时候,不会被使用同一把锁的代码片中断干扰,也就不会出现安全上面的问题了,下面就放上代码,演示如何加锁。同样的,在方法名前面加上synchronized也可以将这个方法变成一个原子方法(不可被同一类的其他方法打断),这里需要注意的是,我上面有提及“同一把锁”,这里的同一把锁在类里面给方法加锁指的是对这个类的所有方法来说用的是同一把锁,给代码片加锁指的锁是一个对象实例来的。

二. Lab6实验记录

这里就大概说一下我遇到的问题吧,就是在那个给ladder设置标记位的时候,可能会出现多个猴子访问一个ladder,一个已经上去了以后设置了标记位以后又被另外一个猴子在内存里面写入了标记位,这样就会导致有猴子对向而行的情况,很明显,这是在读取标记位和写入标记位的时候出现了竞争内存的情况,导致了不可预测的情况,这样的话我就用所有的桥作为一把锁将其整个操作代码片锁住,问题就解决了,这里就列举这一个最典型的问题吧,希望对大家有帮助~