/*
两个储户,到同一个银行存钱,每个人存了3次,一次100元。
1,描述银行。
2,描述储户任务。
分析多线程是否存在安全隐患。
1,线程任务中是否有共享的数据。
2,是否多条操作共享数据的代码。
同步函数。其实就是在函数上加上了同步关键字进行修饰。
同步表现形式有两种:1,同步代码块,2,同步函数。
同步函数使用的锁是什么呢?函数需要被对象调用,哪个对象不确定,但是都用this来表示。
同步函数使用的锁就是this。
*/
class Bank
{
private int sum;
// private Object obj = new Object();
public synchronized void add(int n)
{
// synchronized(obj)
// {
sum = sum + n;
try{Thread.sleep(10);}catch(Exception e){}
System.out.println("sum="+sum);
// }
}
}
class Customer implements Runnable
{
private Bank b = new Bank();
public void run()
{
for(int x=0; x<3; x++)
{
b.add(100);
}
}
}
class BankDemo
{
public static void main(String[] args)
{
//1,创建任务对象。
Customer c = new Customer();
Thread t1 = new Thread(c);
Thread t2 = new Thread(c);
t1.start();
t2.start();
}
}
/*
验证同步函数使用的锁是this。
验证需求:
启动两个线程。
一个线程负责执行同步代码块(使用明锁)。
另一个线程使用同步函数(使用this锁)。
两个执行的任务是一样的都是卖票。如果他们没有使用相同的锁,说明他们没有同步。会出现数据错误。
怎么能让一个线程一直在同步代码块中,一个线程在同步函数呢?
可以通过切换的方式。
*/
class SaleTicket implements Runnable
{
private int tickets = 100;
//定义一个boolean标记。
boolean flag = true;
Object obj = new Object();
public void run()
{
if(flag)
while(true)
{
synchronized(this)
{
if(tickets>0)
{
try{Thread.sleep(10);}catch(InterruptedException e){}//让线程到这里稍微停一下。
System.out.println(Thread.currentThread().getName()+"....code....."+tickets--);
}
}
}
else
while(true)
sale();
}
public synchronized void sale()
{
if(tickets>0)
{
try{Thread.sleep(10);}catch(InterruptedException e){}//让线程到这里稍微停一下。
System.out.println(Thread.currentThread().getName()+".....func...."+tickets--);
}
}
}
class ThisLockDemo
{
public static void main(String[] args) throws InterruptedException
{
SaleTicket t = new SaleTicket();
Thread t1 = new Thread(t);
Thread t2 = new Thread(t);
t1.start();
Thread.sleep(10);
t.flag = false;
t2.start();
}
}
/*
如果同步函数被static修饰呢?
static方法随着类加载,这时不一定有该类的对象。但是一定有一个该类的字节码文件对象。
这个对象简单的表示方式就是 类名.class Class
*/
class SaleTicket implements Runnable
{
private static int tickets = 100;
//定义一个boolean标记。
boolean flag = true;
Object obj = new Object();
public void run()
{
if(flag)
while(true)
{
synchronized(SaleTicket.class)
{
if(tickets>0)
{
try{Thread.sleep(10);}catch(InterruptedException e){}//让线程到这里稍微停一下。
System.out.println(Thread.currentThread().getName()+"....code....."+tickets--);
}
}
}
else
while(true)
sale();
}
public static synchronized void sale()
{
if(tickets>0)
{
try{Thread.sleep(10);}catch(InterruptedException e){}//让线程到这里稍微停一下。
System.out.println(Thread.currentThread().getName()+".....func...."+tickets--);
}
}
}
class StaticLockDemo
{
public static void main(String[] args) throws InterruptedException
{
SaleTicket t = new SaleTicket();
Thread t1 = new Thread(t);
Thread t2 = new Thread(t);
t1.start();
Thread.sleep(10);
t.flag = false;
t2.start();
}
}
//饿汉式。相对与多线程并发,安全!
class Single
{
private static final Single SINGLE_INTSTANCE = new Single();
private Single(){}
public static Single getInstance()
{
return SINGLE_INTSTANCE;
}
}
//懒汉式。延迟加载模式。
/*
在多线程并发访问时,会出现线程安全问题。
加了同步就可以解决问题。无论是同步函数,还是同步代码块都行。
但是,效率低了。
怎么解决效率低的问题。
可以通过if对单例对象的双重判断的形式。哦耶!
*/
class Single
{
private static Single s = null;
private Single(){}
public static Single getInstance()
{
if(s==null)
{
synchronized(Single.class)
{
if(s==null)
//-->0
s = new Single();
}
}
return s;
}
}
class Demo implements Runnable
{
public void run()
{
Single.getInstance();
}
}
class ThreadSingleTest
{
public static void main(String[] args)
{
System.out.println("Hello World!");
}
}
/*
同步函数,同步代码块。
同步代码块使用的任意的对象作为锁。
同步函数只能使用this作为锁。
如果说:一个类中只需要一个锁,这时可以考虑同步函数,使用this,写法简单。
但是,一个类中如果需要多个锁,还有多个类中使用同一个锁,这时只能使用同步代码块。
建议使用同步代码块。
*/
class
{
public static void main(String[] args)
{
System.out.println("Hello World!");
}
}
/*
死锁:
场景一:
同步嵌套。
*/
class SaleTicket implements Runnable
{
private int tickets = 100;
//定义一个boolean标记。
boolean flag = true;
Object obj = new Object();
public void run()
{
if(flag)
while(true)
{
synchronized(obj)//obj lock
{
sale();
}
}
else
while(true)
sale();
}
public synchronized void sale()//this lock
{
synchronized(obj)
{
if(tickets>0)
{
try{Thread.sleep(10);}catch(InterruptedException e){}//让线程到这里稍微停一下。
System.out.println(Thread.currentThread().getName()+".....func...."+tickets--);
}
}
}
}
class DeadLockDemo
{
public static void main(String[] args) throws InterruptedException
{
SaleTicket t = new SaleTicket();
Thread t1 = new Thread(t);
Thread t2 = new Thread(t);
t1.start();
Thread.sleep(10);
t.flag = false;
t2.start();
}
}
/*
多线程间的通信。多个线程都在处理同一个资源,但是处理的任务却不一样。
生产者,消费者。
通过同步,解决了没生产就消费的问题。
*/
//描述资源。
class Res
{
private String name;
private int count = 1;
//提供了给商品赋值的方法。
public synchronized void set(String name)
{
this.name = name + "--" + count;
count++;
System.out.println(Thread.currentThread().getName()+"...生产者....."+this.name);
}
//提供一个获取商品的方法。
public synchronized void get()
{
System.out.println(Thread.currentThread().getName()+".......消费者....."+this.name);
}
}
//生成者。
class Producer implements Runnable
{
private Res r;
Producer(Res r)
{
this.r = r;
}
public void run()
{
while(true)
r.set("面包");
}
}
//消费者
class Consumer implements Runnable
{
private Res r;
Consumer(Res r)
{
this.r = r;
}
public void run()
{
while(true)
r.get();
}
}
class ProducerConsumerDemo
{
public static void main(String[] args)
{
//1,创建资源。
Res r = new Res();
//2,创建两个任务。
Producer pro = new Producer(r);
Consumer con = new Consumer(r);
//3,创建线程。
Thread t1 = new Thread(pro);
Thread t2 = new Thread(con);
t1.start();
t2.start();
}
}
![]()
//同步嵌套,死锁。
class Task implements Runnable
{
private boolean flag;
Task(boolean flag)
{
this.flag = flag;
}
public void run()
{
if(flag)
{
while(true)
synchronized(MyLock.LOCKA)
{
System.out.println("if.....locka");
synchronized(MyLock.LOCKB)
{
System.out.println("if.....lockb");
}
}
}
else
{
while(true)
synchronized(MyLock.LOCKB)
{
System.out.println("else.....lockb");
synchronized(MyLock.LOCKA)
{
System.out.println("else.....locka");
}
}
}
}
}
class MyLock
{
public static final Object LOCKA = new Object();
public static final Object LOCKB = new Object();
}
class DeadLockTest
{
public static void main(String[] args)
{
//创建线程任务。
Task t1 = new Task(true);
Task t2 = new Task(false);
new Thread(t1).start();
new Thread(t2).start();
}
}
/*
多线程间的通信。多个线程都在处理同一个资源,但是处理的任务却不一样。
生产者,消费者。
通过同步,解决了没生产就消费的问题。
但是出现了连续的生产没有消费的情况,和需求生产一个,消费一个的情况不符。
使用了等待唤醒机制。
wait():该方法可以让线程处于冻结状态,并将线程临时存储到线程池中。
notify():唤醒指定线程池中的任意一个线程。
notifyAll():唤醒指定线程池中的所以线程。
这些方法必须使用在同步中,因为它们用来操作同步锁上的线程的状态的。
在使用这些方法时,必须标识它们所属于的锁。标识方式就是 锁对象.wait(); 锁对象.notify(); 锁对象.notifyAll();
相同锁的notify(),可以获取相同锁的wait();
*/
//描述资源。
class Res
{
private String name;
private int count = 1;
//定义标记。
private boolean flag;
//提供了给商品赋值的方法。
public synchronized void set(String name)
{
if(flag)//判断标记为true,执行wait。等待。为false。就生产。
try{this.wait();}catch(InterruptedException e){}
this.name = name + "--" + count;
count++;
System.out.println(Thread.currentThread().getName()+"...生产者....."+this.name);
//生成完毕,将标记改为true。
flag = true;
//唤醒消费者。
this.notify();
}
//提供一个获取商品的方法。
public synchronized void get()
{
if(!flag)
try{this.wait();}catch(InterruptedException e){}
System.out.println(Thread.currentThread().getName()+".......消费者....."+this.name);
//将标记改为false。
flag = false;
//唤醒生产者。
this.notify();
}
}
//生成者。
class Producer implements Runnable
{
private Res r;
Producer(Res r)
{
this.r = r;
}
public void run()
{
while(true)
r.set("面包");
}
}
//消费者
class Consumer implements Runnable
{
private Res r;
Consumer(Res r)
{
this.r = r;
}
public void run()
{
while(true)
r.get();
}
}
class ProducerConsumerDemo2
{
public static void main(String[] args)
{
//1,创建资源。
Res r = new Res();
//2,创建两个任务。
Producer pro = new Producer(r);
Consumer con = new Consumer(r);
//3,创建线程。
Thread t1 = new Thread(pro);
Thread t2 = new Thread(con);
t1.start();
t2.start();
}
}
/*
多生产多消费。
问题1:
重复生成,重复消费。
原因:经过复杂的(等,资格)分析,发现被唤醒的线程没有判断标记就开始工作(生成or消费)了。
导致了重复的生成和消费的发生。
解决:那就是被唤醒的线程必须判断标记。
使用while循环搞定。
问题2:
死锁了。所有的线程都处于冻结状态。
原因:本方线程在唤醒时,又一次唤醒了本方线程。而本方线程循环判断标记,又继续等待。
而导致所有的线程都等待了。
解决;希望本方如果唤醒了对方线程,就可以解决。
可以使用notifyAll()方法。
那不是全唤醒了吗?是的。既有本方,又有对方。但是本方醒后,会判断标记继续等待。
这样对方就有线程可以执行了。
已经实现了多生产多消费。
但是有些小问题,效率有点低,因为notifyAll也唤醒了本方。做了不必要的判断。
*/
//描述资源。
class Res
{
private String name;
private int count = 1;
//定义标记。
private boolean flag;
//提供了给商品赋值的方法。
public synchronized void set(String name)//
{
while(flag)//判断标记为true,执行wait。等待。为false。就生产。
try{this.wait();}catch(InterruptedException e){}//t0(等) t1(等)
this.name = name + "--" + count;//面包1 ,面包2 面包3
count++;//2 3 4
System.out.println(Thread.currentThread().getName()+"...生产者....."+this.name);//t0 面包1、 t0 面包2 t1 ,面包3
//生成完毕,将标记改为true。
flag = true;
//唤醒所有等待线程(包括本方线程)。
this.notifyAll();
}
//提供一个获取商品的方法。
public synchronized void get()//
{
while(!flag)
try{this.wait();}catch(InterruptedException e){}//t2(等) t3(等)
System.out.println(Thread.currentThread().getName()+".......消费者....."+this.name);//t2 面包1.
//将标记改为false。
flag = false;
//唤醒所有等待线程(包括本方线程)。
this.notifyAll();
}
}
//生成者。
class Producer implements Runnable
{
private Res r;
Producer(Res r)
{
this.r = r;
}
public void run()
{
while(true)
r.set("面包");
}
}
//消费者
class Consumer implements Runnable
{
private Res r;
Consumer(Res r)
{
this.r = r;
}
public void run()
{
while(true)
r.get();
}
}
class ProducerConsumerDemo3
{
public static void main(String[] args)
{
//1,创建资源。
Res r = new Res();
//2,创建两个任务。
Producer pro = new Producer(r);
Consumer con = new Consumer(r);
//3,创建线程。
Thread t0 = new Thread(pro);
Thread t1 = new Thread(pro);
Thread t2 = new Thread(con);
Thread t3 = new Thread(con);
t0.start();
t1.start();
t2.start();
t3.start();
}
}
/*
1,搞定妖的问题。
2,name和sex是私有的。需要在Res类中对外提供访问name和sex的方法。这个可以参照生产者消费者producerconsumerdemo.java
3,实现间隔输出,使用等待唤醒机制。producerconsumerdemo2.java
*/
class Res
{
String name;
String sex;
}
class Input implements Runnable
{
private Res r;
Input(Res r)
{
this.r = r;
}
public void run()
{
int x = 0;
while(true)
{
if(x==0)
{
r.name = "张三";
r.sex = "男男男男男男男";
}
else
{
r.name = "rose";
r.sex = "women";
}
x = (x+1)%2;
}
}
}
class Output implements Runnable
{
private Res r;
Output(Res r)
{
this.r = r;
}
public void run()
{
while(true)
{
System.out.println(r.name+"...."+r.sex);
}
}
}
class Test
{
public static void main(String[] args)
{
Res r = new Res();
Input in = new Input(r);
Output out = new Output(r);
new Thread(in).start();
new Thread(out).start();
}
}