昨天回到北京,年前就把工作辞掉了,现在正是失业状态,本想着今天看点东西面试,犯懒了,哎
线程安全的概念
当多个线程访问一个类(对象或者方法)时,这个类始终都能表现出正常的行为,那么这个类(对象,方法)就是线程安全的(这个解释有点搞笑!)
Synchronied
可以再任意对象和方法上加锁。而加锁的这段代码称为互斥区或者叫临界区。
锁竞争的问题
是指在同步的时候,当一个线程运行完毕释放资源,其他线程抢资源会导致CPU使用率暴增引起机器高负荷运行,导致的问题:1程序运行缓慢,2程序崩溃,3更甚者导致服务器宕机。
因此,在程序的编写的过程中要避免锁竞争的问题。极端举例,当五个线程同时竞争还能hold住,当线程超过1k或者说线程到1w,这种问题就会暴露出来,危险!
多个线程多个锁的问题
锁也分对象的锁,类的锁。
1、加对象锁实现同步
package threadTest.多锁;
/** * 单锁,对象锁 * @author Administrator * */ publicclass Lock {
privateintflag = 0;
Object key = null; public Lock(Object key) { this.key = key; }
publicvoid setTag(String tag){ synchronized(key){ if("a".equals(tag)){ flag = 200; System.out.println("set tag,"+flag); }else { flag = 300; System.out.println("set tag,"+flag); } try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("tag="+ tag+",flag="+flag); } }
publicstaticvoid main(String[] args) { Object key = new Object();
final Lock m1 = new Lock(key); final Lock m2 = new Lock(key);
Thread t1 = new Thread(new Runnable() { @Override publicvoid run() { m1.setTag("a"); } });
Thread t2 = new Thread(new Runnable() { @Override publicvoid run() { m2.setTag("b"); } });
t1.start(); t2.start();
}
}
|
2、加类级锁实现同步,操作方法加入static
package threadTest.多锁;
/** * 类锁 * @author Administrator * */ publicclass ClassLock {
privatestaticintflag = 0;
Object key = null; public ClassLock(Object key) { this.key = key; }
public ClassLock() { super(); }
publicstaticsynchronizedvoid setTag(String tag){ if("a".equals(tag)){ flag = 200; System.out.println("set tag,"+flag); }else { flag = 300; System.out.println("set tag,"+flag); } try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("tag="+ tag+",flag="+flag);
}
publicstaticvoid main(String[] args) {
final ClassLock m1 = new ClassLock(); final ClassLock m2 = new ClassLock();
Thread t1 = new Thread(new Runnable() { @Override publicvoid run() { m1.setTag("a"); } });
Thread t2 = new Thread(new Runnable() { @Override publicvoid run() { m2.setTag("b"); } });
t1.start(); t2.start();
}
}
|
同步synchronized
同步的概念就是共享,如果不是共享的资源,就没必要进行同步。(这里要分清原子性和同步的概念,目前我理解的原子性还是,操作不可再分,同步可以让一个组操作完整执行而不被其他线程打断,个人理解,以后还要继续优化)
异步asynchronized
异步的概念是独立,相互之间不受到任何的制约。对java来说异步就是多个线程可以并发的访问没有加同步手段的方法,变量。而同步的方法多个线程要排队一个一个的访问。
同步的目的是线程安全,对于线程安全来说,需要满足两个特性:原子性(同步),可见性
synchronized使用技巧:
publicclass Syn { publicsynchronizedvoid meth1(){ System.out.println("meth01"); meth2(); } publicsynchronizedvoid meth2(){ System.out.println("meth02"); meth3(); } publicsynchronizedvoid meth3(){ System.out.println("meth03"); } publicstaticvoid main(String[] args) { final Syn syn = new Syn(); Thread t1 = new Thread(new Runnable() { publicvoid run() { syn.meth1(); } }); t1.start(); } } |
synchronized的方法可以无阻赛调用。理解:
有点锁域的问题:
换一种写法更容易理解;
Object object = new Object(); publicvoid meth1(){ synchronized (object) { System.out.println("meth01"); //meth2(); synchronized (object) { System.out.println("meth02"); //meth3(); synchronized (object) { System.out.println("meth03"); } } } } |
在同步的时候,同步区运行错误怎么处理?
第一种情况,继续运行方式
在一个同步块里,出错的执行与这个块里面的其他的执行没关系,可以采用try-catch方式捕获出错的执行,记录在日志或者其他里面,然后continue继续执行,待其他的执行完毕后再单独处理这个出错的执行。
第二种情况,中断运行
这种情况的环境前提是,执行区里面的所有的执行都是相互依赖的,同生共死的关系,一个运行失败时抛出打断异常,interruptedException,或者在catch里面throw new runtimeException();都可以中断。然后回滚前面的数据。
synchronized里面的锁
publicclass SynLock {
publicvoid method1(){ synchronized (this) { //对象锁 System.out.println("method1"); } } publicvoid method2(){ synchronized (SynLock.class) { //类锁 System.out.println("method2"); } } publicvoid method3(){ Object object =new Object(); synchronized (object) { //任意对象锁 System.out.println("method3"); } } publicstaticvoid main(String[] args) {
}
} |
注意:在加锁的时候尽量不要出现用字符串常量加锁,会出现死循环的问题(这段代码的结构有问题,不能说明问题,线程1拿到锁资源后一直没释放,导致线程2没进来。),还有字符串的在作为锁的时候不能修改字符串的值,一旦修改他就会指向新的对象,原来的锁就会释放,其他线程就会进来(相当于锁坏了,不能限制排斥其他线程了)
publicclass stringLock {
String s="ce"; publicvoid method(){ synchronized (s) { try { while (true) { System.out.println(Thread.currentThread().getName()+",开始"); Thread.sleep(1000); System.out.println(Thread.currentThread().getName()+",结束"); } } catch (InterruptedException e) { e.printStackTrace(); } } }
publicstaticvoid main(String[] args) { Object object = new Object(); final stringLock lock = new stringLock(); Thread t1 = new Thread(new Runnable() { @Override publicvoid run() { lock.method(); } },"t1"); Thread t2 = new Thread(new Runnable() { @Override publicvoid run() { lock.method(); } },"t2");
t1.start(); t2.start();
}
} |
volatile再次理解
概念:实现变量在多个线程间可见。
验证变量不可见的反例子
publicclass volatileTset extends Thread {
privatebooleanisRunning = true; publicboolean isRunning() { returnisRunning; } publicvoid setRunning(booleanisRunning) { this.isRunning = isRunning; }
@Override publicvoid run() { System.out.println("进入Run"); while(isRunning){
} System.out.println("终止Run"); }
publicstaticvoid main(String[] args) throws InterruptedException { volatileTset v = new volatileTset();
v.start(); v.sleep(3000); v.setRunning(false); System.out.println("设置了isrunning"); v.sleep(1000); System.out.println(v.isRunning());
}
} |
这里虽然改变了isRunning的值但是,线程并没有停止,说明线程的工作内存里的isRunning的值并没有改变。
volatile修饰后的变量,他会强制线程在用到这个变量的时候到主内存中去读取这个变量,并且把值加载到工作内存中去
原子类的使用
publicclass AtomicTest extends Thread { //加入了static 使这个变量变成共有的变量这个变量是类的 privatestaticvolatile AtomicInteger count = new AtomicInteger(0); privatestaticvoid add(){ for(inti = 0;i < 100;i++){ count.getAndIncrement(); } System.out.println(count); } @Override publicvoid run() { add(); }
publicstaticvoid main(String[] args) { AtomicTest[] arr = new AtomicTest[10]; for(inti = 0;i<10;i++){ arr[i] =new AtomicTest(); } for(inti = 0;i<10;i++){ arr[i].start(); } } } |
如果原子类有多个方法一起使用,它只能保证一个方法的原子性。
线程之间的通信
使用wait/notify方法实现线程的通信,
1、 wait和notify必须配合synchronized关键字使用(配合一些锁使用也可以)
2、 wait方法释放锁,notify方法不释放锁
通信渐入
设计需求:
定义一个数组A,线程t1添加元素,t2发现A的大小超过5时就抛出异常终止运行。
publicclass Waitnotify {
privatevolatilestatic ArrayList list = new ArrayList(); privatevolatilestatic inta= 0; publicvoid add(){ list.add("ceshi"); } publicint getLength(){ returnlist.size(); }
publicstaticvoid main(String[] args) {
final Waitnotify w= new Waitnotify(); final Object object = new Object();
Thread t1 = new Thread(new Runnable() { publicvoid run() { synchronized (object) { try { for(inti = 0;i<10;i++){ w.add(); System.out.println(Thread.currentThread().getName()+"添加了一个元素"); Thread.sleep(500); } } catch (InterruptedException e) { e.printStackTrace(); } } } },"t1"); Thread t2 = new Thread(new Runnable() { publicvoid run() { while(true){ if(w.list.size()>5){ System.out.println(Thread.currentThread().getName()+"停止线程"); thrownew RuntimeException(); } } } },"t2");
t1.start(); t2.start();
} } |
优化这段代码
现在加入了锁,和synchronize控制,讲解:程序先执行了t2,启动后拿到锁资源,发现数组长度并没有到5,锁进行了wait(),程序就停止在wait()的这一行,同时释放掉锁资源,同时t1拿到了锁的资源进行执行添加的操作,当添加到5的时候,进行唤醒另一个线程notify(),但是,此刻的锁仍在t1这里,所以t1仍然继续执行,知道t1执行完毕释放了锁,t2才继续执行停止线程。
问题:t2没法时时性得到通知
publicclass Waitnotify {
privatevolatilestatic ArrayList list = new ArrayList(); privatevolatilestaticinta = 0;
publicvoid add() { list.add("ceshi"); }
publicint getLength() { returnlist.size(); }
publicstaticvoid main(String[] args) {
final Waitnotify w = new Waitnotify(); final Object object = new Object();
Thread t1 = new Thread(new Runnable() { publicvoid run() { synchronized (object) { try { for (inti = 0; i < 10; i++) { w.add(); System.out.println(Thread.currentThread().getName() + "添加了一个元素"); Thread.sleep(500); if (w.list.size() == 5) { System.out.println("发出通知~~"); object.notify(); } } } catch (InterruptedException e) { e.printStackTrace(); } } } }, "t1"); Thread t2 = new Thread(new Runnable() { publicvoid run() { synchronized (object) {
try { if (w.list.size() != 5) { object.wait(); } System.out.println(Thread.currentThread().getName() + "停止线程"); thrownew RuntimeException(); } catch (InterruptedException e) { e.printStackTrace(); } } } }, "t2"); t2.start(); t1.start(); }
} |
继续优化:利用java.util.concurrent 下的CountDownLatch,利用await(),与countdown()进行合理利用,具体解释看jdkapi的解释,此时可以达到时时的结束程序
publicclass Waitnotify {
privatevolatilestatic ArrayList list = new ArrayList(); privatevolatilestaticinta = 0;
publicvoid add() { list.add("ceshi"); }
publicint getLength() { returnlist.size(); }
publicstaticvoid main(String[] args) {
final Waitnotify w = new Waitnotify(); //final Object object = new Object(); final CountDownLatch latch = new CountDownLatch(1);
Thread t1 = new Thread(new Runnable() { publicvoid run() { //synchronized (object) { try { for (inti = 0; i < 10; i++) { w.add(); System.out.println(Thread.currentThread().getName() + "添加了一个元素"); Thread.sleep(500); if (w.list.size() == 5) { System.out.println("发出通知~~"); //object.notify(); latch.countDown(); } } } catch (InterruptedException e) { e.printStackTrace(); //} } } }, "t1"); Thread t2 = new Thread(new Runnable() { publicvoid run() { //synchronized (object) {
try { if (w.list.size() != 5) { // object.wait(); latch.await(); } System.out.println(Thread.currentThread().getName() + "停止线程"); thrownew RuntimeException(); } catch (InterruptedException e) { e.printStackTrace(); } } //} }, "t2"); t2.start(); t1.start(); }
} |