线程同步:发生在多个线程操作同一个资源时
并发:同一个对象被多个线程同时操作
线程同步的解决方法就是排队
关键字锁机制synchronized,当一个线程获得对象的排他锁,独占资源,其他线程必须等待,使用后释放即可。
弊端:1.会引起性能问题2.会造成优先级高的线程等待优先级低的线程,优先级倒置,引起性能问题。
- 三大不安全案例
不安全的买票
package 线程同步;
public class UnsafeBuyTicket {
public static void main(String[] args) {
BuyTicket buyTicket = new BuyTicket();
new Thread(buyTicket, "xzc").start();
new Thread(buyTicket, "zyj").start();
new Thread(buyTicket, "zsb").start();
}
}
class BuyTicket implements Runnable{
private int ticketnums = 10;
boolean flag = true;//外部停止方式
@Override
public void run() {
//发票
while(flag){
buy();
}
}
private void buy(){
//判断是否有票
if(this.ticketnums<=0){
this.flag = false;
}
//买票
System.out.println(Thread.currentThread().getName() + "拿到" + ticketnums--);
}
}
//可能存在不安全因为多个线程同时抢夺同一个资源(没有排队)
不安全取钱
不安全线程(两个线程同一时间占了同一块内存导致实际创建的线程数总是小于预期创建的线程数)
等等其程序大体如上述代码,都是因为缺少排队机制从而导致线程同一时间争抢同一个资源,最后该资源变为负数,出现错误。
解决方法:对方法提出synchronized关键字,synchronized关键字包括两种用法:synchronized方法和synchronized块。
synchronized方法:public synchronized void method(...){}
synchronized块:synchronized(Obj){}
缺陷:若将一个大的方法申明为synchronized将会影响效率
synchronized锁的对象要是变化的量,需要增删改查的变量。
package 线程同步;
class BuyTicket implements Runnable{
private int ticketnum = 10;
@Override
public void run() {
while(ticketnum>0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
buy();
}
}
public synchronized void buy(){
if(ticketnum>0){
System.out.println(Thread.currentThread().getName() + "拿到第" + ticketnum + "张票");
ticketnum--;
}
}
}
public class UnsafeBuyTicket{
public static void main(String[] args) {
BuyTicket buyTicket = new BuyTicket();
new Thread(buyTicket, "xzc").start();
new Thread(buyTicket, "zyj").start();
new Thread(buyTicket, "zsb").start();
}
}
/*zsb拿到第10张票
xzc拿到第9张票
zyj拿到第8张票
xzc拿到第7张票
zyj拿到第6张票
zsb拿到第5张票
zsb拿到第4张票
xzc拿到第3张票
zyj拿到第2张票
zyj拿到第1张票*/
上述代码用synchronized代码块也可以实现synchronized(this){...}
- 死锁
多个线程各自占有一些共享资源,并且互相等待其他线程占用的资源才能运行,而导致两个或多个线程都在等待对方释放资源,都停止执行的情形。
产生死锁的四个必要条件:
- 互斥条件:资源是独占的且排他使用
- 不可剥夺条件:进程所获得的资源在未使用完毕之前,不被其他进程强行剥夺
- 请求和保持条件:进程每次申请它所需要的一部分资源,在申请新的资源的同时,继续占用已分配到的资源
- 循环等待条件: 若干进程之间形成一种头尾相接的循环等待资源关系
若要避免死锁只需要破坏四个条件中的一个即可
死锁的演示实例
package 线程同步;
public class TestDeadLock {
public static void main(String[] args) {
doing d1 = new doing(0, "xzc");
doing d2 = new doing(1, "zyj");
d1.start();
d2.start();
}
}
class Lip{
}
class Mir{
}
class doing extends Thread{
static Lip lip = new Lip();
static Mir mir = new Mir();
private int choice;
private String name;
public doing(int choice, String name){
this.choice = choice;
this.name = name;
}
@Override
public void run() {
try {
make();
} catch (Exception e) {
e.printStackTrace();
}
}
public void make() throws Exception{
if(choice==0){
synchronized (lip){
System.out.println(name + "拿到了口红");
Thread.sleep(1000);
synchronized (mir){
System.out.println(name + "拿到了镜子");
}
}
}
else{
synchronized (mir){
System.out.println(name + "拿到了镜子");
Thread.sleep(1000);
synchronized (lip){
System.out.println(name + "拿到了口红");
}
}
}
}
}
/*zyj拿到了镜子
xzc拿到了口红*/
解决死锁:
package 线程同步;
public class TestDeadLock {
public static void main(String[] args) {
doing d1 = new doing(0, "xzc");
doing d2 = new doing(1, "zyj");
d1.start();
d2.start();
}
}
class Lip{
}
class Mir{
}
class doing extends Thread{
static Lip lip = new Lip();
static Mir mir = new Mir();
private int choice;
private String name;
public doing(int choice, String name){
this.choice = choice;
this.name = name;
}
@Override
public void run() {
try {
make();
} catch (Exception e) {
e.printStackTrace();
}
}
public void make() throws Exception{
if(choice==0){
synchronized (lip){
System.out.println(name + "拿到了口红");
Thread.sleep(1000);
}
synchronized (mir){
System.out.println(name + "拿到了镜子");
}
}
else{
synchronized (mir){
System.out.println(name + "拿到了镜子");
Thread.sleep(1000);
}
synchronized (lip){
System.out.println(name + "拿到了口红");
}
}
}
}
/*zyj拿到了镜子
xzc拿到了口红
zyj拿到了口红
xzc拿到了镜子*/
- Lock锁
只需要定义调用就行
package 线程同步;
import java.util.concurrent.locks.ReentrantLock;
//定义lock锁
public class TestLock {
public static void main(String[] args) {
lock l = new lock();
new Thread(l).start();
new Thread(l).start();
}
}
class lock implements Runnable{
private int ticketnum = 10;
//定义lock锁
private final ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
while(true){
try{
lock.lock();//加锁
if(ticketnum>0){
System.out.println(ticketnum--);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
else{
break;
}
}finally {
//解锁
lock.unlock();
}
}
}
}
/*
10
9
8
7
6
5
4
3
2
1
*/