关于多线程的几点总结
- 关于线程 synchronized关键字:
不能用在变量和构造函数上
放在方法上面,锁定的是对象,放在静态方法上锁定的是类
不应该锁定常量,比如String等类型因为程序中这个对象难免还会用
建议:
A:尽量使用同步代码块而不是同步方法。因为同步方法是锁定对象,这样无关安全线程的方法也无法执行了,只锁定代码块,其他线程调用非同步方法的函数一样还可以执行。
B:尽量锁定比较小的范围
C:选择锁的问题,尽量定义一个私有对象来锁定。该对象不可共有,因为公有可以被修改,无法保证锁的唯一,外部修改之后就可以任意执行同步块里的内容,尽量不要对外提供这个对象set方法,原因同上 - 关于wait()
该方法使当前线程等待,直到其锁的对象使用notify或者notifyAll
该方法必须拥有当前对象锁,为了保证这一点,该方法必须方法synchronized代码块当中
wait和sleep的区别:wait是进入等待并释放当前对象锁,sleep进入休眠不释放当前锁。 - 关于notify
唤醒当前对象锁的线程(如果有多个,则随机唤醒一个)
被唤醒的线程不会立即执行,而是等待当前线程释放了锁之后和其他线程进行一般性竞争获取锁
和wait方法一样,必须保证调用时已经获取对象锁,所以也要在synchronized代码块中 - 关于线程通信
既可以用synchronized,wait,notify等传统线程模型实现,也可以使用java提供的专门实现多线程的工具包来实现:java.util.concurrent.lock工具包下有Lock和Condition两个好用的工具哦
Lock相对于传统模型synchronized方法更加面向对象,因为这里锁,本身就是一个对象
Lock锁的话,执行完毕后,要显式的手动执行lock.unlock()来解锁,因为synchronized是JVM层面实现的,系统可以监控锁的释放与否,而Lock是代码层面实现的,所以尽量使用final子句来实现,保证不会遇到异常不能解锁。
代码参考示例:
Lock lock=new ReentrantLock(); Condition condition=lock.newCondition(); //创建一个变量控制执行main还是sub private boolean isSub=true; public void sub(int i){ lock.lock(); try { //当不是sub时,等待 while (!isSub) { try { condition.await(); } catch (Exception e) { e.printStackTrace(); } } for(int j=1;j<=10;j++){ System.out.println("sub Thread sequece of "+ j+",loop of "+i); } isSub=false; condition.signal();//相当于notify } finally { lock.unlock(); } }
还有一个问题就是,在等待Condition时,有可能出现虚假唤醒(spuriouswakeup),所以这里使用while做调节而不是if条件判断。
- 读写锁
关于读写锁,读数据也要加锁,目的是防止读到未写入完成的数据,但不会限制其他的读线程
简单的来说读写锁就是读锁和写锁互斥,写锁和写锁互斥,读锁和读锁不互斥。读写锁的目的是同一把锁读锁既可以与写锁互斥,又可以跟其他读锁共享。