深切怀念传智播客张孝祥老师,特将其代表作——Java并发库视频研读两遍,受益颇丰,记以后阅
03. 传统线程互斥技术
线程安全问题例子:银行转账
同一个账户一边进行出账操作(自己交学费),另一边进行入账操作(别人给自己付款),线程不同步带来的安全问题
示例:逐个字符的方式打印字符串
class Outputer
{
public void output(String name)
{
int len =name.length();
for (int i=0; i<len; i++)
SOP(name.charAt(i));逐个字符打印
SOP();换行
}
}
public void test()
{
Outputer outputer = newOutputer();
//同一个对象的方法两个线程调用肯定有问题
newThread(
new Runnable()
{
public void run()
{
Thread.sleep(100);
outputer.output(“zhangxiaoxiang”);
}
}).start();
newThread(
new Runnable()
{
public void run()
{
Thread.sleep(100);
outputer.output(“lihuoming”);
}
}).start();
}
注意:
内部类不能访问局部变量,要访问需加final
静态方法中不能创建内部类的实例对象,因为内部类是属于对象的,能访问对象的数据,静态方法中是不能访问对象的成员变量的
打印结果发现的问题:线程不同步所致,两个线程都在使用同一个对象
互斥方法:
a、同步代码块
synchronized(lock){}
b、同步方法
方法返回值前加synchronized
同步方法上边用的锁就是this对象
静态同步方法使用的锁是该方法所在的class文件对象
使用synchronized关键字实现互斥,要保证同步的地方使用的是同一个锁对象
public synchronized void output(String name)
{
int len =name.length();
这里就不要再加同步了,加上极易出现死锁
for (int i=0; i<len; i++)
SOP(name.charAt(i));逐个字符打印
SOP();换行
}
public class TraditionalThreadSynchronized{
/**
* @param args
*/
publicstatic void main(String[] args) {
newTraditionalThreadSynchronized().init();
}
privatevoid init(){
finalOutputer outputer = new Outputer();
newThread(new Runnable(){
@Override
publicvoid run() {
while(true){
try{
Thread.sleep(10);
}catch (InterruptedException e) {
// TODO Auto-generatedcatch block
e.printStackTrace();
}
outputer.output("zhangxiaoxiang");
}
}
}).start();
newThread(new Runnable(){
@Override
publicvoid run() {
while(true){
try{
Thread.sleep(10);
}catch (InterruptedException e) {
//TODO Auto-generated catch block
e.printStackTrace();
}
outputer.output3("lihuoming");
}
}
}).start();
}
staticclass Outputer{
publicvoid output(String name){
intlen = name.length();
synchronized(Outputer.class)
{
for(inti=0;i<len;i++){
System.out.print(name.charAt(i));
}
System.out.println();
}
}
publicsynchronized void output2(String name){
//使用的this为同步对像
intlen = name.length();
for(inti=0;i<len;i++){
System.out.print(name.charAt(i));
}
System.out.println();
}
publicstatic synchronized void output3(String name){
//使用的Outputer.class为同步对像
intlen = name.length();
for(inti=0;i<len;i++){
System.out.print(name.charAt(i));
}
System.out.println();
}
}
}
互斥的对象必须是同一个对象,写在普通成员方法上的synchronized使用的是this,写在静态方法上的synchronized使用的是本类的字节码对象。互斥之后,一个线程进入一个方法后,其他所有的线程都进不了与这个方法使用同一个锁的同步方法,可以想象成操作系统目前的状态是单线程。为什么需要同步?因为几个方法访问有共享资源,如果没有共享的资源,就完全没必要同步,因为同步是要耗费资源和时间的,降低效率。例如:JFinal中的Controller就是如此,每个请求都使用一个新的Controller来接收,即每个方法都是完全独立的。
public class TraditionalThreadSynchronized { /** * @param args */ public static void main(String[] args) { new TraditionalThreadSynchronized().init(); } private void init(){ final Outputer outputer = new Outputer(); new Thread(new Runnable(){ @Override public void run() { while(true){ try { Thread.sleep(10); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } outputer.output("zhangxiaoxiang"); } } }).start(); new Thread(new Runnable(){ @Override public void run() { while(true){ try { Thread.sleep(10); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } outputer.output3("lihuoming"); } } }).start(); } static class Outputer{ public void output(String name){ int len = name.length(); synchronized (Outputer.class) { for(int i=0;i<len;i++){ System.out.print(name.charAt(i)); } System.out.println(); } } public synchronized void output2(String name){ int len = name.length(); for(int i=0;i<len;i++){ System.out.print(name.charAt(i)); } System.out.println(); } public static synchronized void output3(String name){ int len = name.length(); for(int i=0;i<len;i++){ System.out.print(name.charAt(i)); } System.out.println(); } } }