1.概述
进程:进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,是系统进行资源分配和调度的一个独立单位。
线程:线程是进程的一个实体,是CPU调度和分派的基本单位,是比进程更小的能独立运行的基本单位。
注意:一个进程至少有一个线程。
2.好处与弊端
优点:
(1).使程序的相应速度更快,因为用户界面可以在进行其它工作的同时一直处于活动状态
(2).当前没有进行处理的任务可以将处理器时间给其它任务
(3).占用大量处理时间的任务可以定期将处理器时间给其它任务
(4).可以随时停止任务
(5).可以分别设置各个任务的优先级和优化性能
缺点:
(1).线程也是程序,所以线程需要占用内存,线程越多内存占用越多
(2).多线程需要协和管理,所以需要CPU时间跟踪线程
(3).线程之间对共享资源 的访问会互相影响,必须解决竞用共享资源的问题
(4).线程太多会导致控制太复杂,最终可能造成很多bug
3.JVM中多线程的解析
JVM启动时就启动了多个线程,至少有两个线程可分析出来:
(1).执行main函数的线程,该线程的任务代码都在main函数中 (2).负责垃圾回收的线程
代码示例:
class Demo{
public void finalize(){
System.out.println("demo ok");
}
}
public class ThreadDemo {
public static void main(String[] args) {
new Demo();
new Demo();
System.gc();
System.out.println("hello world");
}
}
图解:
4.多线程创建的方式一:继承Thread类
代码示例:
/*
* 如何创建一个线程?
* 方式一:继承Thread类
* 步骤:
* 1.定义一个类继承Thread类,
* 2.覆盖Thread类中的run方法,
* 3.直接创建Thread的子类对象创建线程,
* 4.调用start方法开启线程应调用线程的任务run方法执行
*
*/
class Demo1 extends Thread{
private String name;
Demo1(String name){
this.name=name;
}
public void run(){
show();
}
public void show(){
for(int x=0;x<10;x++){
System.out.println(name+"....x="+x+"...name="+Thread.currentThread().getName());
}
}
}
public class ThreadDemo {
public static void main(String[] args) {
Demo1 d1 = new Demo1("xiaoqiang");
Demo1 d2 = new Demo1("小明");
d1.start();
d2.start();
}
}
5.线程的状态
图解:
6.创建线程的方法二:实现Runnable接口
代码示例:
/*
* 方式二:
* 1.定义实现Runnable接口
* 2.覆盖接口中的run方法,将线程的任务代码封装到run方法中
* 3.通过Thread类创建线程对象,并将Runnable接口的子类对象作为Thread类的构造函数的参数进行传递 4.调用线程对象的start方法开启线程
*
* 好处:
* 1.将线程的任务从线程的子类中分离出来,进行单独的封装,按照面向对象的思想将任务封装成对象
* 2.避免java单继承的局限性
*
*/
class Demo1 implements Runnable {
private String name;
public void run() {
show();
}
public void show() {
for (int x = 0; x < 10; x++) {
System.out.println(Thread.currentThread().getName() + "..." + x);
}
}
}
public class ThreadDemo {
public static void main(String[] args) {
Demo1 d = new Demo1();
Thread t1 = new Thread(d);
Thread t2 = new Thread(d);
t1.start();
t2.start();
}
}
7.同步代码块
a.概述:将多条操作共享数据的线程代码封装起来,当有线程在执行这些代码时,其它线程不可以参与运算,必须要当前线程把这些代码都执行完毕后,其它线程才可以参与运算。在java中,用同步代码块解决这个问题。
b.线程安全问题:当一个线程在执行操作共享数据的多条代码时,其它线程参与了运算,就会导致线程安全问题。
c.格式: synchronized(对象) { 需要被同步的代码; }
d.好处和弊端:好处是,解决了线程的安全问题。弊端是相对降低了效率,因为同步外的线程都会判断同步锁。
e:同步的前提:必须有多个线程并使用同一个锁
代码示例:
class Ticket implements Runnable {
private int num = 100;
Object obj = new Object();
public void run() {
show();
}
public void show() {
while (true) {
synchronized (obj) {//同步代码块
if (num > 0) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
}
System.out.println(Thread.currentThread().getName() + "...sale..." + num--);
}
}
}
}
}
public class TicketDemo {
public static void main(String[] args) {
Ticket ticket = new Ticket();
Thread t1 = new Thread(ticket);
Thread t2 = new Thread(ticket);
Thread t3 = new Thread(ticket);
Thread t4 = new Thread(ticket);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
8.同步函数
代码示例:
class Bank {
private int sum;
private Object obj = new Object();
public synchronized void add(int num) {//同步函数
// synchronized (obj) {
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);
}
}
}
public 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();
}
}
9.单例模式涉及的多线程问题
代码示例:
//饿汉式
class Single {
private static final Single s = new Single();//先加载
Single() {}
public static Single getInstance(){
return s;
}
}
// 懒汉式
class Sinlge {
private static Single s = null;//后加载
private void Single() {
}
public static Single getInstance() {
if (s == null) {//解决效率问题
synchronized (Single.class) {//解决线程安全问题
if(s==null)
s = new Single();
}
}
}
10.死锁
两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞现象,若无外力作用,他们都将无法推进下去。
代码示例:
class Ticket3 implements Runnable{
private int num = 100;
Object obj = new Object();
boolean flag = true;
@Override
public void run() {
if(flag)
while(true){
synchronized (obj) {
show();//同步代码块中写入同步函数
}
}
else
while(true)
this.show();
}
public synchronized void show(){
synchronized (obj) {
if(num>0){//同步函数中写入同步代码块
try{Thread.sleep(10);}catch(InterruptedException e){}
System.out.println(Thread.currentThread().getName()+"...sale..."+num--);
}
}
}
}
public class DeadLockDemo{
public static void main(String[] args) {
Ticket3 t = new Ticket3();
Thread t1 = new Thread(t);
Thread t2 = new Thread(t);
t1.start();
try{Thread.sleep(10);}catch(InterruptedException e){}
t.flag = false;
t2.start();
}
}
11.线程间通信
a.概述: 多个线程处理同一资源,但任务不同。
代码示例:
//资源
class Resource {
String name;
String sex;
}
// 输入
class Input1 implements Runnable {
Resource r;
Input1(Resource r) {
this.r = r;
}
public void run() {
int x = 0;
while (true) {
synchronized (r) {
if (x == 0) {
r.name = "mike";
r.sex = "nan";
} else {
r.name = "娜娜";
r.sex = "女";
}
x = (x + 1) % 2;
}
}
}
}
// 输出
class Output1 implements Runnable {
Resource r = new Resource();
Output1(Resource r) {
this.r = r;
}
public void run() {
while (true) {
synchronized (r) {
System.out.println(r.name + "..." + r.sex);
}
}
}
}
public class ResourceDemo {
public static void main(String[] args) {
// 创建资源
Resource r = new Resource();
// 创建任务
Input1 in = new Input1(r);
Output1 out = new Output1(r);
// 创建线程,执行路径
Thread t1 = new Thread(in);
Thread t2 = new Thread(out);
// 开启线程
t1.start();
t2.start();
}
}
b.等待唤醒机制
图例:
代码示例:
/*
* 等待/唤醒机制
* 涉及的方法:
* 1.wait(); 让线程处于冻结状态,被wait的线程会被存储到线程池中
* 2.notify();唤醒线程池中的一个线程(任意)
* 3.notifyAll();唤醒线程池中的所有线程
*
* 这些方法都必须定义在同步中
* 因为这些方法是用于操作线程状态的方法
* 必须要明确到底操作的是哪个锁上的线程
*
* 为什么操作线程的方法 wait notify notifyAll 定义在Object类中?
* 因为这些方法是监视器的方法,监视器其实就是锁
* 锁可以是任意的对象,任意的对象调用方式一定定义在Object类中
*
*/
class Resource {
String name;
String sex;
boolean flag = false;
}
// 输入
class Input implements Runnable {
Resource r;
Input(Resource 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 + 1) % 2;
}
}
}
}
// 输出
class Output implements Runnable {
Resource r = new Resource();
Output(Resource 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();
}
}
}
}
public class ResourceDemo2 {
public static void main(String[] args) {
Resource r = new Resource();
Input in = new Input(r);
Output out = new Output(r);
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;
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 {
Resource r;
Input(Resource r) {
this.r = r;
}
public void run() {
int x = 0;
while (true) {
if (x == 0) {
r.set("mike", "nan");
} else {
r.set("丽丽", "女");
}
x = (x + 1) % 2;
}
}
}
// 输出
class Output implements Runnable {
Resource r = new Resource();
Output(Resource r) {
this.r = r;
}
public void run() {
while (true) {
r.out();
}
}
}
public class ResourceDemo {
public static void main(String[] args) {
Resource r = new Resource();
Input in = new Input(r);
Output out = new Output(r);
Thread t1 = new Thread(in);
Thread t2 = new Thread(out);
t1.start();
t2.start();
}
}
c.多生产多消费问题
代码示例:
/*
* 多生产者多消费者问题解决办法
* if判断标记只有一次,会导致不该运行的线程运行,出现数据错误的情况
* while判断标记,解决线程获取执行权后,是否要运行
*
* notify:只能唤醒一个线程,如果本方唤醒本方,没有意义,而且while判断标记+notify会导致死锁
* notifyAll:解决本方线程一定会唤醒对方线程
*/
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();
}
}
}
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();
}
}
12.wait和sleep的区别
(1).wait可以指定时间也可以不指定,sleep必须指定时间
(2).在同步中,对cpu的执行权和锁的处理不同--wait释放执行权释放锁,sleep释放执行权,不释放锁
13.停止线程的方式
代码示例:
/*
* 停止线程
* 1.stop方法
* 2.run方法结束
* 怎么控制线程的任务结束呢?
* 任务中都会有循环结构,只要控制住循环就可以结束任务
* 控制循环通常就用定义标记来完成 flag = true;
*
* 但是如果线程处于冻结状态,无法读取标记,如何结束呢
* 可以使用interrupt()方法将线程从冻结状态强制恢复到运行状态中来,让线程具备cpu的执行资格
* 但是强制动作会发生InterruptedException,要处理
*
*/
class StopThread implements Runnable {
private boolean flag = true;// 定义结束标记
public synchronized void run() {
while (flag) {
try {
wait();
} catch (InterruptedException e) {
System.out.println(Thread.currentThread().getName() + "...." + e);
flag = false;
}
System.out.println(Thread.currentThread().getName() + "......+++++");
}
}
public void setFlag() {// 对外提供改变标记的方式
flag = false;
}
}
public class StopThreadDemo {
public static void main(String[] args) {
StopThread st = new StopThread();
Thread t1 = new Thread(st);
Thread t2 = new Thread(st);
t1.start();
t2.start();
int num = 0;
for (;;) {
if (++num == 50) {
t1.interrupt();// 强制唤醒
t2.interrupt();
// st.setFlag();将标记重置为false
break;
}
System.out.println("main..." + num);
}
System.out.println("over");
}
}
14.守护线程
a.概述:Java中有两类线程--用户线程(User Thread),守护线程(Daemon Thread),守护线程(Daemon)是运行在后台的一种特殊线程,它独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。
b.特点:只有所有非守护线程执行完毕后,才会执行守护线程,否则不会执行。
c.举例:JVM中的垃圾回收器GC
15.其他方法- join等
概述:join方法阻塞调用此方法的线程,直到线程t完成,此线程再继续。
代码示例:
class Demo3 implements Runnable{
public void run(){
for(int x=0;x<50;x++){
System.out.println(Thread.currentThread().getName()+"..."+x);
}
}
}
public class JoinDemo {
public static void main(String[] args) throws Exception {
Demo3 d = new Demo3();
Thread t1 = new Thread(d);
Thread t2 = new Thread(d);
t1.start();
t1.join();//t1线程要申请加入进来运行,主线程释放执行权和执行资格
t2.start();
for(int x=0;x<50;x++){
System.out.println(Thread.currentThread().getName()+"..."+x);
}
}
}