黑马程序员--Java基础--多线程

来源:互联网 发布:网络舆情分析报告 编辑:程序博客网 时间:2024/05/17 23:14

------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------

1.多线程

进程:

>>狭义定义:进程就是一段程序的执行过程。
>>广义定义:进程是一个具有一定独立功能的程序关于某个数据集合的一次运行活动。它是操作系统动态执行的基本单元,在传统的操作系统中,进程既是基本的分配单元,也是基本的执行单元。
线程:

>>就是进程中一个负责程序执行的控制单元(执行路径),一个进程中有多条执行路径,称之为多线程。

一个进程中至少要有一个线程。开启多个线程是为了同时运行多部分代码。每一个线程都有自己运行的内容。这个内容可以称为线程要执行的任务。

多线程好处:

>>解决了大部分同时运行的问题。

多线程的弊端:

>>线程太多回到会导致效率的降低。

因为应用程序的执行都是cpu在做着快速的切换完成的。且这个切换是随机的。

JVM启动时就启动了多个线程,至少有两个线程可以分析的出来。

1.执行main函数的线程,

>>该线程的任务代码都定义在main函数中。

2.负责垃圾回收的线程。

1.1 多线程的创建方式

如何创建一个线程呢?

创建线程方式一:继承Thread类。

步骤:

1.定义一个类继承Thread类。

2.覆盖Thread类中的run方法。

3.直接创建Thread的子类对象创建线程。

4.调用start方法开启线程并调用线程的任务run方法执行。


public class ThreadDemo2 {public static void main(String[] args){Demo2 d1 = new Demo2("Hello");Demo2 d2 = new Demo2("Hi");d1.start();//开启线程,调用run方法。d2.start();//开启线程,调用run方法。System.out.println("over...."+Thread.currentThread().getName());}}//创建一个类继承Threadclass Demo2 extends Thread{private String name;Demo2(String name){super(name);//this.name=name;}//覆盖run方法public void run(){for(int x=0; x<10; x++){for(int y=0; y<1000000; y++){}System.out.println(name+"....x="+x+".....name="+Thread.currentThread().getName());}}}



创建线程的第二种方式:实现Runnable接口。

1.定义类实现Runnable接口。

2.覆盖接口中的run方法,将线程的任务代码封装到run方法中。

3.通过Thread类创建线程对象,并将Runnable接口的子类对象作为Thread类的构造函数的参数进行传递。

4.调用线程对象的start方法开启线程。


实现Runnable接口的好处:

1.将线程的任务从线程的子类中分离出来,进行了单独的封装。

按照面向对象的思想将任务的封装成对象。

2.避免了java单继承的局限性。

所以,创建线程的第二种方式较为常用。

可以通过Thread的getName获取线程的名称 Thread-编号(从0开始)

public class ThreadDemo {public static void main(String[] args) {// TODO Auto-generated method stub//创建子类对象,并将子类对象作为Thread类的构造函数的参数进行传递。Demo3 d3 = new Demo3();Thread t1 = new Thread(d3);Thread t2 = new Thread(d3);t1.start();t2.start();//创建子类对象,然后开启线程SubThread sub=new SubThread();sub.start();}}// 实现runnable接口class Demo3 implements Runnable {//覆盖run方法public void run() {show();}public void show() {for (int x = 0; x < 20; x++) {System.out.println(Thread.currentThread().getName() + "...implements Runnable..." + x);}}}//继承Threadclass SubThread extends Thread {//覆盖run方法public void run() {System.out.println("extendsThread");}}


1.2 线程四种状态

wait和sleep的区别:

1.wait可以指定时间也可以不指定。

sleep方法必须指定睡眠时间,单位是毫秒。

2.在同步中,对cpu的执行权和所得处理不同。
wait:释放执行权,释放锁。

sleep:释放执行权,不释放锁。

一个特殊的状态:就绪。具备了执行资格,但是还没有获取资源

1.3 线程安全问题

导致安全问题的出现的原因:

1.多个线程在操作共享数据。

2.操作共享数据的线程有多条,当一个线程在执行操作共享数据的多条代码过程中,其他线程参与运算,就会导致线程安全问题的产生。

注:线程安全问题在理想状态下,不容易出现,但一旦出现对软件的影响是非常大 的。

1.4 同步(synchronized)

1.格式:synchronized(对象){需要被同步的代码;}

同步可以解决安全问题的根本原因就在那个对象上。该对象如同锁的功能。

2.好处:解决了多线程的安全问题。

3.弊端:相对降低了效率,因为同步外的线程都会判断同步锁。

4.同步的前提:必须有多个线程并使用同一个锁。

5.同步函数

格式:在函数上加上synchronized修饰符即可。

例:public synchronized void add(int num){...}

6.同步使用的锁:

1.同步函数使用的锁是固定的this。

2.同步代码块的锁是任意的对象(建议使用同步代码块)

3.静态同步函数使用的锁是该函数所属的字节码文件对象,可以使用getClass()获取,也可以用

当前类名.class表示。

class Ticket implements Runnable{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(Thread.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();}}


public class SynchronizedFunction {public static void main(String[] args) {Cus c = new Cus();//创建任务对象Thread t1 = new Thread(c);//创建线程并传递任务对象Thread t2 = new Thread(c);t1.start();//开启线程t2.start();}}class Bank{private int sum;public synchronized void add(int num)//同步函数{sum = sum + num;try{Thread.sleep(10);}catch(InterruptedException e){}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);}}}


1.5 单例设计模式涉及的线程问题

//懒汉式//加入同步为了解决多线程安全问题。//加入双重判断是为了解决效率问题。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) {System.out.println("Hello World!");}}

1.6 线程间的通信

线程间的通信:

多个线程在处理同一资源,但是任务不同。

1.7 等待唤醒机制

设计的方法:

1.wait():让线程处于冻结状态,被wait的线程会被存储到线程池中。

2.notify():唤醒线程池中的任一线程。

3.notifyAll():唤醒线程池中的所有线程。

这些方法必须定义在同步中,因为这些方法是用于操作线程的方法。必须明确到底是操作哪个锁上的线程。

操作线程的wait(),notify(),notifyAll()这些方法定义在Object类中的原因:

因为这些方法是监视器的方法,监视器其实就是锁,锁可以是任意对象,任意对象调用的方法一定定义在Object类中。

1.8 多生产者多消费者

public class ProducerConsumerDemo {public static void main(String[] 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();}}class Resource{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().getName()+"...生产者..."+this.name);flag = true;notifyAll();//唤醒所有线程}public synchronized void out()// 同步函数,消费{while(!flag)try{this.wait();}catch(InterruptedException e){}System.out.println(Thread.currentThread().getName()+"...消费者........"+this.name);flag = 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;Consumer(Resource r){this.r = r;}public void run(){while(true){r.out();}}}

1.9 jdk1.5新特性

1.jdk1.5以后将同步和锁封装成了对象。 

并将操作锁的隐式方式定义到了该对象中,将隐式动作变成了显示动作。
2.Lock接口: 出现替代了同步代码块或者同步函数。将同步的隐式锁操作变成显示锁操作。
同时更为灵活。可以一个锁上加上多组监视器。
lock():获取锁。
unlock():释放锁,通常需要定义finally代码块中。
Condition接口:出现替代了Object中的wait notify notifyAll方法。
将这些监视器方法单独进行了封装,变成Condition监视器对象。

可以任意锁进行组合。

await();---->wait()

signal();---->notify()

signalAll();---->notifyAll()

多生产者多消费者的第二种模式

public class ProducerConsumerDemo2 {public static void main(String[] args) {Resource2 r = new Resource2();Producer2 pro = new Producer2(r);Consumer2 con = new Consumer2(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();}}class Resource2{private String name;private int count = 1;private boolean flag = false;//创建一个锁对象。Lock lock = new ReentrantLock();//通过已有的锁获取两组监视器,一组监视生产者,一组监视消费者。Condition producer_con = lock.newCondition();Condition consumer_con = lock.newCondition();public  void set(String name){lock.lock();//获得锁try{while(flag)try{producer_con.await();}catch(InterruptedException e){}this.name = name + count;count++;//2 3 4System.out.println(Thread.currentThread().getName()+"...生产者5.0..."+this.name);flag = true;consumer_con.signal();// 唤醒对方线程  }finally{lock.unlock();//释放锁}}public  void out(){lock.lock();//获得锁try{while(!flag)try{consumer_con.await();}catch(InterruptedException e){}System.out.println(Thread.currentThread().getName()+"...消费者.5.0......."+this.name);flag = false;producer_con.signal();// 唤醒对方线程  }finally{lock.unlock();//释放锁}}}class Producer2 implements Runnable{private Resource2 r;Producer2(Resource2 r){this.r = r;}public void run(){while(true){r.set("烤鸭");}}}class Consumer2 implements Runnable{private Resource2 r;Consumer2(Resource2 r){this.r = r;}public void run(){while(true){r.out();}}}

1.9 停止线程

定义循环结束标志

1.因为线程运行代码一般都是循环,只要控制了循环即可。

2.使用Interrupt(中断)

该方法将线程从冻结状态强制恢复到运行状态中来,让线程具备cpu的执行资格。 当强制动作时会发生了

InterruptedException,需要处理异常。

2.0 线程常见的一些方法。

setDaemon():将该线程标记为守护线程或用户线程。

join();等待该线程终止。

setPriority(int i):更改线程的优先级

yield();暂停当前正在执行的线程对象,并执行其他线程。

0 0
原创粉丝点击