进程定义
进程是正在运行的程序,是系统进行资源分配和调用的独立单位,每一个进程都有它自己的内存空间和系统资源
线程的定义
线程是进程中单个顺序控制流,是一种执行路径
单线程: 一个进程如果只有一条路径则被称为单线程
多线程: 一个进程如果有多条执行路径,则被称为多线程
多线程的实现之继承Thread类
方式1:继承Thread类(要重写run方法)
package day4; public class MyThread extends Thread{ @Override public void run() { for (int i = 0; i < 100; i++) { System.out.println(i); } } }
package day4; public class ThreadTest{ public static void main(String[] args) { MyThread myThread1 = new MyThread(); MyThread myThread2 = new MyThread(); myThread1.start(); myThread2.start(); } }
为什么要重写run方法
因为run方法是用来封装被线程执行的代码
run方法和start方法的区别
run():封装线程的执行的代码,直接调用,相当于普通方法
start():启动线程 然后JVM调用此线程的run方法
设置和获取线程名称
package day4; public class MyThread extends Thread{ @Override public void run() { for (int i = 0; i < 100; i++) { System.out.println(getName()+": "+i); } } public MyThread(){ } public MyThread(String name){ super(name); } }
package day4; public class MyThread extends Thread{ @Override public void run() { for (int i = 0; i < 100; i++) { System.out.println(getName()+": "+i); } } public MyThread(){ } public MyThread(String name){ super(name); } }
通过在MyThread类中创建有参构造方法,传递名称(只能手动创建不能使用快捷方法创建)
package day4; public class MyThread extends Thread{ @Override public void run() { for (int i = 0; i < 100; i++) { System.out.println(getName()+": "+i); } } }
package day4; public class ThreadTest{ public static void main(String[] args) { MyThread myThread1 = new MyThread(); MyThread myThread2 = new MyThread(); myThread1.setName("小明报数"); myThread2.setName("小红报数"); myThread1.start(); myThread2.start(); } }
通过setName方法设置线程名称
static Thread currentThread() 返回当前正在执行的线程对象引用
package day4; public class ThreadTest{ public static void main(String[] args) { System.out.println(Thread.currentThread().getName()); } }
线程的优先级
package day4; public class ThreadTest{ public static void main(String[] args) { MyThread myThread1 = new MyThread(); MyThread myThread2 = new MyThread(); MyThread myThread3 = new MyThread(); myThread1.setName("小明"); myThread2.setName("小红"); myThread3.setName("小刚"); System.out.println(Thread.MAX_PRIORITY); System.out.println(Thread.MIN_PRIORITY); System.out.println(Thread.NORM_PRIORITY); System.out.println("================="); myThread1.setPriority(1); myThread2.setPriority(3); myThread3.setPriority(10); myThread1.start(); myThread2.start(); myThread3.start(); } }
Thread.MAX_PRIORITY是最高的线程为10
Thread.MIN_PRIORITY是最低的线程为1
Thread.NORM_PRIORITY是默认线程为5
线程的优先级表示的是线程抢占的概率 线程优先级越大 概率越大
线程控制
-
static void sleep(long millis)使当前正在执行的线程停留(暂停执行)指定的毫秒数
-
void join()等待这个线程死亡 只有这个线程死亡之后其他线程才能执行
-
void setDaemon(boolean on)将此线程标记为守护线程,当运行线程都为守护线程的时候Java虚拟机将退出
package day4; public class MyThread extends Thread{ @Override public void run() { for (int i = 0; i < 100; i++) { System.out.println(getName()+": "+i); try { sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } } }
package day4; public class ThreadTest{ public static void main(String[] args) { MyThread myThread1 = new MyThread(); MyThread myThread2 = new MyThread(); MyThread myThread3 = new MyThread(); myThread1.setName("小明"); myThread2.setName("小红"); myThread3.setName("小刚"); myThread1.start(); myThread2.start(); myThread3.start(); } }
package day4; public class MyThread extends Thread{ @Override public void run() { for (int i = 0; i < 100; i++) { System.out.println(getName()+": "+i); } } }
package day4; public class ThreadTest{ public static void main(String[] args) throws InterruptedException { MyThread myThread1 = new MyThread(); MyThread myThread2 = new MyThread(); MyThread myThread3 = new MyThread(); myThread1.setName("小明"); myThread2.setName("小红"); myThread3.setName("小刚"); myThread1.start(); myThread1.join(); myThread2.start(); myThread3.start(); } }
只有在第一个执行完毕后 后两个才开始执行
package day4; public class MyThread extends Thread{ @Override public void run() { for (int i = 0; i < 100; i++) { System.out.println(getName()+": "+i); } } }
package day4; public class ThreadTest{ public static void main(String[] args) throws InterruptedException { MyThread myThread1 = new MyThread(); MyThread myThread2 = new MyThread(); MyThread myThread3 = new MyThread(); myThread1.setName("小明"); myThread2.setName("小红"); //将myThread1、myThread2设置为守护线程 当主线程结束后守护线程就结束 myThread1.setDaemon(true); myThread2.setDaemon(true); myThread1.start(); myThread2.start(); Thread.currentThread().setName("主线程"); for (int i = 0; i < 10; i++) { System.out.println(Thread.currentThread().getName()+" "+i); } } }
当主线程结束以后 守护线程不会立即结束 还会挣扎一会
线程的生命周期
阻塞是不能立马进入运行状态的 只有就绪状态可以立马进入运行状态*
多线程的实现之实现Runnable接口的方式
方式2:实现Runnable接口
定义一个类实现Runnable接口 创建Thread类 把实现Runnable接口的类对象作为构造方法的参数
package day5; public class MyRunnable implements Runnable{ @Override public void run() { for (int i = 0; i < 100; i++) { System.out.println(Thread.currentThread().getName()+" "+i); } } }
package day5; public class RunnableApplication { public static void main(String[] args) { MyRunnable mr = new MyRunnable(); Thread thread1 = new Thread(mr,"高铁"); Thread thread2 = new Thread(mr,"飞机"); thread1.start(); thread2.start(); } }
注意: 不能直接在 MyRunnable类中使用getName方法获取线程的名称 要通过Thread.currentThread().getName()方法获取
实现Runnable类相对于Thread的好处
-
避免Java单继承的局限性
-
适合多个相同程序代码去处理同一个资源的情况,把线程和程序的代码、数据有效分离
线程案例之卖票
案例:共有100张票 ,三个窗口要卖它
package day6; public class ticketRunnable implements Runnable{ int ticket=100; @Override public void run() { while (true){ if (ticket>0){ System.out.println(Thread.currentThread().getName()+"刚刚卖的是第几张票:"+ticket); ticket--; } } } }
package day6; public class ticketApplication { public static void main(String[] args) { ticketRunnable tr = new ticketRunnable(); Thread thread1 = new Thread(tr,"窗口1"); Thread thread2 = new Thread(tr,"窗口2"); Thread thread3 = new Thread(tr,"窗口3"); thread1.start(); thread2.start(); thread3.start(); } }
我们发现这个和我们想的不一样
因为刚刚那段代码存在同步代码块的安全问题
同步安全问题的的标准
-
多线程
-
数据共享
-
多条语句操作共享数据
怎样解决呢?
我们可以在同步代码块加一个锁
package day6; public class ticketRunnable implements Runnable{ private int ticket=100; private Object o=new Object(); @Override public void run() { while (true){ synchronized (o) { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } if (ticket>0){ System.out.println(Thread.currentThread().getName()+"刚刚卖的是第几张票:"+ticket); ticket--; } } } } }
package day6; public class ticketApplication { public static void main(String[] args) { ticketRunnable tr = new ticketRunnable(); Thread thread1 = new Thread(tr,"窗口1"); Thread thread2 = new Thread(tr,"窗口2"); Thread thread3 = new Thread(tr,"窗口3"); thread1.start(); thread2.start(); thread3.start(); } }
此时不存在刚刚的问题
注意: 同步代码块的锁必须是一样的不可以在synchronized里面new一个要在整个类里面定义
注意: 同步代码块的锁必须是一样的不可以在synchronized里面new一个要在整个类里面定义
注意: 同步代码块的锁必须是一样的不可以在synchronized里面new一个要在整个类里面定义
synchronized的好处:解决了线程安全问题
坏处:当线程很多时 每一个线程去判断同步上的锁,很消耗资源,降低效率