Java网络编程学习笔记(5)——多线程(二)

Future, Callable, Executor(回调方法)

创建一个ExecutorService它会根据需要为你创建线程,可以向ExecutorService提交 Callable任务,调用call()方法
对于每个Callable任务,会分别得到一个Future,之后可以向Future请求得到任务的结果

    FindMaxTask task1 = new FindMaxTask(data, 0, data.length/2);
    FindMaxTask task2 = new FindMaxTask(data, data.length/2, data.length);

    ExecutorService service =  Executors.newFixedThreadPool(2);

    Future<Integer> future1 = service.submit(task1);
    Future<Integer> future2 = service.submit(task2);

    return Math.max(future1.get(), future2.get());

FindMaxTask里包含call()方法, 并且implements Callable接口

阻塞:
不过这里有一个重要区别:调用future1.get()时,这个方法会阻塞,等待第一个FindMaxTask完成
只有当第一个完成,才会调用future2.get(),也有可能第二个线程已经结束,在这种情况结果值会直接返回

Executor允许你用不同的策略将任务分配给不同的线程,
线程就像是图书馆的借阅者,它从一个中心资源池借阅,线程通过共享内存,文件,句柄,socket和其他资源使程序更高效

多线程的缺点是,如果两个线程同时访问同一个资源,其中一个必须等待另一个结束,如果其中一个没有等待那么资源就可能被破坏。

例如,多线程的run()方法中有多个System.out.print(),那么很有可能,输出并不是连续的
System.out.print穿插输出,可能A线程输出一句,B线程输出2句,A线程再输出
因为System.out是共享资源,所以System.out.println()需要独占访问

Synchronized同步块(同步方法)

它会对synchronized方法的对象同步。

synchronized(System.out){
    System.out.print(input + " ");
    System.out.println(DatatypeConverter.printHexBinary(digest));
}

(不会出现这种情况:1st线程输出”input: “, 还没输出后面内容,然后2rd线程也输出“input: “。显然这样就乱了)
一旦线程开始打印这些值,所有其他线程在打印它们的值之前就必须停止,需要等待这个线程结束

syschronized本质:
同步要求在同一个对象上,同步的所有代码,要连续地运行,并不能并行运行。
不过,对不同对象同步的代码或者根本不同步代码仍可以与这个代码并行运行。
Java没有提供任何方法来阻止其他线程使用共享资源,它只能防止对同一对象同步的其他线程使用这个共享资源。

这里写图片描述

线程安全的方法(非同步)

1.使用局部变量而不是字段
局部变量不存在同步问题,每次进入一个方法时,虚拟机将为这个方法,创造一组全新的局部变量,这些方法在外部不可见,而且结束时撤销,因此局部变量不可能由两个不同的进程共享。

基本类型的方法参数也可以再单独线程中安全的修改,因为Java是按值而不是按引用来传递参数(这里指基本类型).
对象类型的方法参数有点麻烦,因为按值传递的实际参数是对象的引用,String参数是安全的,因为它们不可变。(一旦创建String对象,它就不能被任何线程修改)不可变对象永远也不会改变状态。StringBuffer参数是不可变的。

线程安全:没有线程有这个对象的引用,基本上线程安全。

2.在自己的类中利用不可变性,使一个类做到线程安全
这往往是最简单的方法,只要将所有字段声明为private(私有),final(最终),而且编写任何可能改变他们的方法。

3.将非线程安全的类作为线程安全类的私有字段
这个例子就是Web服务器可能使用非同步的Logfile类,但是为每个单独的线程提供它自己单独的日志,这样各个线程之间就不会有共享资源了。

4.使用java.util.concurrent.atomic包的类
这里写图片描述
这里写图片描述

死锁

这里写图片描述
这里写图片描述
这里写图片描述
产生死锁的四个必要条件:
(1) 互斥条件:一个资源每次只能被一个进程使用。
(2) 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
(3) 不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺。
(4) 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。

猜你喜欢

转载自blog.csdn.net/qq_37423198/article/details/79678627