多线程编程核心技术:
1).4页一个进程正在运行时至少会有1个线程在运行
2)4页实现多线程有两种方式:一种继承Thread类,另一种实现Runnable接口
继承Thread类局限:不支持多继承,不能共享
3)7页start方法创建顺序不代表线程启动的顺序
4)9页
Thread.java类也实现了Runnable接口,那也就意味意味着构造函数Thread(Runnable target)不光可以传入Runnable接口的对象,还可以传入一个Thread类的对象,这样做完全可以将一个Thread对象中的run()方法交由其他线程进行调用。
5)8页实例变量与线程安全
不共享数据的情况:
MyThread a = new MyTread("A");
MyThread b = new MyTread("B");
每个线程都有各自的变量值
6)13页synchronized可以在任何对象及方法上加锁,加锁的代码称为"互斥区"或"临界区"
术语“非线程安全”:多个线程对同一对象中的同一个实例变量进行操作时出现的值被更改、值不同步的情况。
6)13页对共享变量进行操作,注意这里面不同的Thread对象本来不共享,因为变量前使用了static才共享的。
7)15页System.out.println(i--);,println()方法在内部是同步的,但是i--是在进入println()之前发生的。
8)17页currentThread()方法可返回代码段正在被哪个线程调用的信息
9)18页isAlive()方法判断当前线程是否处于活动状态
10)20页sleep()方法是在指定的毫秒数内让当前正在执行的线程休眠。
课本23页:调用interrupt()方法只是在当前线程中打了一个停止标记,并不是真正的停止线程。
11)28页通过break来停止线程,退出循环,但是循环后面的语句还是会执行
12)28页为什么trycatch语句写在循环中,此时无法停止进程。
13)29页通过异常来停止线程
14)42页yield()方法放弃当前的CPU资源,将它让给其他任务去占用CPU执行时间,放弃的时间不确定。
15)守护线程50页,守护线程依赖于非守护线程
16)interrupted和inInterrupted两者的区别,对它们的调用还不清楚。静态方法中没法使用this
17)课本53页,“非线程安全”问题存在于“实例变量”中,如果是方法内部的私有变量,则不存在“非线程安全”问题。
17)课本57页两个线程访问同一个对象中的非同步方法,线程不安全,但如果访问方法里面的变量,那么不存在线程安全问题。
17)课本61页共享资源的访问才需要同步化,如果不是共享资源,根本没有同步的必要。
17)课本62页A线程持有object对象的Lock锁,B线程可以以异步的方式调用object对象的非synchronized类型的方法
A线程持有object对象的Lock锁,B线程如果在这时调用object对象的synchronized类型的方法则需要等待,也就是同步。
18)课本63页脏读,赋值进行了同步,但是取值的时候没有同步(没有加synchronized)
18)课本65页锁重入:当一个线程得到一个对象锁后,再次请求此对象锁时是可以再次得到该对象锁的,
18)课本68页出现异常,锁自动释放。线程a中出现异常,a的锁会自动释放。
18)子类可以继承父类成员变量的值
class Person
{
private int i = 100;
public void getI()
{
System.out.println(i);
}
}
class Student extends Person
{
}
class HighConcurrency
{
public static void main(String[] args)
{
Student s1 = new Student();
s1.getI();
}
}
18)课本71页,synchronized声明方法在某些情况下有弊端:有些函数执行时间过长,这时候可以使用同步代码块代替
18)课本80页,和synchronzed方法一样,synchronized(this)代码块也是锁定当前对象。
18)课本82页Java支持“任意对象”作为“对象监视器”来实现同步的功能。
18)课本84页锁非this对象优点:如果一个类中有很多个synchronized方法,这时虽然能实现同步,但会受到阻塞,影响运行效率,但如果使用同步代码块锁非this对象,则synchronized(非this)代码块中的程序与同步方法是异步的,不与其他锁this同步方法争抢this锁,可以大大提高效率。
19)97页synchronized关键字加到static静态方法上是给Class类上锁,而synchronized关键字加到非static方法上是给对象上锁。
19)100页synchronized(class)代码块的作用和synchronized static方法的作用一样。
19)102页JVM具有String常量池缓存的功能,String a = "a";String b = "a";a和b都是指向字符串"a"。
19)109页死锁:在程序设计的时候避免双方互相持有对方锁的情况。
19)课本123页,关键字volatile和关键字synchronized的区别:
volatile能保证数据的可见性,但不能保证原子性,而synchronized可以保证原子性,也可以间接保证可见性。
voatile解决的是变量在多个线程之间的可见性,而synchronized关键字解决的是多个线程之间访问资源的同步性。
第三章线程间通信
1)课本135页等待/通知机制
wait说明两点:wait只能在同步代码块或同步方法中执行,执行wait方法后,当前线程释放。
notiry说明两点:notify只能在同步代码块或同步方法中执行,在执行notify后,当前线程不会立马释放该对象锁,要等到执行notiry方法的线程执行完。
1)133页使用轮询机制检测某一个条件,这样浪费CPU资源
2)在调用wait()方法前,线程必须获取该对象的对象级别的锁,即只能在同步方法或同步块中调用wait()方法。
141页wait方法:wait方法可以是处于临界区的线程进入等待状态,同时释放被同步对象的锁。
notify方法:可以唤醒一个因调用了wait操作而处于阻塞状态中的线程,使其进入就绪状态。
notifyAll方法:使所有在等待队列中等待同一共享资源的所有线程从等待状态退出,进入可运行状态,此时优先级最高的那个线程最先执行,但也有可能随机执行,取决于jvm的实现。
143页:每个锁对象都有两个队列,一个是就绪队列,一个是阻塞队列,就绪队列存储了将要获得锁的线程,阻塞对垒存储了被阻塞的线程。一个线程被唤醒后,才会进入就绪队列,等待CPU调度,反之,一个线程被wait后,就会进入阻塞队列,等待下一次被唤醒。
144页notify()方法被执行后,不释放锁,要等所在的同步sychronized代码块后才释放锁。
146页:当线程呈wait()状态时,调用线程对象interrupt()方法会出现InterrupedException异常
148页:调用notify()方法一次只随机通知一个线程进行唤醒???????是随机唤醒一个线程还是唤醒最先wait()的线程。
150页:wait(long):等待某一时间是否有线程对锁进行唤醒,如果超过这个时间自动唤醒。
152页:是同一把锁码吗?思考清除,这里继承的Runnable接口,里面共享的,所以lock是共享的*****。
158页:等待/通知模式最经典就是“生产/消费者”模式
160页:多个生产者,多个消费者,注意点一:while(true)反复执行,二:while(flag)通过设置标识位。
163页:多个生产者多个消费者的情况下,可能会出现假死的情况,这是因为生产者可能唤醒了生产者,消费者唤醒了消费者
164页:操作栈,一生产一消费的问题,这里没有使用标志位,而是使用栈的大小来达到一生产一消费。
168页:操作栈,一生产多消费的问题,引入了栈,从栈空存取,可能出现栈在空的时候,唤醒另一个消费者去消费栈的情况
169页:操作栈,多生产一消费和多生产多消费与上面的一生产多消费问题一样。
181页:方法join的作用使所属的线程对象x正常执行run()方法中的任务,而使当前线程z进入无限期的阻塞,等待线程x销毁后再继续执行线程z的代码。
182页:方法join()和interrupt()方法如果彼此遇到,则会出现异常。
184页:join(long)和Thead.sleep(long)的对比
执行join(long)方法,当前线程的锁被释放,那么其他线程就可以调用此线程中的同步方法。
而Thread.sleep(long)方法却不释放锁。
185页:
191页:类ThreadLocal的使用:实现每个线程都有自己的共享变量.
193页:验证了线程变量的隔离性。
195页:使得ThreadLocal变量有自己的初始值——不为null,继承Thread类,重写里面的initValue()方法。
195页:再次验证线程变量的隔离性,这里注意只有get方法会导致其初试话,原因未明。
201页:ReentrantLock类在扩展功能更强大,具有嗅探锁定、多路分支通知等功能,使用上比synchronized更加灵活。
ReentrantLock对象的lock()方法获取锁,调用unlock()方法释放锁。
204页使用notify/notifyAll方法进行通知时,被通知的线程是由JVM随机选择的?????。但那是使用ReentrantLock结合Condtion类可以实现前面介绍的"选择性通知"—消费者线程只通知生产者线程,生产者线程只通知消费者线程。
二次整理:
206页condition.await()方法调用前要调用lock.lock()方法获取同步监视器。
这里通过线程调用类中的静态方法,使用synchronized同步方法是一种办法,另一种方式是使用同步代码块,这时使用的类.class,返回的是一个class对象。
208页Object类中的wait()方法相当于Condition类中的await()方法
Object类中的wait(long timeout)方法相当于Condition类中的await()方法
Object类中的notify()方法相当于Condition类中的signal()方法
Object类中的notifyAll()方法相当于Condition类中的signalAll()方法。
说明synchronized的对象和wait,notify的对象是相同的。
212页可以使用多个Condition实现通知部分线程的功能。
216页锁Lock分为"公平锁"和"非公平锁"
公平锁:加锁前检查是否有排队等待的线程,优先排队等待的线程,先来先得。
非公平锁:加锁时不考虑排序等待的问题,直接尝试获取锁,获取不到自动到队尾等待。
简单说:公平锁就是A持有锁,B先请求,在A释放锁后,B先获取锁。非公平锁就是,A持有锁,B请求不到挂起,A释放锁时,B将被唤醒,因此B会再次尝试获取这个锁。与此同时,如果线程C也请求这个锁,那么C很可能在B被完全唤醒之前获得锁。
课本215页强调交替打印,这个是在前面没有的,达到这样的目的采用的方法是用时标识位。
class LoginServlet
{
private static String usernameRef;
private static String passwordRef;
synchronized public static void doPost(String username,String password)
{
try
{
usernameRef = username;
if(username.equals("a"))
{
Thread.sleep(5000);
}
passwordRef = password;
System.out.println("username:"+usernameRef+" "+"password:"+passwordRef);
System.out.println();
}catch(InterruptedException e)
{
}
}
}
class ALogin extends Thread
{
public void run(){
//while(true)
LoginServlet.doPost("a", "aa"); //这是静态方法
}
}
class BLogin extends Thread
{
public void run()
{
//while(true)
LoginServlet.doPost("b","bb");
}
}
class HighConcurrency
{
public static void main(String[] args)
{
ALogin a = new ALogin();
a.start();
BLogin b = new BLogin();
b.start();
}
}
关于this,在Person类中为什么可以this,因为这时候对象已经产生了
class Person
{
String name;
int age;
Person(String name,int age)
{
this.name = name;
this.age = age;
}
}
class Test
{
public static void main()
{
Person p = new Person("xiaoming",2);
}
}