一、线程和进程的概念
- 进程:正在运行的程序,具有一定独立的功能
- 线程:线程是进程执行的单元,进程 包含 多个线程
二、线程的实现
- 线程的两种创建启动方式
方法一: 继承Thread
a.定义一个 类 继承 Thread。
public class MyThread extends Thread {} //extends 继承
b.重写run方法。
@Override //重写
public void run()
c.创建子类对象,就是创建线程对象。
MyThread mt = new MyThread();
d.调用start方法,开启线程并让线程执行,同时还会告诉jvm去调用run方法
mt.start();
package com.lixu.javabase.thread;
/**
* 测试Thread 线程
*/
public class TestThread {
public static void main(String[] args) {
mythread mh = new mythread();
//启动新开辟的线程
mh.start();
for(int i=0 ; i<100 ;i++){
System.out.println("mainthread"+ i);
}
}
}
//创建一个线程类;继承Thread的方式
class mythread extends Thread{
@Override //可以不写
public void run(){ //重写run()方法的实现
for(int i=0 ; i<100 ;i++){
System.out.println("mythreadTwo"+ i);
}
}
}
方法二: 实现Runnable接口
1、定义类实现Runnable接口。
public class MyThread2 implements Runnable {}
2、覆盖接口中的run方法。
@Override
public void run() {}
3、创建Thread类的对象
MyThread2 mt2 = new MyThread2();
4、将Runnable接口的子类对象作为参数传递给Thread类的构造函数。
public MyThread2(int num) {
this.num = num;
}
5、调用Thread类的start方法开启线程。
mt2.start();
package com.lixu.javabase.thread;
public class TestThread1 {
public static void main(String[] args) {
sunner sn = new sunner();
//要启动一个新的线程就必须new一个Thread对象出来
Thread thread = new Thread(sn);
//启动新开辟的线程,新线程执行的是run()方法,新线程与主线程会一起并行执行
thread.start();
for(int i=0 ; i<100 ;i++){
System.out.println("mainthread"+ i);
}
}
}
//创建一个线程类;实现Runnable接口方式
class sunner implements Runnable{
@Override
public void run() {
for(int i=0 ; i<100 ;i++){
System.out.println("mythread"+ i);
}
}
}
三、线程的方法
- sleep()
package com.lixu.javabase.thread;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* 线程sleep()
*/
public class TestThread2 {
public static void main(String[] args) {
//创建线程对象,启动线程
mythread1 mt1 = new mythread1();
mt1.start();
//十秒后启动主线程
try {
mythread1.sleep(10000);
System.out.println("主线程睡眠了10秒种后再次启动了");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
mt1.flag=false;//改变循环条件,结束死循环
}
}
class mythread1 extends Thread {
boolean flag = true;
@Override
public void run () {
while(flag){
SimpleDateFormat simpleDateFormat1 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
System.out.println("=== "+ simpleDateFormat1.format(new Date())+"===");
try {
sleep(1000); //睡眠的时如果被打断就会抛出InterruptedException异常
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
四、synchronized关键字
package com.lixu.javabase.thread;
public class TestThread5 implements Runnable {
Timer timer = new Timer();
public static void main(String[] args) {
TestThread5 tt5 = new TestThread5();
Thread thread1 = new Thread(tt5);
Thread thread2 = new Thread(tt5);
thread1.setName("t1");
thread2.setName("t2");
thread1.start();
thread2.start();
}
public void run() {
timer.add(Thread.currentThread().getName());
}
}
class Timer {
private static int num = 0;
public/* synchronized */void add(String name) {// 在声明方法时加入synchronized时表示在执行这个方法的过程之中当前对象被锁定
synchronized (this) {
/*
* 使用synchronized(this)来锁定当前对象,这样就不会再出现两个不同的线程同时访问同一个对象资源的问题了
* 只有当一个线程访问结束后才会轮到下一个线程来访问
*/
num++;
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(name + ":你是第" + num + "个使用timer的线程");
}
}
}
结果:
五、生产者与消费者问题
生产者与消费者问题是多线程同步的一个经典问题。生产者和消费者同时使用一块缓冲区,生产者生产商品放入缓冲区,消费者从缓冲区中取出商品。我们需要保证的是,当缓冲区满时,生产者不可生产商品;当缓冲区为空时,消费者不可取出商品。
wait()与notify()方法
package com.lixu.javabase.thread;
import java.util.LinkedList;
/**
* 生产者消费者问题
*/
public class ProAndCon {
//最大容量
public static final int MAX_SIZE = 2;
//存储媒介
public static LinkedList<Integer> list = new LinkedList<>();
class Producer implements Runnable {
@Override
public void run() {
synchronized (list) {
//仓库容量已经达到最大值
while (list.size() == MAX_SIZE) {
System.out.println("仓库已满,生产者" + Thread.currentThread().getName() + "不可生产.");
try {
list.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
list.add(1);
System.out.println("生产者" + Thread.currentThread().getName() + "生产, 仓库容量为" + list.size());
list.notify();
}
}
}
class Consumer implements Runnable {
@Override
public void run() {
synchronized (list) {
while (list.size() == 0) {
System.out.println("仓库为空,消费者" + Thread.currentThread().getName() + "不可消费.");
try {
list.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
list.removeFirst();
System.out.println("消费者" + Thread.currentThread().getName() + "消费,仓库容量为" + list.size());
list.notify();
}
}
}
public static void main(String[] args) {
ProAndCon proAndCon = new ProAndCon();
Producer producer = proAndCon.new Producer();
Consumer consumer = proAndCon.new Consumer();
for (int i = 0; i < 10; i++) {
Thread pro = new Thread(producer);
pro.start();
Thread con = new Thread(consumer);
con.start();
}
}
}
六、 死锁
参考:https://www.cnblogs.com/xdp-gacl/p/3634382.html