多线程安全
public class ThreadsSafe implements Runnable {
static int a = 10000;
/**
* @param args
*/
public static void main(String[] args) {
ThreadsSafe r = new ThreadsSafe();
//开启两个线程,使a减到为0
Thread t1 = new Thread(r);
Thread t2 = new Thread(r);
t1.start();
t2.start();
}
public void run() {
for(int i = 0; i < 5000; i ++){
System.out.println(a--);
}
}
}
运行结果可能会出现
a没有减到0就结束了
这是两个线程抢占资源导致的安全问题,即公共资源j最后没有减到0,这是线程安全问题
通过加锁可以解决问题
synchronized是对象锁,每个对象都有一把锁,当访问公共资源时锁住公共代码块
当有一个线程在执行代码块时,加上锁,其他线程就进不来
卖票实例
//卖票实例
public class TicketDemo implements Runnable {
int ticketNum = 1000;
/**
* @param args
*/
public static void main(String[] args) {
//开启多个线程售卖
TicketDemo r = new TicketDemo();
Thread t1 = new Thread(r);
Thread t2 = new Thread(r);
Thread t3 = new Thread(r);
Thread t4 = new Thread(r);
t1.start();
t2.start();
t3.start();
t4.start();
}
//加锁
public void run() {
//直到卖完票为止
while(true){
if(ticketNum > 0){
try {
Thread.sleep(10);
} catch (Exception e) {
e.printStackTrace();
}
//每卖一次,打印一次
System.out.println(Thread.currentThread() + "..."+ticketNum--);
}
}
}
}
安全问题:如果线程t1在执行run方法时被调度,t2进入run方法里面,当t2执行ticketNum–后ticketNum的值为0,此时线程t1被唤醒继续执行run方法,则继续销售票会销售0号票,ticketNum的值为-1
,此时则为异常情况
解决思路:
当一个线程执行时其他线程不能进来,给代码块加上锁
while(true){
//给代码块加上锁(同步),当有一个线程进入时锁上代码块,其他线程进不来
synchronized (this) {
if(ticketNum > 0){
try {
Thread.sleep(10);
} catch (Exception e) {
e.printStackTrace();
}
//每卖一次,打印一次
System.out.println(Thread.currentThread() + "..."+ticketNum--);
}
}
}
同步的弊端:每个线程进来前会判断代码块是否有线程持有锁,降低了效率
同步的前提:必须保证有多个线程并使用***同一个锁***
同步函数和同步代码块的区别:
同步函数的锁是固定的this
同步代码块的锁是任意的对象
静态的同步函数使用的锁是该函数所属字节码文件对象,可以用this/类名.getClass()获取
写个死锁实例:
package thread1;
public class DeadLockDemo implements Runnable {
private boolean flag;
DeadLockDemo(boolean flag){
this.flag = flag;
}
MyClock clock = new MyClock();
public void run(){
if(flag){
while(true){
synchronized (clock.clocka) {
System.out.println(Thread.currentThread()+"..if.clocka.."+flag);
synchronized (clock.clockb) {
System.out.println(Thread.currentThread()+"..if..clockb."+flag);
}
}
}
}else{
while(true){
synchronized (clock.clockb) {
System.out.println(Thread.currentThread()+"..else.clockb.."+flag);
synchronized (clock.clocka) {
System.out.println(Thread.currentThread()+"..else..clocka."+flag);
}
}
}
}
}
class MyClock{
Object clocka = new Object();
Object clockb = new Object();
}
/**
* @param args
*/
public static void main(String[] args) {
DeadLockDemo d = new DeadLockDemo(true);
Thread t1 = new Thread(d);
Thread t2 = new Thread(d);
t1.start();
try {
Thread.sleep(10);
} catch (Exception e) {
e.printStackTrace();
}
d.flag = false;
t2.start();
}
}
互相等待对方释放锁导致死锁
线程间通讯
多个线程在处理同一资源,但任务却不同
案例1:开启多个生产者消费者同时进行生产和消费,要求每生产一个消费一个
package thread1;
//生产烤鸭的工厂类
class Product{
private int num = 0;//烤鸭编号
private boolean flag = false;//标志是否已有烤鸭
//生产烤鸭
public synchronized void set(){
if(flag){
try {
this.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
num++;
System.out.println(Thread.currentThread() +"....生产了烤鸭"+num);
flag = true;
this.notify();
}
//出售烤鸭
public synchronized void get(){
if(!flag){
try {
this.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.println(Thread.currentThread()+"出售了烤鸭"+num);
flag = false;
this.notify();
}
}
//生产者
class Producer implements Runnable{
Product p;
Producer(Product p) {
this.p = p;
}
public void run() {
while(true){
p.set();
}
}
}
//消费者
class Customer implements Runnable{
Product p;
Customer(Product p) {
this.p = p;
}
public void run() {
while(true){
p.get();
}
}
}
public class DuckDemo {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
Product p = new Product();
Producer p0 = new Producer(p);
Producer p1 = new Producer(p);
Customer c0 = new Customer(p);
Customer c1 = new Customer(p);
Thread t0 = new Thread(p0);
Thread t1 = new Thread(p1);
Thread t2 = new Thread(c0);
Thread t3 = new Thread(c1);
t0.start();
t1.start();
t2.start();
t3.start();
}
}
会出现生产者生产之后没有被消费的问题
如果把if改为while,会出现死锁的问题,
while(flag){
try {
this.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
因为notify()每次唤醒的是线程池中随机的其中一个线程,因此当出现生产者线程唤醒另一个生产者线程,两个线程都进行等待的情况,就会陷入死锁,因此把notify()方法改为notifyAll(),即唤醒此对象线程池中所有的线程
程序正常运行
jdk1.5以后将同步和锁封装成了对象
并将操作锁的隐式方式定义到了该对象中,
将隐式动作变成了显示动作
Lock接口
替代了同步代码块或者同步函数,将同步的隐式锁操作变成现实所操作,同时更为灵活,可以一个锁上加上多组监视器
lock():获取锁
unlock():释放锁
package thread1;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
//生产烤鸭的工厂类
class Product{
private int num = 0;//烤鸭编号
private boolean flag = false;//标志是否已有烤鸭
Lock lock = new ReentrantLock();
Condition pro_con = lock.newCondition();
Condition cons_con = lock.newCondition();
//生产烤鸭
public void set(){
lock.lock();
try{
while(flag){
try {
pro_con.await();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
num++;
System.out.println(Thread.currentThread() +"....生产了烤鸭"+num);
flag = true;
}finally{
cons_con.signal();
}
lock.unlock();
}
//出售烤鸭
public synchronized void get(){
lock.lock();
try{
while(!flag){
try {
cons_con.await();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.println(Thread.currentThread()+"出售了烤鸭"+num);
flag = false;
}finally{
pro_con.signal();
}
lock.unlock();
}
}
//生产者
class Producer implements Runnable{
Product p;
Producer(Product p) {
this.p = p;
}
public void run() {
while(true){
p.set();
}
}
}
//消费者
class Customer implements Runnable{
Product p;
Customer(Product p) {
this.p = p;
}
public void run() {
while(true){
p.get();
}
}
}
public class DuckDemo {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
Product p = new Product();
Producer p0 = new Producer(p);
Producer p1 = new Producer(p);
Customer c0 = new Customer(p);
Customer c1 = new Customer(p);
Thread t0 = new Thread(p0);
Thread t1 = new Thread(p1);
Thread t2 = new Thread(c0);
Thread t3 = new Thread(c1);
t0.start();
t1.start();
t2.start();
t3.start();
}
}