进程:是正在运行的程序
线程:是进程中的单个顺序控制流,是一条执行路径
举例
方式1:继承Thread类
两个小问题:
代码演示:
定义Mythread类:
public class MyThread extends Thread {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(i);
}
}
//重写run的目的:将自定义代码存储在run方法,让线程运行。
}
创建测试类:
public class ThreadDemo {
public static void main(String[] args) {
MyThread mt = new MyThread();
MyThread mt2 = new MyThread();
mt.start();
mt2.start();
}
}
Thread类中设置和获取线程名称的方法
如何获取main()方法所在的线程名称?
代码演示:
定义MyThread类:
public class MyThread extends Thread {
public MyThread() {}
public MyThread(String name){//给出带参构造方法
super(name);
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(getName() + ": " + i);
}
}
//重写run的目的:讲自定义代码存储在run方法,让线程运行。
}
定义测试类:
public class ThreadDemo {
public static void main(String[] args) {
//无参构造
MyThread mt = new MyThread();
MyThread mt2 = new MyThread();
//给线程设置名称
mt.setName("火车");
mt2.setName("飞机");
//带参构造
MyThread mt3 = new MyThread("小汽车");
MyThread mt4 = new MyThread("轮船");
//启动线程
mt.start();
mt2.start();
mt3.start();
mt4.start();
//获取main方法所在的线程名称
System.out.println(Thread.currentThread().getName()); //main
}
}
5.线程调度
线程有两种调度模型
java使用的是抢占式调度模型
假如计算机只有一个CPU,那么CPU在某一个时刻只能执行一条指令,线程只有得到CPU时间片,也就是使用权,才可以执行指令。所以说多线程程序的执行是有随机性,因为谁抢到CPU的使用权是不一定的
Thread类中设置和获取线程优先级的方法
注意:即使设置了优先级,也不一定优先级大的也不一定优先,因为设置优先级的大小仅仅只是提高了抢占到CPU时间片的概率,只有多次运行或者次数比较多的时候才能看到想要的效果
代码演示:
public class ThreadDemo {
public static void main(String[] args) {
//无参构造
MyThread mt = new MyThread();
MyThread mt2 = new MyThread();
MyThread mt3 = new MyThread();
//给线程设置名称
mt.setName("火车");
mt2.setName("飞机");
mt3.setName("小汽车");
//获得此线程的优先级
System.out.println(mt.getPriority());//5
System.out.println(mt2.getPriority());//5
System.out.println(mt3.getPriority());//5
System.out.println(Thread.MAX_PRIORITY);//最大优先级为10 被final和static修饰的成员变量
System.out.println(Thread.MIN_PRIORITY);//最小优先级为1
System.out.println(Thread.NORM_PRIORITY);//默认的优先级为5
//设置线程优先级
mt.setPriority(10);
mt2.setPriority(5);
mt3.setPriority(1);
//启动线程
mt.start();
mt2.start();
mt3.start();
}
}
代码演示:
public class MyThread extends Thread {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(getName() + ": " + i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class ThreadDemo {
public static void main(String[] args) {
MyThread mt = new MyThread();
MyThread mt2 = new MyThread();
MyThread mt3 = new MyThread();
mt.setName("关羽");
mt2.setName("张飞");
mt3.setName("刘备");
//启动线程
mt.start();
try {
//void join()
mt.join();//只有当这个线程结束时,其他线程才会继续执行
} catch (InterruptedException e) {
e.printStackTrace();
}
mt2.start();
mt3.start();
}
}
public class ThreadDemo {
public static void main(String[] args) {
MyThread mt = new MyThread();
MyThread mt2 = new MyThread();
mt.setName("关羽");
mt2.setName("张飞");
//设置主线程
Thread.currentThread().setName("刘备");
//设置守护线程 void setDaemon(boolean on)
mt.setDaemon(true);
mt2.setDaemon(true);
//启动线程
mt.start();
mt2.start(); //当主线程结束时,守护线程也会结束
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + ": " + i);
}
}
}
实现Runnable接口
多线程的实现方案有两种:
相比继承Thread类,实现Runnable接口的好处
代码演示:
定义MyRunnable类
public class MyRunnable implements Runnable {
@Override
public void run() {
for (int i = 0; i < 100; i++)
//因为getName()是Thread类的特有方法,因为前面第一种方法是继承了Thread类才可以使用
//此处想要使用getName()方法,就要先获取当前线程Thread.currentThread() 获取到了线程才可以使用其getName()方法
System.out.println(Thread.currentThread().getName() + ": " + i);
}
}
测试类:
public class RunnableDemo {
public static void main(String[] args) {
//创建MyRunnable类的对象
MyRunnable mr = new MyRunnable();
//创建Thread类的对象
Thread t = new Thread(mr,"飞机");
Thread t2 = new Thread(mr,"火车");
//启动线程
t.start();
t2.start();
}
}
定义卖票类(SellTicket):
public class SellTicket implements Runnable {
private int tickets = 100;
@Override
public void run() {
while(true){
//下面代码会导致卖票出现两种问题 :1.相同的票卖了多次 2.出现了负数的票
if (tickets > 0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("第"+tickets+"张票被"+Thread.currentThread().getName()+"卖出");
tickets--;
} else {
System.out.println(Thread.currentThread().getName()+"显示票已卖完");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
创建测试类:
public class SellTicketDemo {
public static void main(String[] args) {
//创建卖票类对象
SellTicket st = new SellTicket();
//创建线程对象
Thread t1 = new Thread(st,"第1窗口");
Thread t2 = new Thread(st,"第2窗口");
Thread t3 = new Thread(st,"第3窗口");
//启动线程
t1.start();
t2.start();
t3.start();
}
}
卖票出现了问题:
问题原因:
为什么出现问题?(这也是判断多线程程序是否会有数据安全问题的标准)
如何解决多线程安全问题?
怎么实现?
锁多条语句操作共享数据,可以使用同步代码块实现
改进定义的Sellticket类
public class SellTicket implements Runnable {
private int tickets = 1000;
private Object obj = new Object();
@Override
public void run() {
while (true) {
synchronized (obj) {//此处加上同一把锁,当线程进来先看是否持有锁,有则可以进去执行,只有当前一个线程执行完代码块之后,下一个线程才可以进来
//下面代码会导致卖票出现两种问题 :1.相同的票卖了多次 2.出现了负数的票
if (tickets > 0) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("第" + tickets + "张票被" + Thread.currentThread().getName() + "卖出");
tickets--;
} else {
System.out.println(Thread.currentThread().getName() + "显示票已卖完");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
}
代码演示:
public class SellTicket implements Runnable {
private static int tickets = 100;
private Object obj = new Object();
private int index = 0;
@Override
public void run() {
while (true) {
if (index % 2 == 0) {
synchronized (SellTicket.class) {//此处加上同一把锁,当线程进来先看是否持有锁,有则可以进去执行,只有当前一个线程执行完代码块之后,下一个线程才可以进来
if (tickets > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
//下面两行代码会导致卖票出现两种问题 :1.相同的票卖了多次 2.出现了负数的票
System.out.println("第" + tickets + "张票被" + Thread.currentThread().getName() + "卖出");
tickets--;
}
}
} else {
// synchronized (obj) {//此处加上同一把锁,当线程进来先看是否持有锁,有则可以进去执行,只有当前一个线程执行完代码块之后,下一个线程才可以进来
// if (tickets > 0) {
// try {
// Thread.sleep(100);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
//
// //下面两行代码会导致卖票出现两种问题 :1.相同的票卖了多次 2.出现了负数的票
// System.out.println("第" + tickets + "张票被" + Thread.currentThread().getName() + "卖出");
// tickets--;
// }
// }
sellTicket();
}
index++;
}
}
private static synchronized void sellTicket() {
if (tickets > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
//下面两行代码会导致卖票出现两种问题 :1.相同的票卖了多次 2.出现了负数的票
System.out.println("第" + tickets + "张票被" + Thread.currentThread().getName() + "卖出");
tickets--;
}
}
}
代码演示:
import java.util.*;
public class ThreadDemo2 {
public static void main(String[] args) {
StringBuffer sf = new StringBuffer();
StringBuilder sb = new StringBuilder();
Vector<String> v = new Vector<>();
ArrayList<String> array = new ArrayList<>();
Hashtable<String,String> ht = new Hashtable<>();
HashMap<String,String> hm = new HashMap<>();
//集合通常使用以下Collections工具类的方法来保证线程同步
List<String> list = Collections.synchronizedList(new ArrayList<>());
}
}
代码演示:
定义卖票类:(测试类省略参考以前案例)
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class SellTicket implements Runnable {
private int tickets = 100;
private Lock lock = new ReentrantLock(); //创建Lock类对象
@Override
public void run() {
while (true) {
try {//以下代码可能会出错导致没有释放掉锁
lock.lock();//在此处加锁
if (tickets > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "正在售卖第" + tickets + "张票");
tickets--;
}
} finally {//无论上述代码是否出错都释放锁
lock.unlock();//在此处释放锁
}
}
}
}
定义奶箱类(Box):
public class Box {
private int milk; //奶箱中奶的数量
private boolean flag = false; //奶箱的状态 ,默认开始奶箱为空的
public synchronized void put(int milk) {
//当奶箱有奶,就等待消费
if (flag) {
try {
wait(); //wait()方法需要在线程同步里使用 所以方法加synchronized
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//当奶箱没有奶,就生产
this.milk = milk;
System.out.println("生产了" + milk + "瓶奶");
//生产完改变奶箱的状态
flag = true;
//唤醒其他线程
notifyAll();
}
public synchronized void get() {
//当奶箱没有奶,就等待生产
if (!flag) { //此时假设flag为false 只有if里面为true时才执行,所以加一个逻辑非!来执行代码块内容
try {
wait(); //wait()方法需要在线程同步里使用 所以方法加synchronized
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//当奶箱有奶,就消费
System.out.println("用户拿到了第" + milk + "瓶奶");
//消费完,改变奶箱的状态
flag = false;
//唤醒其他线程启动
notifyAll();
}
}
定义生产者类:
public class Producer implements Runnable {
private Box b;
public Producer(Box b) {
this.b = b;
}
@Override
public void run() {
for (int i = 1; i <= 100; i++) {
b.put(i);
}
}
}
定义消费者类:
public class Customer implements Runnable{
private Box b;
public Customer(Box b) {
this.b = b;
}
@Override
public void run() {
while(true) {
b.get();
}
}
}
定义测试类:
public class BoxDemo {
public static void main(String[] args) {
//创建奶箱对象 这是共享区域
Box b = new Box();
//创建生产者对象
Producer p = new Producer(b);
//创建消费者对象
Customer c = new Customer(b);
//创建两个线程对象
Thread td1 = new Thread(p,"生产者");
Thread td2 = new Thread(c,"消费者");
//启动线程
td1.start();
td2.start();
}
}