刚开始学多线程也是有点点蒙的感觉,就是学了就忘,因为本人还没毕业,大学的时候都是自学的,建议刚开始先理解再看视频或资料
一些定义:
1.JVM中的多线程解析
JVM启动时就启动了多个线程,至少有两个线程可以分析的出来。为主线程和垃圾回收的线程(gc)
多线程创建的方式一继承Tread类创建新执行线程有两种方法。一种方法是将类声明为Thread的子类。
该子类重写Thread类的run方法。接下来可以分配并启动该子类的实例。
步骤:
1.定义一个类继承Thread类。
2.覆盖Thread类中的run方法。
3.直接创建Thread的子类对象创建线程。
4.调用start方法开启线程并调用线程的run方法执行。
实例一:class Demo extends Thread{
private String name;
Demo(String name){
this.name=name;
}
public void run(){
for(int x=0;x<10;x++){
for(int y=-999999;y<999999999;y++){}
System.out.println(name+"......x="+x);
}
}
public void show(){
}
}
class ThreadDemo2{
public static void main(String[]args){
/*
创建线程的目的是为了开启一条执行路径,去运行指定的代码和其他代码实现同时运行。
而运行的指定代码就是这个执行路径的任务。
jvm创建的主线程的任务都定义在了主函数中。
而自定义的线程它的任务在哪儿呢?
Thread类用于描述线程,线程是需要任务的。所以Tread类也 对任务的描述。
这个任务就是通过Thread类中觉得方法体现。也就是说,run方法就是封装自定义线程运行任务的函数。
run方法中定义就是线程要运行的任务代码。
开启线性是为了运行指定代码,所以只有继承Thread类,并复写run方法。
将运行的代码定义在run方法即可。
*/
Demo d1=new Demo("旺财");
Demo d2=new Demo("xiaoqiang");
//d1.run();
d1.start();//开启线程,调用run方法。
System.out.println("haha");
//d2.run();
d2.start();//开启线程
}
问:
调用run和调用start有什么区别?
调用run方法是跟java语法中调用方法没什么区别,调用start方法则是随机执行run方法(大概的理解)创建线程的第二种方式-实现Runnable接口
创建线程的第二种方式:实现Runnable接口。
1.定义类实现Runnable接口。
2.覆盖接口中的run方法,将线程的任务代码封装到run方法中。
3.通过Thread类创建线程对象,并将Runnable接口的子类对象作为构造函数的参数进行传递。
为什么?因为线程的任务都封装在Runnable接口子类对象的run方法中。
所以要在线程对象创建时就必须明确要运行的任务。
4.调用线程对象的start方法开启线程。
实例二:class Demo extends Thread{
public void run(){
for(int x=0;x<10;x++){
System.out.println(Thread.currentThread().getName()+"....."+x);
}
}
}
class ThreadDemo{
public static void main(String[]args){
Demo d1=new Demo();
Demo d2=new Demo();
d1.start();
d2.start();
}
}
实例三:
class Demo implements Runnable ( extends Fu){ //准备扩展Demo类的功能,让其的内容可以作为线程的任务执行。
//通过接口的形式完成。
public void run(){
show();
}
public void show(){
for(int x=0;x<10;x++){
System.out.println(Thread.currentThread().getName()+"....."+x);
}
}
}
class ThreadDemo{
public static void main(String[]args){
Demo d=new Demo();
Thread t1=new Thread(d);
Thread t2=new Thread(d);
t1.start();
t2.start():
// Demo d1=new Demo();
// Demo d2=new Demo();
// d1.start();
// d2.start();
}
}
实例四:
第二种方式的细节
class Thread{
private Runnnable r;
Tread(){ }
Tread(Runnable r){
this.r=r;
}
public void run(){
if(r!=null)
r.run();
}
public void start(){
run();
}
}
class ThreadImpl implements Runnable{
public void run(){
System.out.println("runnable run");
}
}
ThreadImpl i=new ThreadImpl();
Thread t=new Thread(i);
t.start();
class SubThread extends Thread{
public void run(){
System.out.println("haha");
}
}
//SubTread s=new SubThread();
//s.start();
第二种方式的好处
实现Runable接口好处。
1.将线程的任务从线程的子类中分离出来,进行了单独的封装。
按照面向对象的思想将任务的封装成对象。
2.避免了java单继承的局限性。
所以,创建线程的第二个方式较为常用。
实例五:买票示例
class Ticket implement Runnable//extends Thread
{
private( static )int num=100;
public void run()
{
while(true)
{
if(num>0)
{
System.out.println(Tread.currentThread().getName()+"--------sale-----------"+num--);
}
}
}
}
class TicketDemo
{
public static void main(String[ ] args)
{
Ticket t=new Ticket();//创建一个线程对象。
Thread t1=new Thread(t);
Thread t2=new Thread(t);
Thread t3=new Thread(t);
Thread t4=new Thread(t);
t1.start();
t2.start();
t3.start();
t4.start();
Ticket t1=new Ticket();
Ticket t2=new Ticket();
Ticket t3=new Ticket();
Ticket t4=new Ticket();
t1.start();
t2.start();
t3.start();
t4.start();
}
}
线程安全问题的现象 public void run()
{
while(true)
{
if(num>0)
{
try
{Thread.sleep(10);}
catch(InterruptedException e){}
System.out.println(Tread.currentThread().getName()+"--------sale-----------"+num--);
}
}
}
}
线程安全问题产生的原因
1.多个线程在操作共享的数据。
2.操作共享数据线程代码有多条。
当一个线程在执行操作共享数据的多条代码过程中,
其他线程参与的运算。就会导致线程安全性问题的产生。
同步代码块
解决思路:
就是将多条操作共享数据的线程封装起来,当有线程执行这些代码的时候,
其他线程不可以参与运算的。必须要当前线程把这些代码都执行完毕后,
其他线程才可以参与运算。
在java中,用同步代码块就可以解决这个问题。
同步代码块的格式:
synchronized(对象)
{
需要被同步的代码;
}
class Ticket implement Runnable//extends Thread
{
private int num=100;
Object obj=new Object();
public void run()
{
while(true)
{
synchronized(obj)
{
if(num>0)
{
try(Thread.sleep(10);)
}
catch(InterruptedException e){}
System.out.println(Tread.currentThread().getName()+"--------sale-----------"+num--);
}
}
}
}
class TicketDemo
{
public static void main(String[ ] args)
{
Ticket t=new Ticket();//创建一个线程对象。
Thread t1=new Thread(t);
Thread t2=new Thread(t);
Thread t3=new Thread(t);
Thread t4=new Thread(t);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
同步的好处和弊端
同步好处:解决了线程的安全问题。
同步弊端:相对降低了效率,因为同步外的线程的都会判断同步锁。
同步的前提:
同步的前提:同步中必须有多个线程并使用同一个锁。
同步函数
/*
需求:储户,两个,每个都到银行存钱每次存100,共存三次。
*/
class Bank
{
private int sum;
public synchronized void add(int num)//同步函数
{
sum=sum+num;
System.out.println("sum="+sum);
}
}
class Cus 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)
{
Cus c=new Cus();
Thread t1=new Thread(c);
Thread t2=new Thread(c);
t1.start();
t2.start();
}
}
.验证同步函数的锁
同步函数的锁是this。
同步函数和同步代码块的区别:
同步函数的锁是固定的this,
同步代码块的锁是任意的对象。
建议使用同步代码块。
验证静态同步函数的锁
静态的同步函数使用的锁是 该函数所属字节码文件对象 可以用getClass方法获取。
也可以用当前 类名.class 表示。
class Ticket implement Runnable
private static int num=400;
// Object obj=new Object();
boolean flag=true;
public void run()
{
// System.out.println("this:"+this);
if(flag)
while(true)
{
synchronized(Ticket.class)//(this);
{
if(num>0)
{
try{Thread.sleep(10);}catch(InterruptedException e){}
System.out.println(Tread.currentThread().getName()+"--------obj-----------"+num--);
}
}
}
else
while(true)
show();
}
public static synchronized void show()
{
if(num>0)
{
try{Thread.sleep(10);}catch(InterruptedException e){}
System.out.println(Tread.currentThread().getName()+"--------funtion-----------"+num--);
}
}
}
class SynFuntionLockDemo
{
public static void main(String[ ] args)
{
Ticket t=new Ticket();
Class clazz=t.getClass();
Class clazz=Ticket.class; //获取字节码文件对象
// System.out.println("t:"+t);
Thread t1=new Thread(t);
Thread t2=new Thread(t);
t1.start();
try{Thread.sleep(10);}catch(InterruptedException e){}
t.flag=false;
t2.start();
}
}
单例模式涉及的多线程问题
/*
多线程下的单例
*/
//饿汉式
class Single
{
private static final Single s=new Single();
private Single(){}
public static Single getInstance()
{
return s;
}
}
//懒汉式(面试)
class Single
{
private static Single s=null;
private Single(){}
public static Single getInstance()
{
if(s==null)//多加一次判断是为了解决效率问题
{
synchronized (Single.class)//加线程锁是为了解决安全问题
{
if(s==null)
s=new Single();
}
}
return s;
}
}
class SingleDemo
{
public static void main(String[]args)
{
}
}
死锁示例
/*
死锁:常见情景之一:同步嵌套。
*/
class Ticket implement Runnable
private int num=100;
Object obj=new Object();
boolean flag=true;
public void run()
{
if(flag)
while(true)
{
synchronized(obj);
{
if(num>0)
{
try{Thread.sleep(10);}catch(InterruptedException e){}
System.out.println(Tread.currentThread().getName()+"--------obj-----------"+num--);
}
}
}
else
while(true)
show();
}
public synchronized void show()
{
synchronized(obj)
if(num>0)
{
try{Thread.sleep(10);}catch(InterruptedException e){}
System.out.println(Tread.currentThread().getName()+"--------funtion-----------"+num--);
}
}
}
class DeadLockDemo
{
public static void main(String[ ] args)
{
Ticket t=new Ticket();
Thread t1=new Thread(t);
Thread t2=new Thread(t);
t1.start();
try{Thread.sleep(10);}catch(InterruptedException e){}
t.flag=false;
t2.start();
}
}
****************************************************
class Test implements Runnable
{
private boolean flag;
Test(boolean flag)
{
this.flag=flag;
}
public void run()
{
if(flag)
{
synchronized(MyLock.locka)
{
synchronized(MyLock.lockb)
{
}
}
}
else
{
synchronized(MyLock.lockb)
{
synchronized(MyLock.locka)
{
}
}
}
}
}
class Mylock
{
public static final Object locka=newObject();
public static final Object lockb=newObject();
}
class DeadLockTest
{
public static void main(String[]args)
{
Test a=new Test(true);
Test a=new Test(false);
Thread t1=new Thread(a);
Thread t2=new Thread(b);
t1.start();
t2.start();
}
}
线程间通信-等待唤醒机制
思考1:wait(),notify(),notifyAll(),用来操作线程为什么定义在Object类中?
1, 这些方法存在于同步中。
2,使用这些方法时必须要标识所属的同步的锁。
3,锁可以是任意对象,所以任意对象调用的方法一定定义在Object类中。
因为这些方法是监视器的方法,监视器其实就是锁。
思考2:wait(),sleep()有什么区别?
wait():释放cpu执行权,释放锁。
sleep():释放cpu执行权,未释放锁。
1.wait();让线程处于冻结状态,被wait的线程会被存储到线程池中。
2.notify();唤醒线程池中的一个线程(任意)。
3.notifyAll(); 唤醒线程池中的所有线程。
这些方法都必须定义在同步中。
因为这些方法是用于操作线程状态的方法。
必须要明确到底操作的是哪个锁上的线程。
class Resource
{
String name;
String sex;
boolean flag=false;
}
//输入
class Input implements Runnable
{
Resourec r;
Input(Resourec r)
{
this.r=r ;
}
public void run()
{
int x=0;
while(true)
{
synchronized(r)
{
if(r.flag)
try{r.wait();}catch(InterruptedException e){}
if(x==0)
{
r.name="mike";
r.sex="nan";
}
else
{
r.name="丽丽";
r.sex="女女女女女";
}
r.flag=true;
r.notify();
}
x=x++%2;
}
}
}
//输出
class Output implements Runnable
{
Resourec r;
Output(Resourec r)
{
this.r=r;
}
public void run()
{
while(true)
{
synchronized(r)
{
if(!r.flag)
try{r.wait();}catch(InterruptedException e){}
System.out.println(r.name+"...."+r.sex);
r.flag=false;
r.notify();
}
}
}
}
class ResoureDemo2
{
public static void main(String[]args)
{
//创建资源
Resourec r=new Resurec();
//创建任务
Input in=new Input();
Output out=new Output();
//创建线程,执行路径。
Thread t1=new Thread(in);
Thread t2=new Thread(out);
//开启线程
t1.start();
t2.start();
}
}
线程间通信-等待唤醒机制-代码优化
class Resource
{
private String name;
private String sex;
private boolean flag=false;
public synchronized void set(String name,String sex)
{
if(flag)
try{this.wait();}catch(InterruptedException e){}
this.name=name;
this.sex=sex;
flag=true;
this.notify();
}
public synchronized void out()
{
if(!flag)
try{this.wait();}catch(InterruptedException e){}
System.out.println(name+"...."+sex);
flag=false;
notify();
}
}
//输入
class Input implements Runnable
{
Resourec r;
Input(Resourec r)
{
this.r=r;
}
public void run()
{
int x=0;
while(true)
{
if(x==0)
{
r.set("mike","nan");
}
else
{
r.set("丽丽","女女女女女");
}
x=x++%2;
}
}
}
//输出
class Output implements Runnable
{
Resourec r;
Output(Resourec r)
{
this.r=r;
}
public void run()
{
while(true)
{
r.out();
}
}
}
class ResoureDemo3
{
public static void main(String[]args)
{
//创建资源
Resourec r=new Resurec();
//创建任务
Input in=new Input();
Output out=new Output();
//创建线程,执行路径。
Thread t1=new Thread(in);
Thread t2=new Thread(out);
//开启线程
t1.start();
t2.start();
}
}
线程间通信-多生产者多消费者问题
为什么 多生产者,对消费者会出现问题,而单生产和单消费没出现问题呢?
线程间通信-多生产者多消费者问题解决
if判断标识只有一次,会导致不该运行的线程运行了。出现了数据错误的情况
while判断标识,解决了线程获取执行权后,是否要运行!
notify只能唤醒一个线程。如果本方唤醒本方 没意义。而且ehile判断标识+notify会导致死锁。
notifyAll解决了本方线程一定会唤醒对方线程的问题。
class Resoure
{
private String name;
private int count=1;
private boolean flag=false;
public synchronized void set(String name)
{
while(flag)
try{this.wait();}catch(InterruptedException e){}
this.name=name+count;
count++;
System.out.println(Thread.currentThread().getNmae()+"......生产者......"+this.name);
flg=true;
notifyAll();
}
public synchronized void out()
{
while(!flag)
try{this.wait();}catch(InterruptedException e){}
System.out.println(Thread.currentThread().getNmae()+"......消费者......"+this.name);
flg=false;
notifyAll();
}
}
class Producer implements Runnable
{
private Resource r;
Producer(Resource r)
{
this.r=r;
}
public void run()
{
while(true)
{
r.set("烤鸭");
}
}
}
class Consumer implements Runnable
{
private Resource r;
Consume(Resource r)
{
this.r=r;
}
public void run()
{
while(true)
{
r.set("烤鸭");
}
}
}
class ProducerConsumerDemo
{
public static void main(Sting[]args)
{
Resource r=new Resource();
Producer pro=new Producer(r);
Consumer con=new Consumer(r);
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();
}
}
线程间通信-多生产者多消费者问题-JDK1.5新特性-Lock
JDK1.5以后将同步和锁封装成了对象。
并将操作锁的隐式方式定义到了该对象中,
将隐式动作变成了显示动作。
Lock lock=new ReentranLock();
public synchronized void set(String name)
{
lock.lock();
try{
while(flag)
try{this.wait();}catch(InterruptedException e){}
this.name=name+count;
count++;
System.out.println(Thread.currentThread().getNmae()+"......生产者......"+this.name);
flg=true;
notifyAll();
}
finally
{
lock.unlock();
}
}
线程间通信-多生产者多消费者问题-JDK1.5新特性-Condition
//创建一个锁对象、
Lock lock=new ReentranLock();
//通过已有的锁获取该锁上的监视器对象。
Condition con=lock.new.Condition();
public synchronized void set(String name)
{
lock.lock();
try{
while(flag)
//try{this.wait();}catch(InterruptedException e){}
try{con.await();}catch(InterruptedException e){}
this.name=name+count;
count++;
System.out.println(Thread.currentThread().getNmae()+"......生产者......"+this.name);
flg=true;
//notifyAll();
con.signalAll();
}
finally
{
lock.unlock();
}
}
线程间通信-多生产者多消费者问题-JDK1.5解决办法
Lock lock=new ReentranLock();
//通过已有的锁获取该锁上的监视器对象。
//Condition con=lock.new.Condition();
//通过已有的锁获取两组监视器,一组监视生产者,一组监视消费者。
Condition producer_con=lock.newCondition();
Condition consumer_con=lock.newCondition();
public synchronized void set(String name)
{
lock.lock();
try{
while(flag)
//try{this.wait();}catch(InterruptedException e){}
try{producer_con.await();}catch(InterruptedException e){}
this.name=name+count;
count++;
System.out.println(Thread.currentThread().getNmae()+"......生产者......"+this.name);
flg=true;
//notifyAll();
//con.signalAll();
consumer_con.signal();
}
finally
{
lock.unlock();
}
}