--------Java培训、Android培训、iOS培训、.Net培训、期待与您交流!
一.进程、线程、多进程的概念
进程:就是正在进行的程序
线程:进程中程序负责执行的控制单元
多线程:一个进程中有多个执行路径
多线程的好处:解决了多部分代码同时运行的问题。
多线程的弊端:线程太多,会导致效率的降低。
其实,多个应用程序同时执行都是CPU在做着快速的切换完成的。 这个切换是随机的。 CPU的切换是需要花费时间的,从而导致了效率的降低。
注:一个进程中至少要有一个线程。开启多个线程是为了同时运行多部分代码,每一个线程都有自己运行的内容,这个内容可以称为线程要执行的任务。
创建线程的两种方式
(1)继承Thread类
步骤:
(1)定义一个类继承Thread类
(2)覆盖Thread类中的run方法
run方法中定义的是线程要运行的任务代码,开启线程是为了运行指定代码,所以只有继承Thread类,并复写run方法,将运行的代码定义在run方法中即可。
(3)直接创建Thread的子类对象创建线程
(4)调用start方法开启线程并调用线程方法
简单代码如下:
class Demo1 extends Thread //定义一个类继承线程类Thread
{
private String name;//私有一个字符串引用 name
Demo1(String name)//构造函数对Demo1进行初始化并具有属性name
{
super(name);
//this.name=name;
}
public void run()//定义一个run函数来覆盖Thread
{
for(int x=0;x<10;x++)
{
for(int y=-100000;y<890980;y++){}
System.out.println(name+"....x="+x+"....name="+Thread.currentThread().getName());//输出里面调用了getName方法
}
}
}
class DemoThread
{
public static void main(String[] args)
{ Demo1 s1=new Demo1("小皓");//创建Thread子类对象
Demo1 s2=new Demo1("小静");
s1.start();//调用strat方法开启线程
s2.start();
for(int w=0;w<10;w++)
{
System.out.println("小胖子....w="+w+"....name="+Thread.currentThread().getName());
}
}
}
运行结果:小胖子....w=0....name=main小胖子....w=1....name=main
小胖子....w=2....name=main
null....x=0....name=小静
null....x=0....name=小皓
null....x=1....name=小静
小胖子....w=3....name=main
null....x=2....name=小静
null....x=1....name=小皓
null....x=3....name=小静
null....x=4....name=小静
null....x=5....name=小静
null....x=6....name=小静
null....x=7....name=小静
null....x=8....name=小静
null....x=9....name=小静
小胖子....w=4....name=main
小胖子....w=5....name=main
小胖子....w=6....name=main
小胖子....w=7....name=main
小胖子....w=8....name=main
小胖子....w=9....name=main
null....x=2....name=小皓
null....x=3....name=小皓
null....x=4....name=小皓
null....x=5....name=小皓
null....x=6....name=小皓
null....x=7....name=小皓
null....x=8....name=小皓
null....x=9....name=小皓
小结:
(1)线程运行有随机性,抢占cpu
(2)整个类去继承Thread只是为了覆盖run方法,有点浪费,并且java不支持多继承势必会造成局限性。
(3)如果多线程执行的是一个有总数的项目 比如多线程进行卖票,就会new出多个对象,每个对象都是独立的这种方法就不行了。
创建线程方式二:实现Runnable接口
步骤:(1)
定义runnable接口
(2)
覆盖接口中的run方法,
将线程的任务代码封装到run方法中
。
(3)通过Thread类创建线程对象,并将Runnable接口的子类对象作为Thread类的构造函数的参数进行传递
(4)调用线程对象的start方法开启线程。
Thread-0...0
Thread-0...1
Thread-0...2
Thread-1...0
Thread-1...1
Thread-1...2
实现Runnable接口的好处:
1. 将线程的任务从线程的子类中分离出来,进行了单独的封装,按照面向对象的思想将任务封装成对象。
2. 避免了Java单继承的局限性。 所以,创建线程的第二种方式较为常用。
线程安全问题
根据卖票例子可以发现 , Thread-0通过了if判断后,在执行到“ num--” 语句之前,num此时仍等于1。CPU切换到Thread-1、 Thread-2、 Thread-3之后,这些线程依然可以通过if判断,从而执行“ num--” 的操作,因而出现了0、 -1、 -2的情况。
线程安全问题产生的原因:
1. 多个线程在操作共享的数据。
2. 操作共享数据的线程代码有多条。
当一个线程在执行操作共享数据的多条代码过程中,其他线程参与了运算,就会导致线程安全问题的产生。
线程安全问题的解决方案
(4)调用线程对象的start方法开启线程。
package test;
class Demo2 implements Runnable//实现runnable接口
{
public void run(){ //覆盖run方法
show();//调用show方法
}
private void show() {
// TODO Auto-generated method stub
for(int i=0;i<3;i++){
System.out.println(Thread.currentThread().getName() + "..." + i);
}
}
}
public class Demo1{
public static void main(String[] args) {
// TODO Auto-generated method stub
Demo2 d= new Demo2();//创建Demo2对象
Thread t1 = new Thread(d);//创建Thread类并将并将Runnable接口的子类对象做为参数传递
Thread t2 =new Thread(d);
t1.start();//开启线程
t2.start();
}
}
运行结果如下:
Thread-0...0
Thread-0...1
Thread-0...2
Thread-1...0
Thread-1...1
Thread-1...2
实现Runnable接口的好处:
1. 将线程的任务从线程的子类中分离出来,进行了单独的封装,按照面向对象的思想将任务封装成对象。
2. 避免了Java单继承的局限性。 所以,创建线程的第二种方式较为常用。
线程安全问题
根据卖票例子可以发现 , Thread-0通过了if判断后,在执行到“ num--” 语句之前,num此时仍等于1。CPU切换到Thread-1、 Thread-2、 Thread-3之后,这些线程依然可以通过if判断,从而执行“ num--” 的操作,因而出现了0、 -1、 -2的情况。
线程安全问题产生的原因:
1. 多个线程在操作共享的数据。
2. 操作共享数据的线程代码有多条。
当一个线程在执行操作共享数据的多条代码过程中,其他线程参与了运算,就会导致线程安全问题的产生。
线程安全问题的解决方案
(1)在java中,用同步代码块就可以解决这个问题。
同步代码块的格式:
synchronized(对象){
需要被同步的代码;
} 同步的好处:解决了线程的安全问题。
同步的弊端:当线程相当多时,因为每个线程都会去判断同步上的锁,这是很耗费资源的,无形中会降低程序的运行效率。
同步的前提:必须有多个线程并使用同一个锁。
(2)格式:在函数上加上synchronized修饰符即可。
注:同步的目的就是加上锁,谁符合条件就进去执行任何锁上别的线程就进不来了 等到运行结束后再打开锁让这些线程再去抢cup。
线程间通信涉及的方法
多个线程在处理统一资源,但是任务却不同,这时候就需要线程间通信。
等待/唤醒机制涉及的方法:
1. wait():让线程处于冻结状态,被wait的线程会被存储到线程池中。
2. notify():唤醒线程池中的一个线程(任何一个都有可能)。
3. notifyAll():唤醒线程池中的所有线程。
注:
1、这些方法都必须定义在同步中,因为这些方法是用于操作线程状态的方法。
2、必须要明确到底操作的是哪个锁上的线程!
3、wait和sleep区别?
1)wait可以指定时间也可以不指定。sleep必须指定时间。
2)在同步中时,对CPU的执行权和锁的处理不同。
wait:释放执行权,释放锁。
sleep:释放执行权,不释放锁
同步代码块的格式:
synchronized(对象){
需要被同步的代码;
} 同步的好处:解决了线程的安全问题。
同步的弊端:当线程相当多时,因为每个线程都会去判断同步上的锁,这是很耗费资源的,无形中会降低程序的运行效率。
同步的前提:必须有多个线程并使用同一个锁。
(2)格式:在函数上加上synchronized修饰符即可。
注:同步的目的就是加上锁,谁符合条件就进去执行任何锁上别的线程就进不来了 等到运行结束后再打开锁让这些线程再去抢cup。
线程间通信涉及的方法
多个线程在处理统一资源,但是任务却不同,这时候就需要线程间通信。
等待/唤醒机制涉及的方法:
1. wait():让线程处于冻结状态,被wait的线程会被存储到线程池中。
2. notify():唤醒线程池中的一个线程(任何一个都有可能)。
3. notifyAll():唤醒线程池中的所有线程。
注:
1、这些方法都必须定义在同步中,因为这些方法是用于操作线程状态的方法。
2、必须要明确到底操作的是哪个锁上的线程!
3、wait和sleep区别?
1)wait可以指定时间也可以不指定。sleep必须指定时间。
2)在同步中时,对CPU的执行权和锁的处理不同。
wait:释放执行权,释放锁。
sleep:释放执行权,不释放锁