例子程序:
/**
* 多个线程一把锁
* @author dev
*
*/
public class MyThread extends Thread{
private int count = 5;
//synchronized
@Override
public void run() {
count--;
System.err.println(this.currentThread().getName() + " count = "+count);
}
public static void main(String[] args) {
/**
* 多个线程访问myThread的run方法时,以排队的方式进行处理(CPU分配的先后顺序排队)
* 一个线程想要执行synchronized修饰的方法里的代码:
* 1,尝试获得锁
* 2,如果拿到该锁,执行synchronized代码体内容,拿不到锁,这个线程就会不断尝试获得这把锁,
* 直到拿到未止,而且是多个线程同时去竞争这把锁(也就是有锁竞争的问题)
*
*/
MyThread myThread = new MyThread();
//参数:Runnable target, String name
Thread t1 = new Thread(myThread,"t1");
Thread t2 = new Thread(myThread,"t2");
Thread t3 = new Thread(myThread,"t3");
Thread t4 = new Thread(myThread,"t4");
Thread t5 = new Thread(myThread,"t5");
t1.start();
t2.start();
t3.start();
t4.start();
t5.start();
}
}
代码说明:
MyThread继承Thread,是一个线程类,有一成员变量count,重写run方法:对count值进行减减,打印当前线程名字和count值。
main方法:启动5个线程t1、t2、t3、t4、t5,把MyThread对象注入进去,启动5个线程。
执行main方法,结果:
线程启动后,5个线程都等待着cpu的执行,所以5个线程的执行顺序是随机的,导致看到的t1、t2 、t3、 t4、 t5的输出顺序是随机的。对于count值,我们心里预期的应该输出 count=4、count=3、count=2、count=1、count=0,但是输出结果跟心理预期的是不一样的。这就是说,5个线程同时访问MyThread类的run方法,对count值进行减减,不是线程安全的。
将run方法加上synchronized 关键字,就是线程安全的:
//synchronized加锁 @Override public synchronized void run() { count--; System.err.println(this.currentThread().getName() + " count = "+count); }
打印结果:
这和我们心里的预期是一样的,也就是线程安全的。这就和开篇说的线程安全的定义:当多个线程访问某一个类(对象或者方法)时,这个类始终都能表现出正确的行为,那么这个类(对象或方法)就是线程安全的。这里的正确的行为,就是指你心里预期的。
run方法 加上synchronized加锁后,启动5个线程,当第一个线程拿到锁后,其他4个线程就会等待,等着第一个线程释放锁,一次类推。
多线程就是一个厕所,厕所门上有一个锁,外面5个人去抢这一把锁,谁抢到了锁,就可以进去上厕所,当第一个进去的出来后,其他4个人会同时去抢这把锁。所以会产生锁竞争的问题,当线程足够多的时候,锁竞争会导致CPU使用率过高,应尽量避免锁竞争。
搞10000个线程,看看锁竞争cpu使用情况:
代码:
MyThread myThread = new MyThread(); List<Thread> threads = new ArrayList<Thread>(); for(int i=0;i<10000;i++){ threads.add(new Thread(myThread,"t"+i)); } for(Thread t : threads){ t.start(); }
CPU: