进程:正在进行中的程序。 每一个进程执行都有一个执行顺序,该顺序是一个执行路径,或者叫一个控制单元。
线程:就是进程中一个执行单元或执行情景或执行路径负责进程中程序执行的控制单元 。一个进程中至少要有一个线程。当一个进程中线程有多个时,就是多线程。
为什么要用多线程
1,让计算机"同时"做多件事情,节约时间。
2,后台运行程序,提高程序的运行效率.。
3,多线程可以让程序"同时"处理多个事情。
4,计算机CPU大部分时间处于空闲状态,浪费了CPU资源。
1.创建线程的两种方式:
方式一步骤:继承Thread类
子类覆盖父类中的run方法,将线程运行的代码存放在run中。
建立子类对象的同时线程也被创建。
通过调用start方法开启线程。
public class ThreadTest extends Thread{
private int i;
public void run() {
for (i = 0; i < 100; i++)
System.out.println(Thread.currentThread().getName() + "-------"+ i);
}
public static void main(String[] args) {
Demo d1 = new Demo();
Demo d2 = new Demo();
d1.start();
d2.start();
for (int i = 0; i < 60; i++)
System.out.println(Thread.currentThread().getName()+"----------" + i);
}
}
方式二步骤:实现Runnable接口
子类覆盖接口中的run方法。
通过Thread类创建线程,并将实现了Runnable接口的子类对象作为参数传递给Thread类的构造函数。
Thread类对象调用start方法开启线程。
public class RunnableTest implements Runnable {
private int num = 100;
@Override
public void run() {
while (true) {
if (num > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "----->"
+ num--);
} else {
break;
}
}
}
public static void main(String args[]) {
RunnableTest rt = new RunnableTest();
Thread t1 = new Thread(rt, "新线程1");
Thread t2 = new Thread(rt, "新线程2");
Thread t3 = new Thread(rt, "新线程3");
t1.start();
t2.start();
t3.start();
}
2.两种创建方式的对比
Runnable接口相对于继承Thread类的好处:
1.适合多个相同程序代码的线程去处理同意资源的情况,把虚拟CPU(线程)同程序的代码,数据有效分离,较好的体现了面向对象的设计思想。
2.可以避免由于java的单继承特点带来的局限。当我们要将已经继承了某一个类的子类放入多线程中,由于一个类不能同时有两个父类,所以只能使用Runnable接口方法。
3.有力与程序的健壮性,代码能给被多个线程共享,代码与数据是独立的。
线程的四种状态及其转换
同步代码块
1)同步的前提:
A.必须有两个或两个以上的线程
B.必须保证同步的线程使用同一个锁。必须保证同步中只能有一个线程在运行。
好处与弊端:解决了多线程的安全问题。多个线程需要判断锁,较为消耗资源。
public class RunnableTest implements Runnable {
private int num = 100;
Object obj = new Object();
@Override
public void run() {
while (true) {
synchronized (obj) {
if (num > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()
+ "----->" + num--);
} else {
break;
}
}
}
}
public static void main(String args[]) {
RunnableTest rt = new RunnableTest();
Thread t1 = new Thread(rt, "新线程1");
Thread t2 = new Thread(rt, "新线程2");
Thread t3 = new Thread(rt, "新线程3");
t1.start();
t2.start();
t3.start();
}
}
多线程安全的例子:
要求只要银行每次金额的变化情况。
class Bank
{
private int sum;
private Object obj = new Object();
public synchronized void add(int num)
{
sum = sum + num;
try{Thread.sleep(10);}catch(InterruptedException e){}
System.out.println(Thread.currentThread().getName()+"....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();
}
}
等待唤醒机制的方式有三种:
notify():唤醒,唤醒线程池中被wait的线程,一次唤醒一个,而且是任意的。
notifyAll():唤醒全部,可以将线程池中的所有wait线程都唤醒。
多线程总结:
1,进程和线程的概念。
|--进程:
|--线程:
2,jvm中的多线程体现。
|--主线程,垃圾回收线程,自定义线程。以及他们运行的代码的位置。
3,什么时候使用多线程,多线程的好处是什么?创建线程的目的?
|--当需要多部分代码同时执行的时候,可以使用。
4,创建线程的两种方式。
|--继承Thread
|--步骤
|--实现Runnable
|--步骤
|--两种方式的区别?
5,线程的5种状态。
对于执行资格和执行权在状态中的具体特点。
|--被创建:
|--运行:
|--冻结:
|--临时阻塞:
|--消亡:
6,线程的安全问题。
|--安全问题的原因:
|--解决的思想:
|--解决的体现:synchronized
|--同步的前提:但是加上同步还出现安全问题,就需要用前提来思考。
|--同步的两种表现方法和区别:
|--同步的好处和弊端:
|--单例的懒汉式。
|--死锁。
7,线程间的通信。等待/唤醒机制。
|--概念:多个线程,不同任务,处理同一资源。
|--等待唤醒机制。使用了锁上的wait notify notifyAll.
|--生产者/消费者的问题。并多生产和多消费的问题。 while判断标记。用notifyAll唤醒对方。
|--JDK1.5以后出现了更好的方案,
Lock接口替代了synchronized
Condition接口替代了Object中的监视方法,并将监视器方法封装成了Condition
和以前不同的是,以前一个锁上只能有一组监视器方法。现在,一个Lock锁上可以多组监视器方法对象。
可以实现一组负责生产者,一组负责消费者。
|--wait和sleep的区别。
wait()和sleep()方法的异同点:
两个方法都可以让线程处于冻结状态。
sleep()必须指定时间,wait是可以指定时间,也可以不指定。
sleep():会释放执行权,不会释放锁。
wait():会释放执行权,会释放锁。
8,停止线程的方式。
|--原理:
|--表现:--中断。
9,线程常见的一些方法。
|--setDaemon()
|--join();
|--优先级
|--yield();
|--在开发时,可以使用匿名内部类来完成局部的路径开辟。