为什么是线程同步,什么是线程同步
多个线程访问公共资源时,只有一个独占!否则资源出问题
一个简单的同步情景
public class SynchronizedTest implements Runnable{
Timer timer = new Timer();
public static void main(String[] args) {
SynchronizedTest test = new SynchronizedTest();
Thread aThread = new Thread(test);
Thread bThread = new Thread(test);
aThread.setName("a");
bThread.setName("b");
aThread.start();
bThread.start();
}
public void run(){
timer.add(Thread.currentThread().getName());
}
}
class Timer{
private static int num = 0;
public void add(String threadName){
num ++;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return ;
}
System.out.println(threadName+" "+num);
}
}
//a 2
//b 2
第一个线程访问时,静态变量num++ 变为1,第一个线程此时还未输出,但是另一个线程此时也访问,静态变量为2,最终输出结果如上;
而我们现在想得到的结果是一个原子操作,一个线程得到的num:1,输出;另一个线程得到的num:2,输出
同步锁
public class SynchronizedTest implements Runnable{
Timer timer = new Timer();
public static void main(String[] args) {
SynchronizedTest test = new SynchronizedTest();
Thread aThread = new Thread(test);
Thread bThread = new Thread(test);
aThread.setName("a");
bThread.setName("b");
aThread.start();
bThread.start();
}
public void run(){
timer.add(Thread.currentThread().getName());
}
}
class Timer{
private static int num = 0;
public synchronized void add(String threadName){
//synchronized (this) {
num ++;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return ;
}
System.out.println(threadName+" "+num);
//}
}
}
死锁
一个简单死锁的例子
public class DeadLock implements Runnable{
public static Object o1 = new Object();
public static Object o2 = new Object();
public int flag = -1;
public static void main(String args[]) {
// TODO Auto-generated method stub
DeadLock deadLock1 = new DeadLock();
DeadLock deadLock2 = new DeadLock();
deadLock1.flag = 1;
deadLock2.flag = 0;
Thread aThread = new Thread(deadLock1);
Thread bThread = new Thread(deadLock2);
aThread.start();bThread.start();
}
@Override
public void run() {
System.out.println(this);
// TODO Auto-generated method stub
if(flag == 1){
synchronized (o1) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
synchronized (o2){
System.out.println(flag+" o1");
}
}
}
if(flag == 0){
synchronized (o2) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
synchronized (o1){
System.out.println(flag+" o2");
}
}
}
}
}
//DeadLock@782bbb7b
DeadLock@7f21c5df
ing...
为什么会死锁
非同步的方法依旧可以给其他线程执行
public class OrtherTest implements Runnable{
int b = 1000;
public static void main(String[] args) {
OrtherTest test = new OrtherTest();
Thread thread = new Thread(test);
thread.start();
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
test.method();
}
@Override
public void run() {
// TODO Auto-generated method stub
synchronized (this) {
b = 2000;
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(b + "run()");
}
}
public synchronized void method() {
b = 3000;
System.out.println(b +" method()");
}
}
//3000 method()
//3000run()
开启的分支线程先拿到锁,b = 2000,但未输出;主线程里执行一个普通方法b = 3000,先行输出(说明普通方法可以直接执行)
一个任务可以多次获得对象的锁
/**
* 同一任务可以再次持有对象锁 create on 2010.08.04 08:27
*
* @since jdk1.6
* @author maozj
* @version 1.0
*
*/
public class MutiObjectLock {
/**
* Test client
* @param args
*/
public static void main(String[] args) {
new Thread() {
public void run() {
new MutiObjectLock().f1();
}
}.start();
new Thread() {
public void run() {
new MutiObjectLock().f3();
}
}.start();
}
private static int count = 10;
/**
* synchronized f1()
*/
public synchronized void f1() {
if (--count > 0) {
System.out.println("f1() calling f2() count " + count);
f2();
}
}
/**
* synchronized f2()
*/
public synchronized void f2() {
if (--count > 0) {
System.out.println("f2() calling f1() count " + count);
f1();
}
}
public synchronized void f3() {
System.out.println("f3() calling count " + count);
}
}
/*
f1() calling f2() count 9
f2() calling f1() count 8
f1() calling f2() count 7
f2() calling f1() count 6
f1() calling f2() count 5
f2() calling f1() count 4
f1() calling f2() count 3
f2() calling f1() count 2
f1() calling f2() count 1
f3() calling count 0
*/
如果一个方法在一个同一个对象上调用了第二个方法,同时第二个方法也调用了同一对象上的另一方法,JVM就会发生这样的情况。JVM负责跟踪对象被加锁的次数,锁被完全释放,引用计数为0,每当这个任务在这个对象上获得锁时(+1),每当任务离开synchronize(-1),例如(synchronize(this)任务获得this对象锁),注意都是同一对象
wait、sleep、notify
wait():某一条件下,任务阻塞,让出锁的占有;
sleep():当前线程阻塞,但依旧占有锁的权利;
notify():唤醒众多等待同一个锁的任务中只有一个会被唤醒,当前还占有锁的任务继续执行完到synchronize代码块结束,之后让出锁的占有
生产者消费者
wait、notify实现
public class ProducerAndConsumer {
public static void main(String[] args) {
ProductList pl = new ProductList();
Factory f = new Factory(pl);
Consumer c = new Consumer(pl);
Thread t1 = new Thread(f);
Thread t2 = new Thread(c);
t1.start();
t2.start();
}
}
class Product {
private int id;
Product(int id) {
this.id = id;
}
@Override
public String toString() {
return "Product [id=" + id + "]";
}
}
class ProductList {
int index = 0;
private Product[] p = new Product[6];
public synchronized void push(Product pr) {
while (index == p.length) {
try {
this.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
this.notify();
p[index] = pr;
System.out.println("生产了" + p[index]);
index++;
}
public synchronized Product pop() {
while (index == 0) {
try {
this.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
this.notify();
index--;
System.out.println("消费了" + p[index]);
return p[index];
}
}
class Factory implements Runnable {
private ProductList pl = null;
Factory(ProductList pl) {
this.pl = pl;
}
@Override
public void run() {
// TODO Auto-generated method stub
for (int i = 0; i < 20; i++) {
Product p = new Product(i);
pl.push(p);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
class Consumer implements Runnable {
private ProductList pl = null;
Consumer(ProductList pl) {
this.pl = pl;
}
@Override
public void run() {
// TODO Auto-generated method stub
for (int i = 0; i < 20; i++) {
Product p = pl.pop();
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
之前打印语句在pop、push同步操作之后执行,这样会导致错误的结果:消费者先行消费了0号产品,生产者后生产的结果,但实际队列存储的数据是正确的。因为run()里的输出语句此时是多线程同时运行,无法控制先后。
//Todo 其他实现方式