进程是电脑程序运行的最小单位,而线程则是进程的最小执行单位
多线程呢就是指一个进程运行时,多个线程并行交替执行的过程。可以提高代码的执行效离
1.继承thread类
特点:因为是通过继承的方式,所以该类就不能继承其他的类了,有局限性
使用:直接使用该类调用start方法
2.实现Runnable接口
特点:通过实现接口的方式避免了第一种方式的单继承的局限性问题
使用:new Thread(实现了Runnable的类对象).start()
3.实现Callnable接口
synchronized的三种应用方式
修饰实例方法,作用于当前实例加锁,进入同步代码前要获得当前实例的锁
修饰静态方法,作用于当前类对象加锁,进入同步代码前要获得当前类对象的锁
修饰代码块,指定加锁对象,对给定对象加锁,进入同步代码库前要获得给定对象的锁。
public class a {
//方法1
public void display1(Thread t) {
synchronized (this){
System.out.println(t.getName());
for (int i = 0; i < 100; i++) {
System.out.println(i);
}
}
}
//方法2
public synchronized void display2(Thread t) {
System.out.println(t.getName());
for (int i = 0; i < 100; i++) {
System.out.println(i);
}
}
public static void main(String[] args) {
a a = new a();
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
a.display2(Thread.currentThread());
}
},"a1");
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
a.display2(Thread.currentThread());
}
},"a2");
thread1.start();
thread2.start();
}
}
举例:
public class a {
Lock lock = new ReentrantLock(); //用于方法1
ReentrantReadWriteLock locks = new ReentrantReadWriteLock (); //用于方法2
//方法1 一般要写成try catch块的形式,把lock.unclock()方法放到finally里面
public void display1(Thread t) {
lock.lock();
System.out.println(t.getName()+":获取到了锁");
for (int i = 0; i < 100; i++) {
System.out.println(i);
}
lock.unlock();
}
//方法2
public void display2(Thread t) {
locks.writeLock().lock();
// locks.writeLock().lock();
try{
System.out.println(t.getName()+":获取到了锁");
for (int i = 0; i < 100; i++) {
System.out.println(i);
}
}catch (Exception e){
e.printStackTrace();
}finally {
locks.writeLock().unlock();
// locks.writeLock().unlock();
}
}
//方法3 锁降级
public void display4(Thread t) {
locks.writeLock().lock();
locks.readLock().lock();
try{
System.out.println(t.getName()+":获取到了锁");
for (int i = 0; i < 100; i++) {
System.out.println(i);
}
}catch (Exception e){
e.printStackTrace();
}finally {
locks.readLock().unlock();
locks.writeLock().unlock();
}
}
//方法4 锁升级 (会产生死锁问题)
public void display4(Thread t) {
locks.readLock().lock();
locks.writeLock().lock();
try{
System.out.println(t.getName()+":获取到了锁");
for (int i = 0; i < 100; i++) {
System.out.println(i);
}
}catch (Exception e){
e.printStackTrace();
}finally {
locks.writeLock().unlock();
locks.readLock().unlock();
}
}
public static void main(String[] args) {
a a = new a();
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
a.display1(Thread.currentThread());
}
},"a1");
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
a.display1(Thread.currentThread());
}
},"a2");
thread1.start();
thread2.start();
}
}
注意
使用lock锁时,lock必须定义到类里面,这样线程调用的时候拿到的是同一个lock对象,这样才能锁住
用法
常用方法及用处:
lock() 获取锁,获取不到会一直等待
trylock() 返回值是boolean类型,拿到锁就返回true,没有拿到就返回false,带时间限制的tryLock(),拿不到lock,就等一段时间,超时返回false
unlock() 释放锁
读写锁的特性
1.获取锁顺序问题
非公平模式:读锁和写锁的获取的顺序是不确定的,可能会延缓一个或多个读或写线程,但是会比公平锁有更高的吞吐量
公平模式:线程将会以队列的顺序获取锁,等待锁时间越长的先获取
2.可重入:就是说一个线程在获取某个锁后,还可以继续获取该锁。synchronized内置锁就是可重入的,如果不可重入,那么比如一个类的两个方法都被synchronized所修饰,那么方法A就不能够调方法B了。
3.锁降级
首先要知道
锁升级:从读锁变成写锁
锁降级:从写锁变成读锁;
ReetrantReadWriteLock是不支持锁升级的,会产生死锁问题
4.读锁跟写锁是互斥的
对比synchronized使用,测试读操作效率
public class ReadAndWriteLockTest {
public synchronized static void get(Thread thread) {
System.out.println("start time:" + System.currentTimeMillis());
for (int i = 0; i < 5; i++) {
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(thread.getName() + ":正在进行读操作……");
}
System.out.println(thread.getName() + ":读操作完毕!");
System.out.println("end time:" + System.currentTimeMillis());
}
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
get(Thread.currentThread());
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
get(Thread.currentThread());
}
}).start();
}
}
public class ReadAndWriteLockTest {
ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
public static void get(Thread thread) {
lock.readLock().lock();
System.out.println("start time:" + System.currentTimeMillis());
for (int i = 0; i < 5; i++) {
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(thread.getName() + ":正在进行读操作……");
}
System.out.println(thread.getName() + ":读操作完毕!");
System.out.println("end time:" + System.currentTimeMillis());
lock.readLock().unlock();
}
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
get(Thread.currentThread());
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
get(Thread.currentThread());
}
}).start();
}
}
判断读锁跟写锁互斥
public class ReadAndWriteLockTest {
public static ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
public static void main(String[] args) {
//同时读、写
ExecutorService service = Executors.newCachedThreadPool();
service.execute(new Runnable() {
@Override
public void run() {
readFile(Thread.currentThread());
}
});
service.execute(new Runnable() {
@Override
public void run() {
writeFile(Thread.currentThread());
}
});
}
// 读操作
public static void readFile(Thread thread) {
lock.readLock().lock();
boolean readLock = lock.isWriteLocked();
if (!readLock) {
System.out.println("当前为读锁!");
}
try {
for (int i = 0; i < 5; i++) {
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(thread.getName() + ":正在进行读操作……");
}
System.out.println(thread.getName() + ":读操作完毕!");
} finally {
System.out.println("释放读锁!");
lock.readLock().unlock();
}
}
// 写操作
public static void writeFile(Thread thread) {
lock.writeLock().lock();
boolean writeLock = lock.isWriteLocked();
if (writeLock) {
System.out.println("当前为写锁!");
}
try {
for (int i = 0; i < 5; i++) {
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(thread.getName() + ":正在进行写操作……");
}
System.out.println(thread.getName() + ":写操作完毕!");
} finally {
System.out.println("释放写锁!");
lock.writeLock().unlock();
}
}
}
总结
1.Java并发库中ReetrantReadWriteLock实现了ReadWriteLock接口并添加了可重入的特性
2.ReetrantReadWriteLock读写锁的效率明显高于synchronized关键字
3.ReetrantReadWriteLock读写锁的实现中,读锁使用共享模式;写锁使用独占模式,换句话说,读锁可以在没有写锁的时候被多个线程同时持有,写锁是独占的
4.ReetrantReadWriteLock读写锁的实现中,需要注意的,当有读锁时,写锁就不能获得;而当有写锁时,除了获得写锁的这个线程可以获得读锁外,其他线程不能获得读锁
经典用例:可以实现生产者跟消费者的实例
lock可以使用Condition,Condition是一个多线程间协调通信的工具类,要与一个lock绑定使用
①volatile变量是Java语言提供了一种稍弱的同步机制
②volatile能够保证字段的可见性
②volatile能够禁止指令重排
原理:volatile变量不会被缓存在寄存器或者对其他处理器不可见的地方,因此在读取volatile类型的变量时总会返回最新写入的值
究竟能否保证线程安全?
答案是在一定条件下能,1.写入值时不依赖于当前值,不包含其他变量
经典用法
做状态标记量
volatile boolean flag ;
……
while( !flag ){
……
}
public void setFlag() {
flag = true;
}
synchronized是java的一个关键字,是内置的语言实现的,是用来解决多线程安全问题的,他不能去获取到锁状态,当遇到异常的时候会自动的释放掉锁,synchronized使用Object对象本身的wait 、notify、notifyAll调度机制,而Lock可以使用Condition进行线程之间的调度,Synchronized存在明显的一个性能问题就是读与读之间互斥
lock是一个接口,
lock等待锁过程中可以用interrupt来中断等待,而synchronized只能等待锁的释放,不能响应中断;
Lock可以通过trylock来知道有没有获取锁,而synchronized不能;
Lock可以提高多个线程进行读操作的效率。(可以通过readwritelock实现读写分离)
在性能上来说,如果竞争资源不激烈,两者的性能是差不多的,而当竞争资源非常激烈时(即有大量线程同时竞争),此时Lock的性能要远远优于synchronized。所以说,在具体使用时要根据适当情况选择。
思考
为什么在分布式情况下synchronized和lock会失效?
因为他们的前提条件必须是同一进程内,而分布式服务里面,多个服务属于不同进程,所以用普通的同步锁会失效,这里就要用到分布式锁去处理