1. 进程和线程
- 进程:正在运行的程序
- 线程:程序中的一个执行路径.如果一个进程中包含多个线程那么这个程序就是多线程程序
1.1Thread类的方法
void setName(String name) 给线程设置名称
String getName() 获取线程的名称
void setPriority(int newPriority) 设置线程的优先级(范围是1~10)
int getPriority(int newPriority) 获取线程的优先级(范围是1~10)
static void sleep(long millis) 让线程随眠指定的毫秒值
void join() 当一个线程调用此方法,这个线程执行完毕,其他线程才能执行
void setDaemon(boolean on) 设置线程为守护线程,如果主线程结束守护线程也会跟着结束
1.2线程的生命周期
线程的生命周期指的是从线程创建,到线程结束中间经历的某些过程。
1.3多线程的实现方式1
1.写一个Tread的子类(继承Thread)
2.复写run方法
3.创建Thread的子类对象
4.调用start方法开启线程
//写一个Thread的子类
public class DownloadThread extends Thread{
//复写run方法
public void run(){
//模拟下载的进度
for(int i=0;i<=100;i++){
System.out.println("正在下载..."+i+"%");
}
}
}
//写一个测试类
public class Demo1{
public static void main(String[] args){
//创建Thread的子类对象
DownloadThread dt1=new DownloadThread();
dt1.start(); //开启线程1
DownloadThread dt2=new DownloadThread();
dt2.start(); //开启线程2
}
}
1.4多线程的实现方式2
1.先写一个Runnable接口的实现类
2.复写run方法
3.创建Thread对象,并且把Runnable的实现类对象作为线程的执行任务
4.调用start方法,开启线程
//1.先写一个Runnable接口的实现类
//2.复写run方法
public class DownloadRunnable implements Runnable{
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName()+"正在下载..."+i+"%");
}
}
}
//测试类
public class Demo1 {
public static void main(String[] args) {
//创建一个DownloadRunnable线程的执行任务
DownloadRunnable dr1=new DownloadRunnable();
//创建2个线程,共享一个线程任务
Thread t1=new Thread(dr1,"张三");
Thread t2=new Thread(dr1,"李四");
//开启线程
t1.start();
t2.start();
}
}
2.线程安全问题
- 线程安全问题产生的原因
当多线程在访问共享数据时,如果一个线程正在执行而没有执行完,它的执行权被其他线程抢走了就有可能出现安全问题
2.1解决安全问题-同步代码块
//锁对象: 可以是任意对象,但是要保证唯一
synchronized(锁对象){
//...有可能发生安全问题的代码...
}
注意:一个线程进入同步代码块,其他线程就必须在外面等待.
2.2解决安全问题-同步方法
//同步方法:锁对象是this
public synchronized void show(){
}
//静态同步方法:锁对象是 类名.class
public static synchronized void show(){
}
2.3解决安全问题-Lock锁
Lock lock=new ReentrantLock();
lock.lock(); //获取锁
try{
//有安全问题的代码
...
}finally{
lock.unlock(); //释放锁
}
- API中有关线程安全的类
线程安全的类
Vector:功能类似于ArrayList,都是数组结构
Hashtable:功能类似于HashMap,都是哈希表结构
StringBuffer:功能类似于StringBuilder
线程不安全的类
ArrayList
HashMap
StringBuilder
---------------------------
Collections工具类中有一些方法可以把线程不安全的集合转换为线程安全的集合
List<String> list = Collections.synchronizedList(new ArrayList<String>());
Map<String> map = Collections.synchronizedMap(new HashMap<String>());
Set<String> set = Collections.synchronizedSet(new HashSet<String>());
生产者和消费者案例
- 奶箱类
public class Box {
//奶箱的状态,false表示无牛奶
private boolean state=false;
//记录牛奶瓶数
private int num;
//存牛奶
/*
步骤:
1.判断奶箱的状态是true还是false
2.如果是true表示有牛奶,生产者等待wait
3.如果没有牛奶,就开始生产牛奶
4.修改奶箱的状态为true
5.唤醒消费者
*/
public synchronized void put(int num){
if (state){
//如果是true表示有牛奶,生产者等待
try{
this.wait();
//同步方法的锁对象是this,wait方法必须被锁对象调用
}catch(InterruptedException e)
{
e.printStackTrace();
}
}
//如果没有牛奶,就开始产奶
System.out.println("送奶工正在存入第"+num+"瓶牛奶");
this.num=num;
//修改奶箱的状态为true
this.state=true;
//把消费者唤醒
this.notify();
//同步方法的锁对象是this.notify方法必须被锁对象调用
}
public synchronized void get() {
if(!state){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//如果有牛奶,就开始取牛奶
System.out.println("消费者正在取第"+this.num+"瓶牛奶");
//把奶箱的状态改为false
this.state=false;
//唤醒生产者线程
this.notify();
}
}
- 消费者执行任务
public class Customer implements Runnable {
private Box box;
public Customer(Box box) {
this.box = box;
}
@Override
public void run() {
//取走10瓶牛奶
for (int i = 1; i <=10; i++) {
box.get();
}
}
}
- 生产者执行任务
public class Producter implements Runnable {
private Box box;
public Producter(Box box) {
this.box = box;
}
@Override
public void run() {
//生产10瓶牛奶
for (int i = 1; i <= 10; i++) {
box.put(i);
}
}
}
- 测试类
public class Demo {
public static void main(String[] args) {
//创建奶箱对象
Box box = new Box();
//创建两个线程的执行任务
Customer cus = new Customer(box);
new Thread(cus).start();
Producter pro = new Producter(box);
new Thread(pro).start();
}
}