多线程运行的原理:每个程序就是进程,而每个进程中会有多个线程,而CPU是在这个写线程之间进行切换,多线程可以提高程序的运行效率,但是不能无限制的开线程。
Thread种run()方法和start()方法的区别:
run()方法只是普通方法的调用,不会新的线程,start()则会开启一个新的线程。
一、synchronized 线程同步锁
synchronized是java中的一个关键字,也就是说是java语言内置的特性。
如果一个代码块被synchronized 修饰了,当一个线程获取了对应的锁(同一把锁,synchronized中的参数相同就是同一把锁),并执行该代码块时,其他线程便只能一直等待,等待获取锁的线程释放锁,而这里获取锁的线程释放锁只会有两种情况:
1.获取锁的线程执行完了该代码块,然后线程释放对锁的占有。
2.线程执行发生异常,此时JVM会让线程自动释放锁。
final MySynchronized mySynchronized = new MySynchronized();
final MySynchronized mySynchronized2 = new MySynchronized();
new Thread(){
public void run() {
synchronized (mySynchronized) { //可以传任意对象,相同的就是同一把锁
System.out.println(this.getName() + "---start---");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(this.getName() + "---end---");
}
}
}.start();
二、Lock锁
Lock和 synchronized的区别:
1)、Lock不是java语言内置的, synchronized是java语言的关键字,因此是内置特性。Lock是一个类,通过这个类可以实现同步访问。
2)、Lock和 synchronized一个非常大的不同就是, synchronized不需要用户去手动释放锁,当 synchronized方法或者 synchronized代码块执行完毕后或者在发生异常时候,系统会自动让线程释放对锁的占用;而lock必须要用户去手动释放锁,就有可能导致出现死锁现象,因此使用Lock时需要在finally块中释放锁。(使用Lock一定要在try{}catch{}块中进行,在finally去释放锁unlock,否则在发生异常的时候,会造成死锁)。
3)、Lock可以让等待的线程相应中断,而synchronized去不行,使用synchronized时,等待的线程会一直等待下去,不能都响应中断;
4)、Lock可以知道有没有成功获取锁,而synchronized却无法办到。
5)、Lock可以提高多个线程进行读操作的效率。
在性能上来说,如果竞争资源不激烈,两者的性能是差不多,而当竞争资源非常激烈时(即有大量的线程同时竞争),此时Lock的性能要远远优于synchronized。所以说,在具体使用时要根据适当情况选择。
Lock是java.util.concurrent.locks包下的类
Lock是一个接口
public interface Lock{
void lock(); //获取锁void lockInterruptibly throws interruptedException; //获取可中断的锁(这里是指等待的线程可以不在等待)
boolean tryLock(); //试着获取锁,返回true或false
boolean tryLock(long time,TimeUnit unit) throws InterruptedException; //如果没有拿到锁,会等一定时间
void unlock(); //释放锁
}
1.lockInterruptibly()//可中断锁
lockInterruptibly()方法,当线程通过这个方法去获取锁时,如果其他线程正在等待获取锁,则这个等待的线程可以被中断等待的状态。也就是说,当两天线程同时通过lock.lockInterruptibly()方法想获取某个锁时,假若此时A线程获取了锁,而线程B只有等待,那么线程B可以调用threadB.interrupt()方法能够中断线程B 的等待状态。(注意是等待的线程的等待状态被中断,获取锁的线程不会被中断)
例子:
public class MyInterruptibly {
private Lock lock = new ReentrantLock();
public static void main(String[] args) {
MyInterruptibly test = new MyInterruptibly();
MyThread thread0 = new MyThread(test);
MyThread thread1 = new MyThread(test);
thread0.start();
thread1.start();
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
thread1.interrupt();
System.out.println("================================");
}
public void insert(Thread thread) throws InterruptedException{
lock.lockInterruptibly();
try {
System.out.println(thread.getName() + "得到了锁");
long startTime = System.currentTimeMillis();
for (; ; ) {
if (System.currentTimeMillis() - startTime >= Integer.MAX_VALUE)
break;
}
}finally { //防止异常出现死锁
lock.unlock();
System.out.println(thread.getName()+"释放了锁");
}
}
class MyThread extends Thread{
private MyInterruptibly test = null;
public MyThread(MyInterruptibly test){
this.test = test;
}
public void run(){
try {
test.insert(Thread.currentThread());
} catch (Exception e) {
// e.printStackTrace();
System.out.println(Thread.currentThread().getName()+"被中断");
}
}
}
2.tryLock
tryLock(long time,TimeUnit unit)方法和tryLock()方法是类似的,只不过区别在于这个方法在拿不到锁的时候会等待一定时间,在时间期限内如果还拿不到锁,就返回false.如果一开始就拿到锁或者在等待期限内拿到了锁,则返回true。
public class MyTryLock {
private static ArrayList<Integer> arrayList = new ArrayList<Integer>();
static Lock lock = new ReentrantLock();
public static void main(String[] args) {
new Thread(){
public void run(){
Thread thread = Thread.currentThread();
boolean tryLock = lock.tryLock();
System.out.println(thread.getName()+" "+tryLock);
if(tryLock){
System.out.println(thread.getName() + "拿到了锁");
for(int i = 1;i<10;i++){
arrayList.add(i);
}
lock.unlock();
System.out.println(thread.getName()+"释放了锁");
}
}
}.start();
new Thread(){
public void run(){
Thread thread = Thread.currentThread();
boolean tryLock = lock.tryLock();
System.out.println(thread.getName()+" "+tryLock);
if(tryLock){
System.out.println(thread.getName() + "拿到了锁");
for(int i = 0;i<10;i++){
arrayList.add(i);
}
lock.unlock();
System.out.println(thread.getName()+"释放了锁");
}
}
}.start();
}
}
3.ReadWriteLock读写锁
ReadWriteLock也是一个接口,在它里面只定义了两个方法:
public interface ReadWriteLock(){
Lock readLock();
Lock writeLock();
}
一个用来获取读锁,一个用户来获取写锁,也就是说将文件的读写操作分开,分成2个锁来分配线程,从而使得多个线程可以同时进行读操作。下面的ReentranReadWriteLock实现了ReadWriteLock接口。
如果一个线程已经占用了读锁,则此时其他线程如果要申请写锁,则申请写锁的线程会一直等待释放读锁,而其他申请线程如果要申请读锁则可以正常执行。
如果一个线程已经占用了写锁,则此时其他线程如果申请写锁或读锁,则申请的线程会一直等待释放写锁。
ReadWriteLock读写锁和synchronized区别:
使用读写锁,可以实现读写分离锁定,读操作并发进行,写操作锁定单个进程。而使用synchronized则要读写都完成后才能释放锁。
public class MyReentranReadWriteLock {
private ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
public static void main(String[] args) {
final MyReentranReadWriteLock test = new MyReentranReadWriteLock();
new Thread(){
public void run(){
test.get(Thread.currentThread());
test.write(Thread.currentThread());
}
}.start();
new Thread(){
public void run(){
test.get(Thread.currentThread());
test.write(Thread.currentThread());
}
}.start();
}
//读操作,用读锁来锁定
public void get(Thread thread){
rwl.readLock().lock();
try {
long start = System.currentTimeMillis();
while (System.currentTimeMillis() -start <= 1){
System.out.println(thread.getName()+"正在进行读操作");
}
System.out.println(thread.getName()+"读操作完毕");
}finally {
rwl.readLock().unlock();
}
}
//写操作用写锁来锁定
public void write(Thread thread){
rwl.writeLock().lock();
try {
long start = System.currentTimeMillis();
while (System.currentTimeMillis() - start <=1){
System.out.println(thread.getName()+"正在进行写操作");
}
System.out.println(thread.getName()+"写操作完毕");
}finally {
rwl.writeLock().unlock();
}
}
}