20200717 by 1z
91、举例说明同步和异步
同步: 发送消息之后需要等待反馈,然后再开始下一次发送信息。
异步: 发送一个请求,不等待返回,随时可以再次发送下一个请求。
同步和异步的区别: 同步需要等待,异步不需要等待。
例子;
电话是一个同步的例子,需要等待接收者接通电话才可以进行下一步行为
广播是一个异步的例子,不需要等待接收者的反馈信息,可以连续发送信息
- 请介绍一下线程同步 和 线程调度的相关方法
(1)wait():使一个线程处于等待(阻塞)状态,并且释放所持有的对象的锁;
(2)sleep():使一个正在运行的线程处于睡眠状态,是一个静态方法,调用此方法要处理InterruptedException异常; //sleep(int times)
(3)notify():唤醒一个处于等待状态的线程,当然在调用此方法的时候,并不能确切的唤醒某一个等待状态的线程,而是由JVM确定唤醒哪个线程,而且与优先级无关;[随机唤醒]
(4)notityAll():唤醒所有处于等待状态的线程,该方法并不是将对象的锁给所有线程,而是让它们竞争,只有获得锁的线程才能进入就绪状态;
(5)通过Lock接口提供了显式的锁机制(explicit lock),增强了灵活性以及对线程的协调。Lock接口中定义了加锁(lock())和解锁(unlock())的方法,同时还提供了newCondition()方法来产生用于线程之间通信的Condition对象【Condition 可以实现精确唤醒(为每一个线程 分配一个condition 用来唤醒)】
此外,Java 5还提供了信号量机制(semaphore),信号量可以用来限制对某个共享资源进行访问的线程的数量。在对资源进行访问之前,线程必须得到信号量的许可(调用Semaphore对象的acquire()方法);在完成对资源的访问后,线程必须向信号量归还许可(调用Semaphore对象的release()方法)。
- 当一个线程进入一个对象的synchronized方法A之后,其他线程是否可进入此对象的的synchronized的方法B?
不可以,当一个线程访问对象的synchronized方法后,该对象的对象锁已经被获取了,如果需要再次访问阻塞方法B的时候,需要等待释放
ps:
如果当synchronized锁定方法A的时候,则其他线程可以获取方法B对应的锁,从而进入方法B
如果当synchronized锁定的是对象,则方法A 和 B需要共用一把锁(对象锁 ACC_SYNCHRONIZED),,不能进入其他方法
- 请简述一下线程的sleep() 方法 和 yield()方法有什么区别?
* sleep()方法给其他线程机会时不考虑线程的优先级,因此会给低优先级线程以运行的机会,yield()方法只会给相同优先级或者更高优先级以运行的机会
* 线程使用sleep()后转入阻塞状态,执行yield()方法后转入就绪状态(自己让出cpu之后,再次参与抢夺之中)
* sleep()可以设置延时的时间,yield()使用后,直接让出cpu资源
* sleep()声明抛出InterruptedException异常,而yield()方法没有任何声明异常
* sleep() 比 yield() 具有更好的可移植性
- 请回答以下几个问题:
95-1:java中有几种方法可以实现一个线程?
有三种方式可以实现一个线程
* 实现Runnable方法,简单,但是传参不方便
* 继承Thread重写run: 传递参数方便,但是java不支持多继承
* 使用FutureTask接口: 可以拿到任务的返回值 通过 实例化对象的get方法
coding
//1.继承Thread类 实现run方法
public static class MyThread extends Thread{
@Override
public void run() {
System.out.println("I am a child thread by extending Thread");
}
}
//2.使用Runnable接口实现线程创建
public static class RunnableTask implements Runnable{
@Override
public void run() {
System.out.println("I am a child thread by implementing Runnable ");
}
}
//3.!!使用FutureTask
public static class CallerTask implements Callable<String>{
@Override
public String call() throws Exception {
System.out.println("I am a child thread by implementing Callable");
//调用线程可以有返回值传递
return "hello";
}
}
public static class CallerTask2 implements Callable<String>{
@Override
public String call() throws Exception {
System.out.println("I am a child thread2 by implementing Callable");
return "CallerTask2Msg";
}
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
MyThread thread=new MyThread();
//直到调用start之后才算真正启动了线程,调用start后,线程进入了就绪态(已经获取了除CPU资源外的其他资源,获取CPU资源之后才会真正处于运行状态,在run结束后处于终止态)
thread.start();
System.out.println("----------------------------------------");
RunnableTask task=new RunnableTask();
new Thread(task).start();
new Thread(task).start();
FutureTask<String>futureTask=new FutureTask<>(new CallerTask());
//在futureTask中有一个get方法 可以获取返回值
System.out.println("------------------------------------------");
new Thread(futureTask).start();
//现在线程已经启动 开始进行返回值获取
try {
String result=futureTask.get();
System.out.println(result);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
FutureTask<String> futureTask2=new FutureTask<String>(new CallerTask2());
new Thread(futureTask2).start();
//进行信息的反馈
String result2 = futureTask2.get();
System.out.println(result2);
}
}
95-2:用什么关键词修饰同步方法?
synchronized
95-3:stop()和suspend()方法为什么不推荐使用,请说明原因?
stop和suspend都有一些共同的点:都试图专横的控制一个给定了的线程的行为.
1.stop这个方法将终止所有未结束的方法,包括run方法。当一个线程停止时候,他会立即释放所有他锁住对象上的锁。这会导致对象处于不一致的状态。(当需要去停止一个线程的时候,它无法知道何时调用stop是安全的)
2.suspend不会破坏对象。但是,如果你用一个suspend挂起一个有锁的线程,那么在锁恢复之前将不会被释放。如果调用suspend的方法线程试图取得相同的锁,程序就会死锁。
- 请分别说明一下多线程和同步有几种实现方法,并且这些实现方法的具体内容都是什么?
见95-1
有三种方式可以实现多线程
* 实现Runnable方法,简单,但是传参不方便
* 继承Thread重写run: 传递参数方便,但是java不支持多继承
* 使用FutureTask接口: 可以拿到任务的返回值 通过 实例化对象的get方法
同步五种方式
* 同步方法
* 同步代码块
* 使用volatile实现线程同步
* 使用重入锁实现线程同步
* 使用局部变量实现线程同步
-----------------------------------------------------------------------------------------
coding
//1.继承Thread类 实现run方法
public static class MyThread extends Thread{
@Override
public void run() {
System.out.println("I am a child thread by extending Thread");
}
}
//2.使用Runnable接口实现线程创建
public static class RunnableTask implements Runnable{
@Override
public void run() {
System.out.println("I am a child thread by implementing Runnable ");
}
}
//3.!!使用FutureTask
public static class CallerTask implements Callable<String>{
@Override
public String call() throws Exception {
System.out.println("I am a child thread by implementing Callable");
//调用线程可以有返回值传递
return "hello";
}
}
public static class CallerTask2 implements Callable<String>{
@Override
public String call() throws Exception {
System.out.println("I am a child thread2 by implementing Callable");
return "CallerTask2Msg";
}
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
MyThread thread=new MyThread();
//直到调用start之后才算真正启动了线程,调用start后,线程进入了就绪态(已经获取了除CPU资源外的其他资源,获取CPU资源之后才会真正处于运行状态,在run结束后处于终止态)
thread.start();
System.out.println("----------------------------------------");
RunnableTask task=new RunnableTask();
new Thread(task).start();
new Thread(task).start();
FutureTask<String>futureTask=new FutureTask<>(new CallerTask());
//在futureTask中有一个get方法 可以获取返回值
System.out.println("------------------------------------------");
new Thread(futureTask).start();
//现在线程已经启动 开始进行返回值获取
try {
String result=futureTask.get();
System.out.println(result);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
FutureTask<String> futureTask2=new FutureTask<String>(new CallerTask2());
new Thread(futureTask2).start();
//进行信息的反馈
String result2 = futureTask2.get();
System.out.println(result2);
}
}
- 请说明你所知道的线程同步的方法
* 同步方法(synchronized 修饰方法)
* 同步代码块 (synchronized 修饰代码块)
* 使用volatile实现线程同步(使用volatile 修饰变量)
* 使用重入锁实现线程同步(ReentractLock)
* 使用局部变量实现线程同步(ThreadLocal)
- 启动一个线程是用run() 还是start()?
启动一个线程调用start(),使得线程处于就绪状态,之后可以被调度称为运行状态
run()是线程执行的代码
- 请使内部类实现线程设计四个线程,其中两个线程每次对j 增加1,其他两个线程对j减少1
public class ManyThread {
// 采用 Runnable 接口方式创建的多条线程可以共享实例属性
private int i;
// 同步增加方法
private synchronized void inc() {
i++;
System.out.println(Thread.currentThread().getName() + "--inc--" + i);
}
// 同步减算方法
private synchronized void dec() {
i--;
System.out.println(Thread.currentThread().getName() + "--dec--" + i);
}
// 增加线程 注意是内部类,且是非静态的
class Inc implements Runnable {
public void run() {
int i = 0;
while (i++ < 100) {
inc();
}
}
}
// 减算线程 注意是内部类,且是非静态的
class Dec extends Thread {
public void run() {
int i = 0;
while (i++ < 100) {
dec();
}
}
}
public static void main(String[] args) {
// 由于内部类是非静态的,所以这样需要Test的实例化才能调用生成内部类实例
ManyThread t = new ManyThread();
// 内部类的实例化
Inc inc = t.new Inc(); //
// Dec dec = t. new Dec();
Thread thread = null;
// 创建 2 个增加线程
for (int i = 0; i < 2; i++) {
thread = new Thread(inc); // 实现Runnable的类的实例化,使用带参数的Thread构造方法.
thread.start();
}
// 创建 2 个减少线程
for (int i = 0; i < 2; i++) {
thread = t.new Dec(); // 继承Thread的类可以直接实例化.
thread.start();
}
}
}
- 请说明线程中同步和异步有何异同?请举例说明什么情况下会使用到同步和异步?
同步: 发送消息之后需要等待反馈,然后再开始下一次发送信息。
异步: 发送一个请求,不等待返回,随时可以再次发送下一个请求。
同步可以避免脏数据的产生,假设是共享文件情境下,a和b都有着修改权限的能力。为了防止一个人读取到另外一个人删除的内容,采用同步的方式。进行按顺序操作。
异步可以提高效率,假设服务器主线程中需要启动一个子线程完成一些耗时操作,此时使用异步操作(客户端通过ajax发送异步请求),客户端无需等待直接运行,等到异步操作完成后反馈一条信息,客户端再进行回显即可。