针对于临界资源安全隐患问题的解决方式。引入锁机制。 1. 锁机制的作用:将异步的代码块变成同步的代码块。 2. 语法: synchronized(锁对象的地址){ //需要同步的代码块( 如果不同步,就会出现安全隐患问题) } 3. 任何的java对象都可以作为锁。 一个要求: 所有的线程看到的都是同一个对象。 4. 同步的代码块在可能的情况下,尽量缩小范围,提高其他代码的并发效率。 5. 运行逻辑: 当一个线程A执行到{}里,就表示该线程获取了锁对象,其他线程都必须等待,直到 线程A执行完了同步代码块,会自动释放锁对象。其他线程才有机会获取锁对象,谁获取到锁 对象,谁就执行同步代码块。
package CSDN.day02;
public class _13 {
public static void main(String[] args) {
}
}
class Desk1 implements Runnable {
private static int beanCount = 10;
public void take() {
beanCount--;
}
public void run() {
while (true) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
synchronized (this) {
if (beanCount > 0) {
take();
}
}
System.out.println(Thread.currentThread().getName() + "拿走了一颗豆子,剩余:" + beanCount);
if (beanCount <= 0) {
break;
}
}
}
}
1.如果使用了synchronized(锁对象){ } ,那么{ }中就是同步块。
2.synchronized可以作用在非静态方法,此时锁对象是this。
3.synchronized可以作用在静态方法上,此时锁对象是类名.class。
package CSDN.day0821;
public class _01 {
public static void main(String[] args) {
Blackboard b1 = new Blackboard();
Thread xm = new Thread(b1,"小明");
Thread xl = new Thread(b1,"小莉");
xm.start();
xl.start();
}
}
class Blackboard implements Runnable{
private static int apple = 10;
public void run() {
while(true){
take();
System.out.println(Thread.currentThread().getName()
+"拿走了一颗苹果,还剩"+apple+"个苹果");
if(apple <= 0){
break;
}
}
}
public synchronized void take(){
if(apple > 0){
apple--;
}
}
}
4.synchronized可以锁住方法里面的部分代码 即锁的范围就是同步块的范围 此时,锁对象可以是java的任何引用类型的对象。 5.非静态方法上锁: 就是在方法上添加修饰词synchronized -1. 当一个线程访问了该方法时,就是获取了锁对象,其他线程想要访问该方法,处于等待状态。 -2. 有个前提:多个线程访问的对象实例必须是同一个。 比如下面案例的桌子 必须是同一张桌子。 -3. 当一个线程正在访问一个实例的某一个同步方法时,this这个锁对象即被他所占用,其他线程想要执行 该实例的其他的同步方法时,也需要等待,因为this锁已经被占用了。
package CSDN.day0821;
public class _02 {
public static void main(String[] args) {
Desk desk1 = new Desk();
Desk desk2 = new Desk();
Thread t1 = new Thread(desk1,"xm");
Thread t2 = new Thread(desk1,"qq");
t1.start();
t2.start();
}
}
class Desk implements Runnable{
private int BeanCount = 10;
public synchronized void take(){
System.out.println("开始取豆子");
if(BeanCount>0){
BeanCount--;
}
if(BeanCount==0){
System.out.println("没豆子了");
}
// System.out.println("豆-1");
}
public void run() {
while(true){
take();
if (BeanCount<=0){
break;
}
System.out.println(Thread.currentThread().getName()+"取豆,还剩:"+BeanCount);
}
}
}
package CSDN.day0821;
import java.util.LinkedList;
import java.util.List;
public class _03 {
public static void main(String[] args) {
List pool = new LinkedList<>();
for (int i = 0; i < 10; i++) {
Thread t = new Thread(){
public void run(){
Boss b = Boss.getInstance();
}
};
pool.add(t);
}
for (Thread t : pool) {
t.start();
}
}
}
class Boss {
private static Boss instance;
private Boss() {
}
public synchronized static Boss getInstance() {
//synchronized(Boss.class)
if (instance == null) {
instance = new Boss();
}
return instance;
}
}
产生原因:线程1 先获取锁A,然后再想要获取锁B
线程2 先获取锁B,然后再想要获取锁A
两个线程都占用了对方想要的锁,而对方还占用并不释放。因此都出现了等待现象,无法继续向下执行。 这就是死锁。
例如:
package CSDN.day0821;
public class _04 {
public static void main(String[] args) {
Thread t1 = new Thread("小明"){
public void run(){
synchronized ("A"){
for(int i=1;i<=10;i++){
System.out.println(getName()+":"+i);
}
}
synchronized ("B"){
for(int i=1;i<=10;i++){
System.out.println(getName()+":"+i);
}}
}
};
Thread t2 = new Thread("小l"){
public void run(){
synchronized ("B"){
for(int i=1;i<=10;i++){
System.out.println(getName()+":"+i);
}
}
synchronized ("A"){
for(int i=1;i<=10;i++){
System.out.println(getName()+":"+i);
}}
}
};
}
}
1. 就是按照顺序加锁 2. 设置超时等待。 设置了一定时间限制,如果在这个时间范围内没有获取到锁,那就不执行锁里的内容
package CSDN.day0821;
public class _05 {
public static void main(String[] args) {
Thread t1 = new Thread("小明"){
public void run() {
synchronized ("A"){
for (int i = 0; i < 50; i++) {
System.out.println(getName()+":"+i);
}
synchronized ("B"){
for (int i = 50; i < 100; i++) {
System.out.println(getName()+":"+i);
}
}
}
}
};
Thread t2 = new Thread("小红"){
public void run() {
synchronized ("B"){
for (int i = 0; i < 50; i++) {
System.out.println(getName()+":"+i);
}
synchronized ("A"){
for (int i = 50; i < 100; i++) {
System.out.println(getName()+":"+i);
}
try {
"B".wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
};
t1.start();
t2.start();
}
}
与锁有关的方法
1、wait():释放自己占有的锁对象,进入等待队列中,不参与锁的争抢,不参与时间片段的争抢。 也就是阻塞了。知道被notify/notifyAll.注意,是锁调用该方法
wait(long timeout):等待一个指定时间,如果超过这个时间,则自动唤醒。
wait(long timeout ,intnaous) 指定的等待时间更精确一些。
2.notify():通知,唤醒等待队列中的某一个线程,是随机的。被唤醒的那个线程进入锁池状态,开始争抢锁对象。 3. notifyAll():通知,唤醒等待队列中的所有线程。被唤醒的所有线程进入锁池状态,开始争抢锁对象。
package CSDN.day0822;
public class _01 {
public static void main(String[] args) {
Object obj = new Object();
Thread down = new Thread(() -> {
System.out.println("开始加载图片");
for (int i = 1; i <= 100; i++) {
System.out.println(Thread.currentThread()
.getName() + ": " + i + "%");
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace( );
}
}
System.out.println("图片加载完成");
synchronized (obj) {
obj.notify();
}
}, "下载图片");
Thread show = new Thread(() -> {
try {
synchronized (obj) {
obj.wait(5000);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("开始显示图片");
for (int i = 1; i <= 100; i++) {
System.out.println(Thread.currentThread()
.getName() + ": " + i + "%");
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("图片显示完成");
}
}, "显示图片");
down.start();
show.start();
}
}
标准写法: 释放锁操作在finally模块中
package CSDN.day0822;
import java.util.concurrent.locks.ReentrantLock;
public class _02 {
public static void main(String[] args) {
MyCounter1 counter = new MyCounter1("秒表");
Thread thread = new Thread(counter,"壮壮");
Thread thread1 = new Thread(counter,"十八");
thread.start();
thread1.start();
}
}
class MyCounter1 implements Runnable{
private int count = 0;
private String name;
ReentrantLock rl = new ReentrantLock(true);
public MyCounter1(String name) {
this.name = name;
}
public void run() {
rl.lock();
for (int i = 0; i < 50; i++) {
System.out.println(Thread.currentThread().getName()
+"计数:"+i);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
try {
String str = null;
System.out.println(str.length());
} catch (Exception e) {
e.printStackTrace();
} finally {
rl.unlock();
}
}
}
是一个能让线程多次获取锁对象的类型,里面内置了一个计数器,用于记录当前线程获取该锁的次数 能避免死锁情况 该类提供了两个子类型: 非公平锁 :多个线程获取锁的方式不是按照线程的请求顺序,而是可能发生”插队“现象,这种锁性能高,但是可能增加线程解现象 公平锁:按照请求顺序获取,谁都获取到锁,减少了线程接线箱,但是系统的吞吐量可能不高 (性能高的线程也需要派对才能获取锁) 如何使用: 构造器 构造器中传入true,表示传入公平锁,传入false或者不指定参数,使用非公平锁 该类比synchronized更加灵活,但是需要手动上锁和解锁 lock() 上锁方法:锁对象没有被其他线程占用是,会成功,苟泽当前的线程处于阻塞状态 unlock() 必须在占有锁的时候才能进行解锁,否则报异常 trylock() 尝试获取锁,如果获取不到,不阻塞,而是执行其他代码 获取不到锁返回false 获取到返回true trylock(long time,TimeUnit unit) 可以指定一定时间内获取锁对象,如果超市还没获取,返回false 获取返回true
package CSDN.day0822;
import java.util.concurrent.locks.ReentrantLock;
public class _03 {
public static void main(String[] args) {
MyCounter counter = new MyCounter("秒表");
Thread t1 = new Thread(counter,"小明");
Thread t2 = new Thread(counter,"小美");
t1.start();
t2.start();
}
}
class MyCounter implements Runnable{
private int count = 0;
private String name;
ReentrantLock rl = new ReentrantLock(true);
public MyCounter(String name) {
this.name = name;
}
public void run() {
rl.lock();
for (int i = 0; i < 10; i++) {
count++;
System.out.println(Thread.currentThread().getName()
+ ": " + count + " times");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
rl.unlock();
}
}