多线程锁
JAVA(多线程)
一,锁
1. synchronized 多线程并发编程
synchronized 三种方式来加锁:
- 修饰静态方法,作用于当前类对象加锁,进入同步代码之前要获得当前类对象的锁。形象的比喻就是,对于这个对象可能有很多方法(房间),有一些加了锁(锁门的房间),而这些房间共用一把钥匙,导致当一个被锁方法被访问时,无法访问其他带锁的方法。
class Product{
static volatile Integer n=10;
static volatile boolean f=false;
//生产
public synchronized void put() throws InterruptedException {//加锁,wait默认是Object的,枷锁后是锁里的
if(f){
try{
wait();
} catch (InterruptedException e){
e.printStackTrace();
}
}
for (int i = 1; i <= 3; i++) {
Thread.sleep(500);
this.n=i;
System.out.println("a->"+n);
}
f=true;
notify();
}
//消费
public synchronized void get() throws InterruptedException {
if(!f){
try{
wait();
} catch (InterruptedException e){
e.printStackTrace();
}
}
for (int i = n; i >= 0; i--) {
Thread.sleep(500);
this.n=i;
System.out.println("b->"+n);
}
f=false;
notify();
}
}
- 修饰成员方法,作用于当前实例加锁,进入同步代码前要获得当前实例的锁()
- 代码块,指定加锁对象,对给定向对象加锁,进入同步代码块之前要获得给指定对象的锁
1. 实例方法:调用该方法的实例
2. 静态方法:类对象
3. this:调用该方法的实例对象
4. 类对象:类对象
//同步代码块
//创建一个对象
//类对象
//当前实例this
//同步监视器
synchronized(类名.class ){
}
操作共享数据的代码
共享数据:多个线程共同操作
案例:
package duoxiancheng.suo;
/**
* 当使用同步方法,synchronized锁的是this
* <p>
* 同步方法:
* 1.同步方法依然涉及到同步锁对象,不需要我们写
* 2.非静态同步方法,同步锁就是this
* 静态的同步方法,同步监视器就是类本身
* <p>
* 同步代码块:
* 1.选好同步监视器(锁)推荐使用类对象,第三方,this
* 2.在实现接口创建的线程类中,同步代码块不能用this充当同步锁
* <p>
* synchronized只针对当前jvm可以解决线程安全问题
* synchronized不能跨jvm解决问题
*/
public class SellTicktDemo implements Runnable {
/*
* 需求:多线程模拟售票,多个窗口售票
*
* 分析:
* A.需要的类
* 1.建立一个多线程的类SellTicktDemo
* 2.创建一个测试类SellTicktDemoTest
* B.类的关系
* 1.多线程的类SellTicktDemo,实现Runnable接口,重写run()方法
* 2.SellTicktDemoTest 测试多线程类
* C.实现多线程同步
* 1.用synchronized()方法实现线程同步
* D.在SellTicktDemoTest中实现多数窗口
*
*/
//定义票的总数
private int total = 100;
//定义票的编号
private int no = total + 1;
//定义一个线程同步对象
private Object obj = new Object();
@Override
public void run() {
while (true) {
//同步锁
synchronized (this.obj) {
if (this.total > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
String msg = Thread.currentThread().getName() + " 售出第 " + (this.no - this.total) + " 张票";
System.out.println(msg);
this.total--;
} else {
System.out.println("票已售完,请下次再来!");
System.exit(0);
}
}
}
}
}
package duoxiancheng.suo;
public class Test10 {
public static void main(String[] args) {
//得到对象
SellTicktDemo std = new SellTicktDemo();
//把对象放入线程中
Thread t1 = new Thread(std,"售票窗口1");
Thread t2 = new Thread(std,"售票窗口2");
Thread t3 = new Thread(std,"售票窗口3");
Thread t4 = new Thread(std,"售票窗口4");
t1.start();
t2.start();
t3.start();
t4.start();
}
}
二,死锁
多个线程同时被堵塞,他们一个或多个都在等待某个资源的释放,由于线程无限期的堵塞,程序不能正常终止
java死锁产生四个条件
1. 互斥条件,当资源被一个线程使用,别的线程不能使用
2. 不可抢占,资源请求者不能强制从占有者中抢夺资源,资源只能从占有者手动释放
3. 请求和保持
4. 循环等待,存在一个等待队列,p1占有p2的资源,p2占有p3的资源,p3占有p1的资源,形成了一个等待环路
案例
package duoxiancheng.suo;
import java.util.Date;
class LockA implements Runnable{
@Override
public void run() {
System.out.println(new Date().toString()+"beging");
while (true){
synchronized (Test11.obj1){
System.out.println(new Date().toString()+"obj1beging");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
synchronized (Test11.obj2){
System.out.println(new Date().toString()+"obj2beging");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
class LockB implements Runnable{
@Override
public void run() {
System.out.println(new Date().toString()+"beging");
while (true){
synchronized (Test11.obj2){
System.out.println(new Date().toString()+"obj2beging");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
synchronized (Test11.obj1){
System.out.println(new Date().toString()+"obj1beging");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
public class Test11 {
public static String obj1="obj1";
public static String obj2="obj2";
public static void main(String[] args) {
LockA lockA=new LockA();
new Thread(lockA).start();
LockB lockB=new LockB();
new Thread(lockB).start();
}
}
三,线程重入
任意线程拿到锁之后,再次获取该锁不会被该锁锁阻碍
线程不会被自己锁死,synchronized可重入锁
public class Test12 {
private static final Object M1=new Object();
private static final Object M2=new Object();
public static void main(String[] args) {
new Thread(
()-> {
synchronized (M1){
synchronized (M2){
synchronized (M1){
synchronized (M2){
System.out.println("hello lock");
}
}
}
}
}
).start();
}
}
JDK1.6之后:
无锁:不加锁
偏向锁:不锁锁,只有一个线程争夺时,偏向某一个线程,这个线程不加锁
轻量级锁:少量线程来了之后,尝试自旋,不挂起线程
重量级锁:排队挂起线程(synchronzied)
挂起线程和恢复线程需要转入内核态中完成这些操作,给系统的并发性带来很大压力
四,Object类对多线程的支持
wait()
wait(long timeout)当线程进入等待状态
notify()唤醒正在等待的下一个线程
notifyAll()唤醒正在等待的所有线程
五,线程间的通信
package duoxiancheng.suo;
public class Test13 {
private static int num=10;
private static final Object OBJ=new Object();
public static void plus(int code,int i){
synchronized (OBJ){
if(num >= 10){
//唤醒其他等待的线程
// OBJ.notify();
//唤醒所有等待的线程
OBJ.notifyAll();
}
System.out.println("这是线程"+code+"->"+ ++num + "->" + i);
}
}
public static void sub(int code,int i) throws InterruptedException {
synchronized (OBJ){
if(num <= 0){
OBJ.wait();
}
System.out.println("这是线程"+code+"->"+ --num + "->" + i);
}
}
public static void main(String[] args) {
Thread t1=new Thread(()->{
for (int i = 0; ; i++) {
try {
Thread.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
sub(1,i);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
Thread t2=new Thread(()->{
for (int i = 0; ; i++) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
plus(2,i);
}
});
t1.start();
t2.start();
}
}
Thread的两个静态方法
sleep释放CPU资源,但不会释放锁
yield方法释放CPU执行权,保留了CPU的执行资格
join方法,让出了执行权,join就加入进来
wait:释放CPU资源,释放锁
题:sleep和wait的区别
1. 出处:
sleep 一般用于当前线程休眠,或者轮循暂停操作,Thread
wait 则多用于多线程之间的通信,Object
2.锁的控制:
sleep释放CPU资源,但不会释放锁
wait释放CPU资源,释放锁
3.使用:
使用 sleep 方法可以让让当前线程休眠,时间一到当前线程继续往下执行,在任何地方都能使用,但需要捕获 InterruptedException 异常
sleep 一般用于当前线程休眠,或者轮循暂停操作
wait 则多用于多线程之间的通信。
4.线程切换:
sleep 会让出 CPU 执行时间且强制上下文切换,而 wait 则不一定,wait 后可能还是有机会重新竞争到锁继续执行的。
守护线程和用户线程
- 守护线程依赖于创建它的线程,而用户线程则不依赖。
- 如果在main线程中创建了一个守护线程,当main方法运行完毕之后,守护线程也会随着消亡。
- main方法执行完毕后,用户线程则不会消亡,用户线程会一直运行直到其运行完毕。在JVM中,像垃圾收集器线程就是守护线程。
案例:生产者与消费者模型
2个线程,一条线程生产产品,另一条线程消费产品
六,线程的退出
使用线程标志,线程正常退出
interrupt方法,中断线程,会抛出异常,捕获后再做停止线程的逻辑
七,线程常用方法
1. start:启动当前线程,启动run方法
2. run
3. currentThread:静态方法,获取正在执行的线程
4. getId() :返回此线程的唯一标识
5. getName():设置当前线程name
6. getName():获取当前线程name
7. getPriority():获取当前线程优先级
8. setPriority():设置当前线程优先级
9. getState() :获取当前线程的生命周期
10. interrupt():终端线程的执行
11. interrupted():查看当前线程是否中断
12. isDaemon():是否是守护线程
懒汉单例的改进
单例推荐内部类,枚举(天生构造器私有化)
public class Test1 {
private Test1(){}
private static Test1 INSTANT;
public static Test1 getInstance(){
if(INSTANT == null){
synchronized (Test1.class) {
if(INSTANT == null){
INSTANT = new Test1();
}
}
}
return INSTANT;
}
}
枚举单一用例
class Singleton {
private Singleton(){
}
public static enum SingletonEnum {
SINGLETON;
private Singleton instance = null;
private SingletonEnum(){
instance = new Singleton();
}
public Singleton getInstance(){
return instance;
}
}
}
public class Test4 {
public static void main(String[] args) {
Singleton s1=Singleton.SingletonEnum.SINGLETON.getInstance();
}
}

浙公网安备 33010602011771号